Files
ai-scoring-application/lib/services/database.ts

434 lines
14 KiB
TypeScript

import { query, transaction } from '../database';
import type {
User,
CriteriaTemplate,
CriteriaItem,
Project,
ProjectFile,
ProjectWebsite,
Evaluation,
EvaluationScore,
EvaluationFeedback,
ProjectWithDetails,
EvaluationWithDetails,
CriteriaTemplateWithItems,
} from '../models';
// 用戶相關操作
export class UserService {
static async create(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>) {
const sql = `
INSERT INTO users (email, username, password_hash, full_name, avatar_url, role, is_active, email_verified_at, last_login_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const result = await query(sql, [
userData.email,
userData.username,
userData.password_hash,
userData.full_name,
userData.avatar_url,
userData.role,
userData.is_active,
userData.email_verified_at,
userData.last_login_at,
]);
return result;
}
static async findByEmail(email: string): Promise<User | null> {
const sql = 'SELECT * FROM users WHERE email = ?';
const rows = await query(sql, [email]) as User[];
return rows.length > 0 ? rows[0] : null;
}
static async findById(id: number): Promise<User | null> {
const sql = 'SELECT * FROM users WHERE id = ?';
const rows = await query(sql, [id]) as User[];
return rows.length > 0 ? rows[0] : null;
}
static async updateLastLogin(id: number) {
const sql = 'UPDATE users SET last_login_at = NOW() WHERE id = ?';
await query(sql, [id]);
}
}
// 評分標準模板相關操作
export class CriteriaTemplateService {
static async create(templateData: Omit<CriteriaTemplate, 'id' | 'created_at' | 'updated_at'>) {
const sql = `
INSERT INTO criteria_templates (user_id, name, description, is_default, is_public, total_weight)
VALUES (?, ?, ?, ?, ?, ?)
`;
const result = await query(sql, [
templateData.user_id,
templateData.name,
templateData.description,
templateData.is_default,
templateData.is_public,
templateData.total_weight,
]);
return result;
}
static async findById(id: number): Promise<CriteriaTemplate | null> {
const sql = 'SELECT * FROM criteria_templates WHERE id = ?';
const rows = await query(sql, [id]) as CriteriaTemplate[];
return rows.length > 0 ? rows[0] : null;
}
static async findByUserId(userId: number): Promise<CriteriaTemplate[]> {
const sql = 'SELECT * FROM criteria_templates WHERE user_id = ? ORDER BY created_at DESC';
return await query(sql, [userId]) as CriteriaTemplate[];
}
static async findDefault(): Promise<CriteriaTemplate | null> {
const sql = 'SELECT * FROM criteria_templates WHERE is_default = 1 LIMIT 1';
const rows = await query(sql) as CriteriaTemplate[];
return rows.length > 0 ? rows[0] : null;
}
static async findWithItems(id: number): Promise<CriteriaTemplateWithItems | null> {
const template = await this.findById(id);
if (!template) return null;
const itemsSql = 'SELECT * FROM criteria_items WHERE template_id = ? ORDER BY sort_order';
const items = await query(itemsSql, [id]) as CriteriaItem[];
return { ...template, items };
}
static async update(id: number, templateData: Partial<CriteriaTemplate>) {
const fields = Object.keys(templateData).map(key => `${key} = ?`).join(', ');
const values = Object.values(templateData);
const sql = `UPDATE criteria_templates SET ${fields}, updated_at = NOW() WHERE id = ?`;
await query(sql, [...values, id]);
}
static async delete(id: number) {
const sql = 'DELETE FROM criteria_templates WHERE id = ?';
await query(sql, [id]);
}
}
// 評分項目相關操作
export class CriteriaItemService {
static async create(itemData: Omit<CriteriaItem, 'id' | 'created_at' | 'updated_at'>) {
const sql = `
INSERT INTO criteria_items (template_id, name, description, weight, max_score, sort_order)
VALUES (?, ?, ?, ?, ?, ?)
`;
const result = await query(sql, [
itemData.template_id,
itemData.name,
itemData.description,
itemData.weight,
itemData.max_score,
itemData.sort_order,
]);
return result;
}
static async findByTemplateId(templateId: number): Promise<CriteriaItem[]> {
const sql = 'SELECT * FROM criteria_items WHERE template_id = ? ORDER BY sort_order';
const rows = await query(sql, [templateId]) as any[];
// 映射資料庫欄位到前端期望的格式
return rows.map(row => ({
id: row.id,
template_id: row.template_id,
name: row.name,
description: row.description,
weight: Number(row.weight) || 0,
maxScore: Number(row.max_score) || 10, // 映射 max_score 到 maxScore 並轉換為數字
sort_order: row.sort_order,
created_at: row.created_at,
updated_at: row.updated_at
}));
}
static async update(id: number, itemData: Partial<CriteriaItem>) {
const fields = Object.keys(itemData).map(key => `${key} = ?`).join(', ');
const values = Object.values(itemData);
const sql = `UPDATE criteria_items SET ${fields}, updated_at = NOW() WHERE id = ?`;
await query(sql, [...values, id]);
}
static async delete(id: number) {
const sql = 'DELETE FROM criteria_items WHERE id = ?';
await query(sql, [id]);
}
static async deleteByTemplateId(templateId: number) {
const sql = 'DELETE FROM criteria_items WHERE template_id = ?';
await query(sql, [templateId]);
}
}
// 專案相關操作
export class ProjectService {
static async create(projectData: Omit<Project, 'id' | 'created_at' | 'updated_at'>) {
const sql = `
INSERT INTO projects (user_id, template_id, title, description, status, analysis_started_at, analysis_completed_at)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const result = await query(sql, [
projectData.user_id,
projectData.template_id,
projectData.title,
projectData.description,
projectData.status,
projectData.analysis_started_at,
projectData.analysis_completed_at,
]);
return result;
}
static async findById(id: number): Promise<Project | null> {
const sql = 'SELECT * FROM projects WHERE id = ?';
const rows = await query(sql, [id]) as Project[];
return rows.length > 0 ? rows[0] : null;
}
static async findByUserId(userId: number, limit = 20, offset = 0): Promise<Project[]> {
const sql = `
SELECT * FROM projects
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`;
return await query(sql, [userId, limit, offset]) as Project[];
}
static async findWithDetails(id: number): Promise<ProjectWithDetails | null> {
const project = await this.findById(id);
if (!project) return null;
const template = await CriteriaTemplateService.findById(project.template_id);
if (!template) return null;
const files = await ProjectFileService.findByProjectId(id);
const websites = await ProjectWebsiteService.findByProjectId(id);
const evaluation = await EvaluationService.findByProjectId(id);
return {
...project,
template,
files,
websites,
evaluation,
};
}
static async update(id: number, projectData: Partial<Project>) {
const fields = Object.keys(projectData).map(key => `${key} = ?`).join(', ');
const values = Object.values(projectData);
const sql = `UPDATE projects SET ${fields}, updated_at = NOW() WHERE id = ?`;
await query(sql, [...values, id]);
}
static async delete(id: number) {
const sql = 'DELETE FROM projects WHERE id = ?';
await query(sql, [id]);
}
}
// 專案文件相關操作
export class ProjectFileService {
static async create(fileData: Omit<ProjectFile, 'id' | 'created_at' | 'updated_at'>) {
const sql = `
INSERT INTO project_files (project_id, original_name, file_name, file_path, file_size, file_type, mime_type, upload_status, upload_progress)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const result = await query(sql, [
fileData.project_id,
fileData.original_name,
fileData.file_name,
fileData.file_path,
fileData.file_size,
fileData.file_type,
fileData.mime_type,
fileData.upload_status,
fileData.upload_progress,
]);
return result;
}
static async findByProjectId(projectId: number): Promise<ProjectFile[]> {
const sql = 'SELECT * FROM project_files WHERE project_id = ? ORDER BY created_at';
return await query(sql, [projectId]) as ProjectFile[];
}
static async updateStatus(id: number, status: ProjectFile['upload_status'], progress?: number) {
const sql = 'UPDATE project_files SET upload_status = ?, upload_progress = ?, updated_at = NOW() WHERE id = ?';
await query(sql, [status, progress || 0, id]);
}
static async delete(id: number) {
const sql = 'DELETE FROM project_files WHERE id = ?';
await query(sql, [id]);
}
}
// 專案網站相關操作
export class ProjectWebsiteService {
static async create(websiteData: Omit<ProjectWebsite, 'id' | 'created_at' | 'updated_at'>) {
const sql = `
INSERT INTO project_websites (project_id, url, title, description, status)
VALUES (?, ?, ?, ?, ?)
`;
const result = await query(sql, [
websiteData.project_id,
websiteData.url,
websiteData.title,
websiteData.description,
websiteData.status,
]);
return result;
}
static async findByProjectId(projectId: number): Promise<ProjectWebsite[]> {
const sql = 'SELECT * FROM project_websites WHERE project_id = ? ORDER BY created_at';
return await query(sql, [projectId]) as ProjectWebsite[];
}
static async updateStatus(id: number, status: ProjectWebsite['status']) {
const sql = 'UPDATE project_websites SET status = ?, updated_at = NOW() WHERE id = ?';
await query(sql, [status, id]);
}
static async delete(id: number) {
const sql = 'DELETE FROM project_websites WHERE id = ?';
await query(sql, [id]);
}
}
// 評審相關操作
export class EvaluationService {
static async create(evaluationData: Omit<Evaluation, 'id' | 'created_at' | 'updated_at'>) {
const sql = `
INSERT INTO evaluations (project_id, overall_score, max_possible_score, grade, analysis_duration, ai_model_version, status, error_message)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`;
const result = await query(sql, [
evaluationData.project_id,
evaluationData.overall_score,
evaluationData.max_possible_score,
evaluationData.grade,
evaluationData.analysis_duration,
evaluationData.ai_model_version,
evaluationData.status,
evaluationData.error_message,
]);
return result;
}
static async findById(id: number): Promise<Evaluation | null> {
const sql = 'SELECT * FROM evaluations WHERE id = ?';
const rows = await query(sql, [id]) as Evaluation[];
return rows.length > 0 ? rows[0] : null;
}
static async findByProjectId(projectId: number): Promise<Evaluation | null> {
const sql = 'SELECT * FROM evaluations WHERE project_id = ? ORDER BY created_at DESC LIMIT 1';
const rows = await query(sql, [projectId]) as Evaluation[];
return rows.length > 0 ? rows[0] : null;
}
static async findWithDetails(id: number): Promise<EvaluationWithDetails | null> {
const evaluation = await this.findById(id);
if (!evaluation) return null;
const project = await ProjectService.findById(evaluation.project_id);
if (!project) return null;
const scoresSql = `
SELECT es.*, ci.name as criteria_item_name, ci.description as criteria_item_description
FROM evaluation_scores es
JOIN criteria_items ci ON es.criteria_item_id = ci.id
WHERE es.evaluation_id = ?
ORDER BY ci.sort_order
`;
const scores = await query(scoresSql, [id]) as (EvaluationScore & { criteria_item: CriteriaItem })[];
const feedback = await EvaluationFeedbackService.findByEvaluationId(id);
return {
...evaluation,
project,
scores,
feedback,
};
}
static async update(id: number, evaluationData: Partial<Evaluation>) {
const fields = Object.keys(evaluationData).map(key => `${key} = ?`).join(', ');
const values = Object.values(evaluationData);
const sql = `UPDATE evaluations SET ${fields}, updated_at = NOW() WHERE id = ?`;
await query(sql, [...values, id]);
}
static async delete(id: number) {
const sql = 'DELETE FROM evaluations WHERE id = ?';
await query(sql, [id]);
}
}
// 評分結果相關操作
export class EvaluationScoreService {
static async create(scoreData: Omit<EvaluationScore, 'id' | 'created_at'>) {
const sql = `
INSERT INTO evaluation_scores (evaluation_id, criteria_item_id, score, max_score, weight, weighted_score, percentage)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const result = await query(sql, [
scoreData.evaluation_id,
scoreData.criteria_item_id,
scoreData.score,
scoreData.max_score,
scoreData.weight,
scoreData.weighted_score,
scoreData.percentage,
]);
return result;
}
static async findByEvaluationId(evaluationId: number): Promise<EvaluationScore[]> {
const sql = 'SELECT * FROM evaluation_scores WHERE evaluation_id = ?';
return await query(sql, [evaluationId]) as EvaluationScore[];
}
static async deleteByEvaluationId(evaluationId: number) {
const sql = 'DELETE FROM evaluation_scores WHERE evaluation_id = ?';
await query(sql, [evaluationId]);
}
}
// 評語相關操作
export class EvaluationFeedbackService {
static async create(feedbackData: Omit<EvaluationFeedback, 'id' | 'created_at'>) {
const sql = `
INSERT INTO evaluation_feedback (evaluation_id, criteria_item_id, feedback_type, content, sort_order)
VALUES (?, ?, ?, ?, ?)
`;
const result = await query(sql, [
feedbackData.evaluation_id,
feedbackData.criteria_item_id,
feedbackData.feedback_type,
feedbackData.content,
feedbackData.sort_order,
]);
return result;
}
static async findByEvaluationId(evaluationId: number): Promise<EvaluationFeedback[]> {
const sql = 'SELECT * FROM evaluation_feedback WHERE evaluation_id = ? ORDER BY sort_order';
return await query(sql, [evaluationId]) as EvaluationFeedback[];
}
static async deleteByEvaluationId(evaluationId: number) {
const sql = 'DELETE FROM evaluation_feedback WHERE evaluation_id = ?';
await query(sql, [evaluationId]);
}
}