feat(admin): 方案A—消息铃铛接真实待办聚合 + 清理空壳接口与空卡片

【消息铃铛(真实数据)】
- POST /api/message/list 改为待办聚合:待接单订单/待审核大神/待审核提现/待处理举报/待仲裁工单(复用 getDashboardTodos + 新增 pendingOrdersCount)
- POST /api/message/read 持久化已读:新表 admin_message_reads 记录"已读时的数量",待办数量增加会重新变未读
- 两个接口均加 requireAdmin(待办数量属运营数据,不再匿名可见)

【删除空壳/死接口(前端已无调用方)】
- /api/user/save-info、/api/chat/list、/api/user/my-team/list、/api/user/latest-activity、/api/user/my-project/list

【后台页面清理】
- 「用户与个人中心 → 管理员资料」移除 我的项目/最近动态/我的团队/站内通知 四张空卡(保留真实的管理员资料头部)
- 删除对应 4 个组件;api/user-center.ts 移除 queryMyProjectList/queryMyTeamList/queryLatestActivity/saveUserInfo;api/message.ts 移除 queryChatList

【验证】
- 新增 admin-todo-bell.spec.ts;更新 admin-demo-data-cleanup.spec.ts
- 全量 101 套件 / 690 用例全绿;冒烟 8/8(未登录50008、待办数量、已读、新增后重新未读)
- 后台 pnpm build 通过
这个提交包含在:
xcbyy
2026-10-20 15:52:04 +08:00
父节点 05815b4708
当前提交 f3ea26d161
共有 16 个文件被更改,包括 250 次插入 和 455 次删除

查看文件

@@ -661,6 +661,15 @@ export async function initDatabase(): Promise<void> {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 管理端消息铃铛:待办已读状态(记录已读时数量,新增待办会重新变未读)
await conn.query(`
CREATE TABLE IF NOT EXISTS admin_message_reads (
todo_key VARCHAR(64) PRIMARY KEY,
read_count INT NOT NULL DEFAULT 0,
read_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 用户已领取的优惠券(券中心领取 → 下单核销 → 取消/退款回退)
await conn.query(`
CREATE TABLE IF NOT EXISTS user_coupons (

查看文件

@@ -3729,6 +3729,13 @@ export class MysqlRepository {
} catch (e) {
logServerError('getDashboardTodos.reports', e);
}
let pendingOrdersCount = 0;
try {
const [oRows]: any = await pool.query('SELECT COUNT(*) as c FROM orders WHERE status = "待接单"');
pendingOrdersCount = Number((oRows && oRows[0] && oRows[0].c) || 0);
} catch (e) {
logServerError('getDashboardTodos.orders', e);
}
const pendingWithdrawalAmount = wdRows.reduce((sum: number, w: any) => sum + Number(w.amount), 0);
@@ -3737,6 +3744,7 @@ export class MysqlRepository {
pendingArbitrationsCount: arbRows.length,
pendingWithdrawalsCount: wdRows.length,
pendingWithdrawalAmount,
pendingOrdersCount,
pendingReportsCount: rptRows.length,
riskInterceptionsCount: secRows.length,
pendingAudits: auditRows.map((c: any) => ({
@@ -3768,6 +3776,38 @@ export class MysqlRepository {
};
}
/** 管理员待办"已读"状态:记录每个待办项已读时的数量,用于判断是否有新增待办 */
async getAdminTodoReadCounts(): Promise<Record<string, number>> {
try {
const [rows]: any = await pool.query('SELECT todo_key, read_count FROM admin_message_reads');
const map: Record<string, number> = {};
for (const r of rows || []) map[String(r.todo_key)] = Number(r.read_count) || 0;
return map;
} catch (e) {
logServerError('getAdminTodoReadCounts', e);
return {};
}
}
async markAdminTodosRead(items: Array<{ key: string; count: number }>): Promise<number> {
let affected = 0;
for (const it of items || []) {
if (!it || !it.key) continue;
try {
await pool.query(
`INSERT INTO admin_message_reads (todo_key, read_count, read_at)
VALUES (?, ?, NOW())
ON DUPLICATE KEY UPDATE read_count = VALUES(read_count), read_at = NOW()`,
[String(it.key), Number(it.count) || 0]
);
affected += 1;
} catch (e) {
logServerError('markAdminTodosRead', e);
}
}
return affected;
}
// 全平台用户管理相关
async getUsers(filter?: { keyword?: string; role?: string; status?: string }): Promise<any[]> {
let sql = `