init
This commit is contained in:
349
lib/database.ts
Normal file
349
lib/database.ts
Normal file
@@ -0,0 +1,349 @@
|
||||
import mysql from 'mysql2/promise'
|
||||
|
||||
// MySQL 連接設定
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST!,
|
||||
port: parseInt(process.env.DB_PORT!),
|
||||
database: process.env.DB_DATABASE!,
|
||||
user: process.env.DB_USERNAME!,
|
||||
password: process.env.DB_PASSWORD!,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0
|
||||
}
|
||||
|
||||
// 創建連接池
|
||||
export const db = mysql.createPool(dbConfig)
|
||||
|
||||
// 測試連接函數
|
||||
export async function testConnection() {
|
||||
try {
|
||||
const connection = await db.getConnection()
|
||||
console.log('MySQL 資料庫連接成功')
|
||||
connection.release()
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('MySQL 資料庫連接失敗:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Database types
|
||||
export interface User {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
role: "executive" | "manager" | "hr"
|
||||
avatar_url?: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface KPI {
|
||||
id: string
|
||||
user_id: string
|
||||
title: string
|
||||
description?: string
|
||||
category: "financial" | "operational" | "team" | "innovation" | "strategy"
|
||||
weight: number
|
||||
target_value: number
|
||||
current_value: number
|
||||
unit?: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
approved_by?: string
|
||||
status: "pending" | "approved" | "rejected" | "active" | "completed"
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface KPIProgress {
|
||||
id: string
|
||||
kpi_id: string
|
||||
progress: number
|
||||
comment?: string
|
||||
attachment_url?: string
|
||||
blockers?: string
|
||||
created_by: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface Review {
|
||||
id: string
|
||||
reviewer_id: string
|
||||
reviewee_id: string
|
||||
review_type: "quarterly" | "annual" | "one_on_one" | "goal_setting"
|
||||
scheduled_date: string
|
||||
completed_date?: string
|
||||
status: "scheduled" | "in_progress" | "completed" | "cancelled"
|
||||
summary?: string
|
||||
overall_rating?: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
// CRUD API functions
|
||||
export const kpiService = {
|
||||
// READ - 獲取用戶的 KPI
|
||||
async getKPIsByUser(userId: string): Promise<KPI[]> {
|
||||
try {
|
||||
const [rows] = await db.execute(
|
||||
'SELECT * FROM kpi WHERE user_id = ? AND status = ?',
|
||||
[userId, 'active']
|
||||
)
|
||||
return rows as KPI[]
|
||||
} catch (error) {
|
||||
console.error('獲取 KPI 失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// CREATE - 創建新 KPI
|
||||
async createKPI(kpi: Omit<KPI, "id" | "created_at" | "updated_at">): Promise<KPI> {
|
||||
try {
|
||||
const id = `kpi_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
const now = new Date().toISOString()
|
||||
|
||||
await db.execute(
|
||||
`INSERT INTO kpi (id, user_id, title, description, category, weight, target_value,
|
||||
current_value, unit, start_date, end_date, approved_by, status, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
id, kpi.user_id, kpi.title, kpi.description, kpi.category, kpi.weight,
|
||||
kpi.target_value, kpi.current_value, kpi.unit, kpi.start_date, kpi.end_date,
|
||||
kpi.approved_by, kpi.status, now, now
|
||||
]
|
||||
)
|
||||
|
||||
const [rows] = await db.execute('SELECT * FROM kpi WHERE id = ?', [id])
|
||||
return (rows as KPI[])[0]
|
||||
} catch (error) {
|
||||
console.error('創建 KPI 失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// UPDATE - 更新 KPI 進度
|
||||
async updateKPIProgress(kpiId: string, progress: number, comment?: string, blockers?: string, userId?: string): Promise<void> {
|
||||
try {
|
||||
const id = `progress_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
const now = new Date().toISOString()
|
||||
|
||||
await db.execute(
|
||||
`INSERT INTO kpi_progress (id, kpi_id, progress, comment, blockers, created_by, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
[id, kpiId, progress, comment, blockers, userId || 'system', now]
|
||||
)
|
||||
|
||||
// 同時更新 KPI 的當前值
|
||||
await db.execute(
|
||||
'UPDATE kpi SET current_value = ?, updated_at = ? WHERE id = ?',
|
||||
[progress, now, kpiId]
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('更新 KPI 進度失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// UPDATE - 更新 KPI
|
||||
async updateKPI(id: string, updates: Partial<KPI>): Promise<KPI> {
|
||||
try {
|
||||
const now = new Date().toISOString()
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id').map(key => `${key} = ?`)
|
||||
const values = Object.values(updates).filter((_, index) => Object.keys(updates)[index] !== 'id')
|
||||
|
||||
if (fields.length === 0) throw new Error('沒有要更新的欄位')
|
||||
|
||||
await db.execute(
|
||||
`UPDATE kpi SET ${fields.join(', ')}, updated_at = ? WHERE id = ?`,
|
||||
[...values, now, id]
|
||||
)
|
||||
|
||||
const [rows] = await db.execute('SELECT * FROM kpi WHERE id = ?', [id])
|
||||
return (rows as KPI[])[0]
|
||||
} catch (error) {
|
||||
console.error('更新 KPI 失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// DELETE - 刪除 KPI(軟刪除,將狀態改為 deleted)
|
||||
async deleteKPI(id: string): Promise<void> {
|
||||
try {
|
||||
const now = new Date().toISOString()
|
||||
await db.execute(
|
||||
'UPDATE kpi SET status = ?, updated_at = ? WHERE id = ?',
|
||||
['deleted', now, id]
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('刪除 KPI 失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const reviewService = {
|
||||
// READ - 獲取即將到來的評審
|
||||
async getUpcomingReviews(userId: string): Promise<Review[]> {
|
||||
try {
|
||||
const [rows] = await db.execute(
|
||||
`SELECT r.*, u.name as reviewee_name, u.email as reviewee_email
|
||||
FROM reviews r
|
||||
LEFT JOIN users u ON r.reviewee_id = u.id
|
||||
WHERE r.reviewer_id = ? AND r.status = ?
|
||||
ORDER BY r.scheduled_date ASC`,
|
||||
[userId, 'scheduled']
|
||||
)
|
||||
return rows as Review[]
|
||||
} catch (error) {
|
||||
console.error('獲取評審列表失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// CREATE - 創建新評審
|
||||
async createReview(review: Omit<Review, "id" | "created_at" | "updated_at">): Promise<Review> {
|
||||
try {
|
||||
const id = `review_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
const now = new Date().toISOString()
|
||||
|
||||
await db.execute(
|
||||
`INSERT INTO reviews (id, reviewer_id, reviewee_id, review_type, scheduled_date,
|
||||
completed_date, status, summary, overall_rating, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
id, review.reviewer_id, review.reviewee_id, review.review_type, review.scheduled_date,
|
||||
review.completed_date, review.status, review.summary, review.overall_rating, now, now
|
||||
]
|
||||
)
|
||||
|
||||
const [rows] = await db.execute('SELECT * FROM reviews WHERE id = ?', [id])
|
||||
return (rows as Review[])[0]
|
||||
} catch (error) {
|
||||
console.error('創建評審失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// READ - 獲取所有評審
|
||||
async getAllReviews(): Promise<Review[]> {
|
||||
try {
|
||||
const [rows] = await db.execute('SELECT * FROM reviews ORDER BY created_at DESC')
|
||||
return rows as Review[]
|
||||
} catch (error) {
|
||||
console.error('獲取所有評審失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// UPDATE - 更新評審
|
||||
async updateReview(id: string, updates: Partial<Review>): Promise<Review> {
|
||||
try {
|
||||
const now = new Date().toISOString()
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id').map(key => `${key} = ?`)
|
||||
const values = Object.values(updates).filter((_, index) => Object.keys(updates)[index] !== 'id')
|
||||
|
||||
if (fields.length === 0) throw new Error('沒有要更新的欄位')
|
||||
|
||||
await db.execute(
|
||||
`UPDATE reviews SET ${fields.join(', ')}, updated_at = ? WHERE id = ?`,
|
||||
[...values, now, id]
|
||||
)
|
||||
|
||||
const [rows] = await db.execute('SELECT * FROM reviews WHERE id = ?', [id])
|
||||
return (rows as Review[])[0]
|
||||
} catch (error) {
|
||||
console.error('更新評審失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// DELETE - 刪除評審
|
||||
async deleteReview(id: string): Promise<void> {
|
||||
try {
|
||||
await db.execute('DELETE FROM reviews WHERE id = ?', [id])
|
||||
} catch (error) {
|
||||
console.error('刪除評審失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// 用戶管理服務
|
||||
export const userService = {
|
||||
// CREATE - 創建用戶
|
||||
async createUser(user: Omit<User, "id" | "created_at" | "updated_at">): Promise<User> {
|
||||
try {
|
||||
const id = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
const now = new Date().toISOString()
|
||||
|
||||
await db.execute(
|
||||
`INSERT INTO users (id, name, email, role, avatar_url, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
[id, user.name, user.email, user.role, user.avatar_url, now, now]
|
||||
)
|
||||
|
||||
const [rows] = await db.execute('SELECT * FROM users WHERE id = ?', [id])
|
||||
return (rows as User[])[0]
|
||||
} catch (error) {
|
||||
console.error('創建用戶失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// READ - 獲取所有用戶
|
||||
async getAllUsers(): Promise<User[]> {
|
||||
try {
|
||||
const [rows] = await db.execute('SELECT * FROM users ORDER BY created_at DESC')
|
||||
return rows as User[]
|
||||
} catch (error) {
|
||||
console.error('獲取用戶列表失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// READ - 根據 ID 獲取用戶
|
||||
async getUserById(id: string): Promise<User | null> {
|
||||
try {
|
||||
const [rows] = await db.execute('SELECT * FROM users WHERE id = ?', [id])
|
||||
const users = rows as User[]
|
||||
return users.length > 0 ? users[0] : null
|
||||
} catch (error) {
|
||||
console.error('獲取用戶失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// UPDATE - 更新用戶
|
||||
async updateUser(id: string, updates: Partial<User>): Promise<User> {
|
||||
try {
|
||||
const now = new Date().toISOString()
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id').map(key => `${key} = ?`)
|
||||
const values = Object.values(updates).filter((_, index) => Object.keys(updates)[index] !== 'id')
|
||||
|
||||
if (fields.length === 0) throw new Error('沒有要更新的欄位')
|
||||
|
||||
await db.execute(
|
||||
`UPDATE users SET ${fields.join(', ')}, updated_at = ? WHERE id = ?`,
|
||||
[...values, now, id]
|
||||
)
|
||||
|
||||
const [rows] = await db.execute('SELECT * FROM users WHERE id = ?', [id])
|
||||
return (rows as User[])[0]
|
||||
} catch (error) {
|
||||
console.error('更新用戶失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// DELETE - 刪除用戶
|
||||
async deleteUser(id: string): Promise<void> {
|
||||
try {
|
||||
await db.execute('DELETE FROM users WHERE id = ?', [id])
|
||||
} catch (error) {
|
||||
console.error('刪除用戶失敗:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
}
|
6
lib/utils.ts
Normal file
6
lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
Reference in New Issue
Block a user