實作前台 APP 呈現、APP 詳細頁面
This commit is contained in:
@@ -18,6 +18,14 @@ const dbConfig = {
|
||||
reconnect: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
// 添加連接重試和錯誤處理配置
|
||||
retryDelay: 2000,
|
||||
maxRetries: 3,
|
||||
// 添加連接池配置
|
||||
idleTimeout: 300000,
|
||||
maxIdle: 10,
|
||||
// 添加 SSL 配置(如果需要)
|
||||
ssl: false,
|
||||
};
|
||||
|
||||
// 創建連接池
|
||||
@@ -46,19 +54,51 @@ export class Database {
|
||||
|
||||
// 執行查詢
|
||||
public async query<T = any>(sql: string, params?: any[]): Promise<T[]> {
|
||||
const connection = await this.getConnection();
|
||||
try {
|
||||
const [rows] = await connection.execute(sql, params);
|
||||
return rows as T[];
|
||||
} finally {
|
||||
connection.release();
|
||||
let connection;
|
||||
let retries = 0;
|
||||
const maxRetries = 3;
|
||||
|
||||
while (retries < maxRetries) {
|
||||
try {
|
||||
connection = await this.getConnection();
|
||||
const [rows] = await connection.execute(sql, params);
|
||||
return rows as T[];
|
||||
} catch (error: any) {
|
||||
console.error(`資料庫查詢錯誤 (嘗試 ${retries + 1}/${maxRetries}):`, error.message);
|
||||
|
||||
if (connection) {
|
||||
connection.release();
|
||||
}
|
||||
|
||||
if (error.code === 'ECONNRESET' || error.code === 'PROTOCOL_CONNECTION_LOST') {
|
||||
retries++;
|
||||
if (retries < maxRetries) {
|
||||
console.log(`等待 ${2000 * retries}ms 後重試...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000 * retries));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
if (connection) {
|
||||
connection.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('資料庫連接失敗,已達到最大重試次數');
|
||||
}
|
||||
|
||||
// 執行單一查詢
|
||||
public async queryOne<T = any>(sql: string, params?: any[]): Promise<T | null> {
|
||||
const results = await this.query<T>(sql, params);
|
||||
return results.length > 0 ? results[0] : null;
|
||||
try {
|
||||
const results = await this.query<T>(sql, params);
|
||||
return results.length > 0 ? results[0] : null;
|
||||
} catch (error) {
|
||||
console.error('資料庫單一查詢錯誤:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 執行插入
|
||||
|
@@ -239,6 +239,7 @@ export interface UserRating {
|
||||
user_id: string;
|
||||
app_id: string;
|
||||
rating: number;
|
||||
comment?: string;
|
||||
rated_at: string;
|
||||
}
|
||||
|
||||
|
@@ -1396,6 +1396,7 @@ export class AppService {
|
||||
SELECT
|
||||
ur.id,
|
||||
ur.rating,
|
||||
ur.comment,
|
||||
ur.rated_at,
|
||||
u.name as user_name,
|
||||
u.department as user_department,
|
||||
@@ -1411,7 +1412,7 @@ export class AppService {
|
||||
return reviews.map((review: any) => ({
|
||||
id: review.id,
|
||||
rating: review.rating,
|
||||
review: '用戶評價', // 暫時使用固定文字,因為資料庫中沒有 review 欄位
|
||||
review: review.comment || '用戶評價', // 使用 comment 欄位,如果為空則顯示預設文字
|
||||
ratedAt: review.rated_at,
|
||||
userName: review.user_name,
|
||||
userDepartment: review.user_department,
|
||||
@@ -1464,6 +1465,79 @@ export class AppService {
|
||||
return { success: false, error: '刪除評價時發生錯誤' };
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取應用部門列表
|
||||
async getAppDepartments(): Promise<{ department: string; count: number }[]> {
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
u.department,
|
||||
COUNT(DISTINCT a.id) as count
|
||||
FROM apps a
|
||||
JOIN users u ON a.creator_id = u.id
|
||||
WHERE a.is_active = TRUE
|
||||
GROUP BY u.department
|
||||
ORDER BY count DESC, u.department ASC
|
||||
`;
|
||||
|
||||
const departments = await this.query(sql);
|
||||
return departments.map((dept: any) => ({
|
||||
department: dept.department,
|
||||
count: parseInt(dept.count)
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('獲取應用部門列表錯誤:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取應用類型列表
|
||||
async getAppTypes(): Promise<{ type: string; count: number }[]> {
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
type,
|
||||
COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE is_active = TRUE
|
||||
GROUP BY type
|
||||
ORDER BY count DESC, type ASC
|
||||
`;
|
||||
|
||||
const types = await this.query(sql);
|
||||
return types.map((type: any) => ({
|
||||
type: type.type,
|
||||
count: parseInt(type.count)
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('獲取應用類型列表錯誤:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取應用分類列表
|
||||
async getAppCategories(): Promise<{ category: string; count: number }[]> {
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
category,
|
||||
COUNT(*) as count
|
||||
FROM apps
|
||||
WHERE is_active = TRUE
|
||||
GROUP BY category
|
||||
ORDER BY count DESC, category ASC
|
||||
`;
|
||||
|
||||
const categories = await this.query(sql);
|
||||
return categories.map((cat: any) => ({
|
||||
category: cat.category,
|
||||
count: parseInt(cat.count)
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('獲取應用分類列表錯誤:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
|
Reference in New Issue
Block a user