完成 APP 建立流程和使用分析、增加主機備機的備援機制、管理者後臺增加資料庫監控

This commit is contained in:
2025-09-12 18:22:30 +08:00
parent 9c5dceb001
commit b85a9ce95e
19 changed files with 2982 additions and 757 deletions

View File

@@ -202,69 +202,6 @@ export class UserService {
};
}
// 獲取用戶的應用和評價統計
async getUserAppAndReviewStats(userId: string): Promise<{
appCount: number;
reviewCount: number;
}> {
try {
console.log('獲取用戶統計數據userId:', userId);
// 先檢查資料庫中是否有數據
const checkAppsSql = 'SELECT COUNT(*) as total FROM apps';
const checkRatingsSql = 'SELECT COUNT(*) as total FROM user_ratings';
const checkUsersSql = 'SELECT COUNT(*) as total FROM users';
const [totalApps, totalRatings, totalUsers] = await Promise.all([
this.queryOne(checkAppsSql),
this.queryOne(checkRatingsSql),
this.queryOne(checkUsersSql)
]);
console.log('資料庫總數據:', {
totalApps: totalApps?.total || 0,
totalRatings: totalRatings?.total || 0,
totalUsers: totalUsers?.total || 0
});
// 獲取用戶創建的應用數量
const appCountSql = `
SELECT COUNT(*) as app_count
FROM apps
WHERE creator_id = ?
`;
// 獲取用戶撰寫的評價數量
const reviewCountSql = `
SELECT COUNT(*) as review_count
FROM user_ratings
WHERE user_id = ?
`;
const [appResult, reviewResult] = await Promise.all([
this.queryOne(appCountSql, [userId]),
this.queryOne(reviewCountSql, [userId])
]);
console.log('應用查詢結果:', appResult);
console.log('評價查詢結果:', reviewResult);
const result = {
appCount: appResult?.app_count || 0,
reviewCount: reviewResult?.review_count || 0
};
console.log('最終統計結果:', result);
return result;
} catch (error) {
console.error('獲取用戶應用和評價統計錯誤:', error);
return {
appCount: 0,
reviewCount: 0
};
}
}
// 獲取用戶活動記錄
async getUserActivities(
userId: string,
@@ -804,7 +741,7 @@ export class UserService {
// 獲取所有用戶
async getAllUsers(limit = 50, offset = 0): Promise<User[]> {
const sql = 'SELECT * FROM users WHERE is_active = TRUE ORDER BY created_at DESC LIMIT ? OFFSET ?';
const sql = 'SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?';
return await db.query<User>(sql, [limit, offset]);
}
@@ -839,6 +776,46 @@ export class UserService {
const service = new UserService();
return await service.getAllUsers(limit, offset);
}
// 獲取用戶的應用和評價統計
async getUserAppAndReviewStats(userId: string): Promise<{
appCount: number;
reviewCount: number;
}> {
try {
// 獲取用戶創建的應用數量
const appCountSql = `
SELECT COUNT(*) as app_count
FROM apps
WHERE creator_id = ?
`;
// 獲取用戶撰寫的評價數量
const reviewCountSql = `
SELECT COUNT(*) as review_count
FROM user_ratings
WHERE user_id = ?
`;
const [appResult, reviewResult] = await Promise.all([
this.queryOne(appCountSql, [userId]),
this.queryOne(reviewCountSql, [userId])
]);
const result = {
appCount: appResult?.app_count || 0,
reviewCount: reviewResult?.review_count || 0
};
return result;
} catch (error) {
console.error('獲取用戶應用和評價統計錯誤:', error);
return {
appCount: 0,
reviewCount: 0
};
}
}
}
// =====================================================
@@ -1358,7 +1335,7 @@ export class AppService {
}
// 獲取應用使用統計
async getAppUsageStats(appId: string, startDate?: string, endDate?: string): Promise<{
async getAppUsageStats(appId: string, startDate?: string, endDate?: string, department?: string): Promise<{
dailyUsers: number;
weeklyUsers: number;
monthlyUsers: number;
@@ -1369,7 +1346,7 @@ export class AppService {
try {
// 今日使用者
const dailySql = `
SELECT COUNT(DISTINCT user_id) as daily_users
SELECT COUNT(*) as daily_users
FROM user_views
WHERE app_id = ? AND DATE(viewed_at) = CURDATE()
`;
@@ -1377,7 +1354,7 @@ export class AppService {
// 本週使用者
const weeklySql = `
SELECT COUNT(DISTINCT user_id) as weekly_users
SELECT COUNT(*) as weekly_users
FROM user_views
WHERE app_id = ? AND viewed_at >= DATE_SUB(CURDATE(), INTERVAL 1 WEEK)
`;
@@ -1385,7 +1362,7 @@ export class AppService {
// 本月使用者
const monthlySql = `
SELECT COUNT(DISTINCT user_id) as monthly_users
SELECT COUNT(*) as monthly_users
FROM user_views
WHERE app_id = ? AND viewed_at >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)
`;
@@ -1399,48 +1376,73 @@ export class AppService {
`;
const totalResult = await this.queryOne(totalSql, [appId]);
// 部門使用統計
const deptSql = `
SELECT
u.department,
COUNT(*) as count
FROM user_views uv
JOIN users u ON uv.user_id = u.id
WHERE uv.app_id = ?
GROUP BY u.department
ORDER BY count DESC
LIMIT 5
`;
const deptResult = await this.query(deptSql, [appId]);
// 部門使用統計 - 支援日期範圍過濾
let deptSql: string;
let deptParams: any[];
// 使用趨勢 - 支援自定義日期範圍
if (startDate && endDate) {
deptSql = `
SELECT
u.department,
COUNT(*) as count
FROM user_views uv
JOIN users u ON uv.user_id = u.id
WHERE uv.app_id = ? AND DATE(uv.viewed_at) BETWEEN ? AND ?
GROUP BY u.department
ORDER BY count DESC
LIMIT 5
`;
deptParams = [appId, startDate, endDate];
} else {
deptSql = `
SELECT
u.department,
COUNT(*) as count
FROM user_views uv
JOIN users u ON uv.user_id = u.id
WHERE uv.app_id = ?
GROUP BY u.department
ORDER BY count DESC
LIMIT 5
`;
deptParams = [appId];
}
const deptResult = await this.query(deptSql, deptParams);
// 使用趨勢 - 支援自定義日期範圍和部門過濾
let trendSql: string;
let trendParams: any[];
// 構建部門過濾條件
const departmentFilter = department ? 'AND u.department = ?' : '';
const baseWhere = `uv.app_id = ? ${departmentFilter}`;
if (startDate && endDate) {
// 使用自定義日期範圍
trendSql = `
SELECT
DATE(viewed_at) as date,
COUNT(DISTINCT user_id) as users
FROM user_views
WHERE app_id = ? AND DATE(viewed_at) BETWEEN ? AND ?
GROUP BY DATE(viewed_at)
DATE(uv.viewed_at) as date,
COUNT(*) as users
FROM user_views uv
JOIN users u ON uv.user_id = u.id
WHERE ${baseWhere} AND DATE(uv.viewed_at) BETWEEN ? AND ?
GROUP BY DATE(uv.viewed_at)
ORDER BY date ASC
`;
trendParams = [appId, startDate, endDate];
trendParams = department ? [appId, department, startDate, endDate] : [appId, startDate, endDate];
} else {
// 預設過去7天
trendSql = `
SELECT
DATE(viewed_at) as date,
COUNT(DISTINCT user_id) as users
FROM user_views
WHERE app_id = ? AND viewed_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY DATE(viewed_at)
DATE(uv.viewed_at) as date,
COUNT(*) as users
FROM user_views uv
JOIN users u ON uv.user_id = u.id
WHERE ${baseWhere} AND uv.viewed_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY DATE(uv.viewed_at)
ORDER BY date ASC
`;
trendParams = [appId];
trendParams = department ? [appId, department] : [appId];
}
const trendResult = await this.query(trendSql, trendParams);