整合資料庫、完成登入註冊忘記密碼功能
This commit is contained in:
621
lib/services/database-service.ts
Normal file
621
lib/services/database-service.ts
Normal file
@@ -0,0 +1,621 @@
|
||||
// =====================================================
|
||||
// 資料庫服務層
|
||||
// =====================================================
|
||||
|
||||
import { db } from '../database';
|
||||
import type {
|
||||
User,
|
||||
Judge,
|
||||
Team,
|
||||
TeamMember,
|
||||
Competition,
|
||||
CompetitionRule,
|
||||
CompetitionAwardType,
|
||||
App,
|
||||
Proposal,
|
||||
AppJudgeScore,
|
||||
ProposalJudgeScore,
|
||||
Award,
|
||||
UserFavorite,
|
||||
UserLike,
|
||||
UserView,
|
||||
UserRating,
|
||||
ChatSession,
|
||||
ChatMessage,
|
||||
AIAssistantConfig,
|
||||
SystemSetting,
|
||||
ActivityLog,
|
||||
UserStatistics,
|
||||
AppStatistics,
|
||||
CompetitionStatistics
|
||||
} from '../models';
|
||||
|
||||
// =====================================================
|
||||
// 用戶服務
|
||||
// =====================================================
|
||||
export class UserService {
|
||||
// 創建用戶
|
||||
async create(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<User> {
|
||||
const sql = `
|
||||
INSERT INTO users (id, name, email, password_hash, avatar, department, role, join_date, total_likes, total_views, is_active, last_login)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
userData.id,
|
||||
userData.name,
|
||||
userData.email,
|
||||
userData.password_hash,
|
||||
userData.avatar || null,
|
||||
userData.department,
|
||||
userData.role,
|
||||
userData.join_date,
|
||||
userData.total_likes,
|
||||
userData.total_views,
|
||||
userData.is_active,
|
||||
userData.last_login || null
|
||||
];
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.findByEmail(userData.email) as User;
|
||||
}
|
||||
|
||||
// 根據郵箱獲取用戶
|
||||
async findByEmail(email: string): Promise<User | null> {
|
||||
const sql = 'SELECT * FROM users WHERE email = ? AND is_active = TRUE';
|
||||
return await db.queryOne<User>(sql, [email]);
|
||||
}
|
||||
|
||||
// 根據ID獲取用戶
|
||||
async findById(id: string): Promise<User | null> {
|
||||
const sql = 'SELECT * FROM users WHERE id = ? AND is_active = TRUE';
|
||||
return await db.queryOne<User>(sql, [id]);
|
||||
}
|
||||
|
||||
// 更新用戶
|
||||
async update(id: string, updates: Partial<User>): Promise<User | null> {
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
|
||||
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
||||
const values = fields.map(field => (updates as any)[field]);
|
||||
|
||||
const sql = `UPDATE users SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
|
||||
const result = await db.update(sql, [...values, id]);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return await this.findById(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新最後登入時間
|
||||
async updateLastLogin(id: string): Promise<boolean> {
|
||||
const sql = 'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?';
|
||||
const result = await db.update(sql, [id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取用戶統計
|
||||
async getUserStatistics(id: string): Promise<UserStatistics | null> {
|
||||
const sql = 'SELECT * FROM user_statistics WHERE id = ?';
|
||||
return await db.queryOne<UserStatistics>(sql, [id]);
|
||||
}
|
||||
|
||||
// 獲取所有用戶(管理員用)
|
||||
async findAll(filters: {
|
||||
search?: string;
|
||||
department?: string;
|
||||
role?: string;
|
||||
status?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
} = {}): Promise<{ users: User[]; total: number }> {
|
||||
const { search, department, role, status, page = 1, limit = 10 } = filters;
|
||||
|
||||
// 構建查詢條件
|
||||
let whereConditions = ['is_active = TRUE'];
|
||||
let params: any[] = [];
|
||||
|
||||
if (search) {
|
||||
whereConditions.push('(name LIKE ? OR email LIKE ?)');
|
||||
params.push(`%${search}%`, `%${search}%`);
|
||||
}
|
||||
|
||||
if (department && department !== 'all') {
|
||||
whereConditions.push('department = ?');
|
||||
params.push(department);
|
||||
}
|
||||
|
||||
if (role && role !== 'all') {
|
||||
whereConditions.push('role = ?');
|
||||
params.push(role);
|
||||
}
|
||||
|
||||
if (status && status !== 'all') {
|
||||
if (status === 'active') {
|
||||
whereConditions.push('last_login IS NOT NULL AND last_login >= DATE_SUB(NOW(), INTERVAL 30 DAY)');
|
||||
} else if (status === 'inactive') {
|
||||
whereConditions.push('last_login IS NULL OR last_login < DATE_SUB(NOW(), INTERVAL 30 DAY)');
|
||||
}
|
||||
}
|
||||
|
||||
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
|
||||
|
||||
// 獲取總數
|
||||
const countSql = `SELECT COUNT(*) as total FROM users ${whereClause}`;
|
||||
const countResult = await this.query(countSql, params);
|
||||
const total = countResult[0]?.total || 0;
|
||||
|
||||
// 獲取用戶列表
|
||||
const offset = (page - 1) * limit;
|
||||
const usersSql = `
|
||||
SELECT
|
||||
id, name, email, avatar, department, role, join_date,
|
||||
total_likes, total_views, is_active, last_login, created_at, updated_at
|
||||
FROM users
|
||||
${whereClause}
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ${offset}, ${limit}
|
||||
`;
|
||||
const users = await this.query<User>(usersSql, params);
|
||||
|
||||
return { users, total };
|
||||
}
|
||||
|
||||
// 獲取用戶統計數據
|
||||
async getUserStats(): Promise<{
|
||||
totalUsers: number;
|
||||
activeUsers: number;
|
||||
adminCount: number;
|
||||
developerCount: number;
|
||||
inactiveUsers: number;
|
||||
newThisMonth: number;
|
||||
}> {
|
||||
const sql = `
|
||||
SELECT
|
||||
COUNT(*) as total_users,
|
||||
COUNT(CASE WHEN last_login IS NOT NULL AND last_login >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as active_users,
|
||||
COUNT(CASE WHEN role = 'admin' THEN 1 END) as admin_count,
|
||||
COUNT(CASE WHEN role = 'developer' THEN 1 END) as developer_count,
|
||||
COUNT(CASE WHEN last_login IS NULL OR last_login < DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as inactive_users,
|
||||
COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as new_this_month
|
||||
FROM users
|
||||
WHERE is_active = TRUE
|
||||
`;
|
||||
const result = await this.query(sql);
|
||||
const stats = result[0] || {};
|
||||
|
||||
return {
|
||||
totalUsers: stats.total_users || 0,
|
||||
activeUsers: stats.active_users || 0,
|
||||
adminCount: stats.admin_count || 0,
|
||||
developerCount: stats.developer_count || 0,
|
||||
inactiveUsers: stats.inactive_users || 0,
|
||||
newThisMonth: stats.new_this_month || 0
|
||||
};
|
||||
}
|
||||
|
||||
// 通用查詢方法
|
||||
async query<T = any>(sql: string, params: any[] = []): Promise<T[]> {
|
||||
return await db.query<T>(sql, params);
|
||||
}
|
||||
|
||||
// 通用單一查詢方法
|
||||
async queryOne<T = any>(sql: string, params: any[] = []): Promise<T | null> {
|
||||
return await db.queryOne<T>(sql, params);
|
||||
}
|
||||
|
||||
// 獲取所有用戶
|
||||
async getAllUsers(limit = 50, offset = 0): Promise<User[]> {
|
||||
const sql = 'SELECT * FROM users WHERE is_active = TRUE ORDER BY created_at DESC LIMIT ? OFFSET ?';
|
||||
return await db.query<User>(sql, [limit, offset]);
|
||||
}
|
||||
|
||||
// 靜態方法保持向後兼容
|
||||
static async createUser(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<User> {
|
||||
const service = new UserService();
|
||||
return await service.create(userData);
|
||||
}
|
||||
|
||||
static async getUserByEmail(email: string): Promise<User | null> {
|
||||
const service = new UserService();
|
||||
return await service.findByEmail(email);
|
||||
}
|
||||
|
||||
static async getUserById(id: string): Promise<User | null> {
|
||||
const service = new UserService();
|
||||
return await service.findById(id);
|
||||
}
|
||||
|
||||
static async updateUser(id: string, updates: Partial<User>): Promise<boolean> {
|
||||
const service = new UserService();
|
||||
const result = await service.update(id, updates);
|
||||
return result !== null;
|
||||
}
|
||||
|
||||
static async getUserStatistics(id: string): Promise<UserStatistics | null> {
|
||||
const service = new UserService();
|
||||
return await service.getUserStatistics(id);
|
||||
}
|
||||
|
||||
static async getAllUsers(limit = 50, offset = 0): Promise<User[]> {
|
||||
const service = new UserService();
|
||||
return await service.getAllUsers(limit, offset);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 評審服務
|
||||
// =====================================================
|
||||
export class JudgeService {
|
||||
// 創建評審
|
||||
static async createJudge(judgeData: Omit<Judge, 'id' | 'created_at' | 'updated_at'>): Promise<Judge> {
|
||||
const sql = `
|
||||
INSERT INTO judges (id, name, title, department, expertise, avatar, is_active)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
judgeData.name,
|
||||
judgeData.title,
|
||||
judgeData.department,
|
||||
JSON.stringify(judgeData.expertise),
|
||||
judgeData.avatar || null,
|
||||
judgeData.is_active
|
||||
];
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.getJudgeByName(judgeData.name) as Judge;
|
||||
}
|
||||
|
||||
// 根據姓名獲取評審
|
||||
static async getJudgeByName(name: string): Promise<Judge | null> {
|
||||
const sql = 'SELECT * FROM judges WHERE name = ? AND is_active = TRUE';
|
||||
const result = await db.queryOne<Judge>(sql, [name]);
|
||||
if (result) {
|
||||
result.expertise = JSON.parse(result.expertise as any);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 根據ID獲取評審
|
||||
static async getJudgeById(id: string): Promise<Judge | null> {
|
||||
const sql = 'SELECT * FROM judges WHERE id = ? AND is_active = TRUE';
|
||||
const result = await db.queryOne<Judge>(sql, [id]);
|
||||
if (result) {
|
||||
result.expertise = JSON.parse(result.expertise as any);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 獲取所有評審
|
||||
static async getAllJudges(): Promise<Judge[]> {
|
||||
const sql = 'SELECT * FROM judges WHERE is_active = TRUE ORDER BY created_at DESC';
|
||||
const results = await db.query<Judge>(sql);
|
||||
return results.map(judge => ({
|
||||
...judge,
|
||||
expertise: JSON.parse(judge.expertise as any)
|
||||
}));
|
||||
}
|
||||
|
||||
// 更新評審
|
||||
static async updateJudge(id: string, updates: Partial<Judge>): Promise<boolean> {
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
|
||||
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
||||
const values = fields.map(field => {
|
||||
if (field === 'expertise') {
|
||||
return JSON.stringify((updates as any)[field]);
|
||||
}
|
||||
return (updates as any)[field];
|
||||
});
|
||||
|
||||
const sql = `UPDATE judges SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
|
||||
const result = await db.update(sql, [...values, id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 競賽服務
|
||||
// =====================================================
|
||||
export class CompetitionService {
|
||||
// 創建競賽
|
||||
static async createCompetition(competitionData: Omit<Competition, 'id' | 'created_at' | 'updated_at'>): Promise<Competition> {
|
||||
const sql = `
|
||||
INSERT INTO competitions (id, name, year, month, start_date, end_date, status, description, type, evaluation_focus, max_team_size, is_active)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
competitionData.name,
|
||||
competitionData.year,
|
||||
competitionData.month,
|
||||
competitionData.start_date,
|
||||
competitionData.end_date,
|
||||
competitionData.status,
|
||||
competitionData.description || null,
|
||||
competitionData.type,
|
||||
competitionData.evaluation_focus || null,
|
||||
competitionData.max_team_size || null,
|
||||
competitionData.is_active
|
||||
];
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.getCompetitionByName(competitionData.name) as Competition;
|
||||
}
|
||||
|
||||
// 根據名稱獲取競賽
|
||||
static async getCompetitionByName(name: string): Promise<Competition | null> {
|
||||
const sql = 'SELECT * FROM competitions WHERE name = ? AND is_active = TRUE';
|
||||
return await db.queryOne<Competition>(sql, [name]);
|
||||
}
|
||||
|
||||
// 根據ID獲取競賽
|
||||
static async getCompetitionById(id: string): Promise<Competition | null> {
|
||||
const sql = 'SELECT * FROM competitions WHERE id = ? AND is_active = TRUE';
|
||||
return await db.queryOne<Competition>(sql, [id]);
|
||||
}
|
||||
|
||||
// 獲取所有競賽
|
||||
static async getAllCompetitions(): Promise<Competition[]> {
|
||||
const sql = 'SELECT * FROM competitions WHERE is_active = TRUE ORDER BY year DESC, month DESC';
|
||||
return await db.query<Competition>(sql);
|
||||
}
|
||||
|
||||
// 獲取競賽統計
|
||||
static async getCompetitionStatistics(id: string): Promise<CompetitionStatistics | null> {
|
||||
const sql = 'SELECT * FROM competition_statistics WHERE id = ?';
|
||||
return await db.queryOne<CompetitionStatistics>(sql, [id]);
|
||||
}
|
||||
|
||||
// 更新競賽
|
||||
static async updateCompetition(id: string, updates: Partial<Competition>): Promise<boolean> {
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
|
||||
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
||||
const values = fields.map(field => (updates as any)[field]);
|
||||
|
||||
const sql = `UPDATE competitions SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
|
||||
const result = await db.update(sql, [...values, id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 應用服務
|
||||
// =====================================================
|
||||
export class AppService {
|
||||
// 創建應用
|
||||
static async createApp(appData: Omit<App, 'id' | 'created_at' | 'updated_at'>): Promise<App> {
|
||||
const sql = `
|
||||
INSERT INTO apps (id, name, description, creator_id, team_id, category, type, likes_count, views_count, rating, is_active)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
appData.name,
|
||||
appData.description || null,
|
||||
appData.creator_id,
|
||||
appData.team_id || null,
|
||||
appData.category,
|
||||
appData.type,
|
||||
appData.likes_count,
|
||||
appData.views_count,
|
||||
appData.rating,
|
||||
appData.is_active
|
||||
];
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.getAppByName(appData.name) as App;
|
||||
}
|
||||
|
||||
// 根據名稱獲取應用
|
||||
static async getAppByName(name: string): Promise<App | null> {
|
||||
const sql = 'SELECT * FROM apps WHERE name = ? AND is_active = TRUE';
|
||||
return await db.queryOne<App>(sql, [name]);
|
||||
}
|
||||
|
||||
// 根據ID獲取應用
|
||||
static async getAppById(id: string): Promise<App | null> {
|
||||
const sql = 'SELECT * FROM apps WHERE id = ? AND is_active = TRUE';
|
||||
return await db.queryOne<App>(sql, [id]);
|
||||
}
|
||||
|
||||
// 獲取所有應用
|
||||
static async getAllApps(limit = 50, offset = 0): Promise<App[]> {
|
||||
const sql = 'SELECT * FROM apps WHERE is_active = TRUE ORDER BY created_at DESC LIMIT ? OFFSET ?';
|
||||
return await db.query<App>(sql, [limit, offset]);
|
||||
}
|
||||
|
||||
// 獲取應用統計
|
||||
static async getAppStatistics(id: string): Promise<AppStatistics | null> {
|
||||
const sql = 'SELECT * FROM app_statistics WHERE id = ?';
|
||||
return await db.queryOne<AppStatistics>(sql, [id]);
|
||||
}
|
||||
|
||||
// 更新應用
|
||||
static async updateApp(id: string, updates: Partial<App>): Promise<boolean> {
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
|
||||
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
||||
const values = fields.map(field => (updates as any)[field]);
|
||||
|
||||
const sql = `UPDATE apps SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
|
||||
const result = await db.update(sql, [...values, id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 評分服務
|
||||
// =====================================================
|
||||
export class ScoringService {
|
||||
// 提交應用評分
|
||||
static async submitAppScore(scoreData: Omit<AppJudgeScore, 'id' | 'submitted_at'>): Promise<AppJudgeScore> {
|
||||
const sql = `
|
||||
INSERT INTO app_judge_scores (id, judge_id, app_id, innovation_score, technical_score, usability_score, presentation_score, impact_score, total_score, comments)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
innovation_score = VALUES(innovation_score),
|
||||
technical_score = VALUES(technical_score),
|
||||
usability_score = VALUES(usability_score),
|
||||
presentation_score = VALUES(presentation_score),
|
||||
impact_score = VALUES(impact_score),
|
||||
total_score = VALUES(total_score),
|
||||
comments = VALUES(comments),
|
||||
submitted_at = CURRENT_TIMESTAMP
|
||||
`;
|
||||
const params = [
|
||||
scoreData.judge_id,
|
||||
scoreData.app_id,
|
||||
scoreData.innovation_score,
|
||||
scoreData.technical_score,
|
||||
scoreData.usability_score,
|
||||
scoreData.presentation_score,
|
||||
scoreData.impact_score,
|
||||
scoreData.total_score,
|
||||
scoreData.comments || null
|
||||
];
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.getAppScore(scoreData.judge_id, scoreData.app_id) as AppJudgeScore;
|
||||
}
|
||||
|
||||
// 獲取應用評分
|
||||
static async getAppScore(judgeId: string, appId: string): Promise<AppJudgeScore | null> {
|
||||
const sql = 'SELECT * FROM app_judge_scores WHERE judge_id = ? AND app_id = ?';
|
||||
return await db.queryOne<AppJudgeScore>(sql, [judgeId, appId]);
|
||||
}
|
||||
|
||||
// 獲取應用的所有評分
|
||||
static async getAppScores(appId: string): Promise<AppJudgeScore[]> {
|
||||
const sql = 'SELECT * FROM app_judge_scores WHERE app_id = ? ORDER BY submitted_at DESC';
|
||||
return await db.query<AppJudgeScore>(sql, [appId]);
|
||||
}
|
||||
|
||||
// 提交提案評分
|
||||
static async submitProposalScore(scoreData: Omit<ProposalJudgeScore, 'id' | 'submitted_at'>): Promise<ProposalJudgeScore> {
|
||||
const sql = `
|
||||
INSERT INTO proposal_judge_scores (id, judge_id, proposal_id, problem_identification_score, solution_feasibility_score, innovation_score, impact_score, presentation_score, total_score, comments)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
problem_identification_score = VALUES(problem_identification_score),
|
||||
solution_feasibility_score = VALUES(solution_feasibility_score),
|
||||
innovation_score = VALUES(innovation_score),
|
||||
impact_score = VALUES(impact_score),
|
||||
presentation_score = VALUES(presentation_score),
|
||||
total_score = VALUES(total_score),
|
||||
comments = VALUES(comments),
|
||||
submitted_at = CURRENT_TIMESTAMP
|
||||
`;
|
||||
const params = [
|
||||
scoreData.judge_id,
|
||||
scoreData.proposal_id,
|
||||
scoreData.problem_identification_score,
|
||||
scoreData.solution_feasibility_score,
|
||||
scoreData.innovation_score,
|
||||
scoreData.impact_score,
|
||||
scoreData.presentation_score,
|
||||
scoreData.total_score,
|
||||
scoreData.comments || null
|
||||
];
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.getProposalScore(scoreData.judge_id, scoreData.proposal_id) as ProposalJudgeScore;
|
||||
}
|
||||
|
||||
// 獲取提案評分
|
||||
static async getProposalScore(judgeId: string, proposalId: string): Promise<ProposalJudgeScore | null> {
|
||||
const sql = 'SELECT * FROM proposal_judge_scores WHERE judge_id = ? AND proposal_id = ?';
|
||||
return await db.queryOne<ProposalJudgeScore>(sql, [judgeId, proposalId]);
|
||||
}
|
||||
|
||||
// 獲取提案的所有評分
|
||||
static async getProposalScores(proposalId: string): Promise<ProposalJudgeScore[]> {
|
||||
const sql = 'SELECT * FROM proposal_judge_scores WHERE proposal_id = ? ORDER BY submitted_at DESC';
|
||||
return await db.query<ProposalJudgeScore>(sql, [proposalId]);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 獎項服務
|
||||
// =====================================================
|
||||
export class AwardService {
|
||||
// 創建獎項
|
||||
static async createAward(awardData: Omit<Award, 'id' | 'created_at'>): Promise<Award> {
|
||||
const sql = `
|
||||
INSERT INTO awards (id, competition_id, app_id, team_id, proposal_id, app_name, team_name, proposal_title, creator, award_type, award_name, score, year, month, icon, custom_award_type_id, competition_type, rank, category)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
awardData.competition_id,
|
||||
awardData.app_id || null,
|
||||
awardData.team_id || null,
|
||||
awardData.proposal_id || null,
|
||||
awardData.app_name || null,
|
||||
awardData.team_name || null,
|
||||
awardData.proposal_title || null,
|
||||
awardData.creator,
|
||||
awardData.award_type,
|
||||
awardData.award_name,
|
||||
awardData.score,
|
||||
awardData.year,
|
||||
awardData.month,
|
||||
awardData.icon,
|
||||
awardData.custom_award_type_id || null,
|
||||
awardData.competition_type,
|
||||
awardData.rank,
|
||||
awardData.category
|
||||
];
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.getAwardByCompetitionAndCreator(awardData.competition_id, awardData.creator) as Award;
|
||||
}
|
||||
|
||||
// 根據競賽和創作者獲取獎項
|
||||
static async getAwardByCompetitionAndCreator(competitionId: string, creator: string): Promise<Award | null> {
|
||||
const sql = 'SELECT * FROM awards WHERE competition_id = ? AND creator = ? ORDER BY created_at DESC LIMIT 1';
|
||||
return await db.queryOne<Award>(sql, [competitionId, creator]);
|
||||
}
|
||||
|
||||
// 根據年份獲取獎項
|
||||
static async getAwardsByYear(year: number): Promise<Award[]> {
|
||||
const sql = 'SELECT * FROM awards WHERE year = ? ORDER BY month DESC, rank ASC';
|
||||
return await db.query<Award>(sql, [year]);
|
||||
}
|
||||
|
||||
// 根據競賽獲取獎項
|
||||
static async getAwardsByCompetition(competitionId: string): Promise<Award[]> {
|
||||
const sql = 'SELECT * FROM awards WHERE competition_id = ? ORDER BY rank ASC';
|
||||
return await db.query<Award>(sql, [competitionId]);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 系統設定服務
|
||||
// =====================================================
|
||||
export class SystemSettingService {
|
||||
// 獲取設定值
|
||||
static async getSetting(key: string): Promise<string | null> {
|
||||
const sql = 'SELECT value FROM system_settings WHERE `key` = ?';
|
||||
const result = await db.queryOne<{ value: string }>(sql, [key]);
|
||||
return result?.value || null;
|
||||
}
|
||||
|
||||
// 設定值
|
||||
static async setSetting(key: string, value: string, description?: string, category = 'general', isPublic = false): Promise<boolean> {
|
||||
const sql = `
|
||||
INSERT INTO system_settings (id, \`key\`, value, description, category, is_public)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
value = VALUES(value),
|
||||
description = VALUES(description),
|
||||
category = VALUES(category),
|
||||
is_public = VALUES(is_public),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
`;
|
||||
const params = [key, value, description || null, category, isPublic];
|
||||
|
||||
const result = await db.insert(sql, params);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取所有公開設定
|
||||
static async getPublicSettings(): Promise<SystemSetting[]> {
|
||||
const sql = 'SELECT * FROM system_settings WHERE is_public = TRUE ORDER BY category, `key`';
|
||||
return await db.query<SystemSetting>(sql);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user