feat(M08-B): 补保洁统计趋势
This commit is contained in:
@@ -137,6 +137,15 @@ interface ManagerMemberStatsRow extends RowDataPacket {
|
||||
pendingSettlementCents: number | null;
|
||||
settledRewardCents: number | null;
|
||||
}
|
||||
interface ManagerDailyTrendRow extends RowDataPacket {
|
||||
bucketDate: string | Date;
|
||||
taskTotal: number;
|
||||
pendingReview: number;
|
||||
completed: number;
|
||||
rejected: number;
|
||||
rewardCents: number | null;
|
||||
paidSettlementCents: number | null;
|
||||
}
|
||||
|
||||
export class CleaningTaskRepository {
|
||||
constructor(private readonly pool: MySqlPool) {}
|
||||
@@ -827,10 +836,34 @@ export class CleaningTaskRepository {
|
||||
WHERE ${taskWhereSql}
|
||||
GROUP BY m.user_id, u.nickname
|
||||
ORDER BY pendingSettlementCents DESC, settledRewardCents DESC, taskCount DESC
|
||||
LIMIT 20`,
|
||||
LIMIT 20`,
|
||||
taskParams
|
||||
);
|
||||
return publicManagerStatistics(statusRows, storeRows, settlementRows, memberRows);
|
||||
const [trendRows] = await this.pool.execute<ManagerDailyTrendRow[]>(
|
||||
`SELECT DATE(t.created_at) AS bucketDate,
|
||||
COUNT(*) AS taskTotal,
|
||||
SUM(CASE WHEN t.status = 'SUBMITTED' THEN 1 ELSE 0 END) AS pendingReview,
|
||||
SUM(CASE WHEN t.status IN ('COMPLETED', 'SETTLED') THEN 1 ELSE 0 END) AS completed,
|
||||
SUM(CASE WHEN t.status = 'REJECTED' THEN 1 ELSE 0 END) AS rejected,
|
||||
COALESCE(SUM(t.reward_cents), 0) AS rewardCents,
|
||||
COALESCE((
|
||||
SELECT SUM(s.total_reward_cents)
|
||||
FROM qipai_cleaning_settlements s
|
||||
WHERE s.tenant_id = t.tenant_id
|
||||
AND s.deleted_at IS NULL
|
||||
AND s.status = 'PAID'
|
||||
AND DATE(s.paid_at) = DATE(t.created_at)
|
||||
AND ${storeScopeSql(input.access, 'COALESCE(s.store_id, 0)')}
|
||||
${input.storeId ? 'AND s.store_id = ?' : ''}
|
||||
), 0) AS paidSettlementCents
|
||||
FROM qipai_cleaning_tasks t
|
||||
WHERE ${taskWhereSql}
|
||||
GROUP BY DATE(t.created_at), t.tenant_id
|
||||
ORDER BY bucketDate ASC
|
||||
LIMIT 62`,
|
||||
input.storeId ? [input.storeId, ...taskParams] : taskParams
|
||||
);
|
||||
return publicManagerStatistics(statusRows, storeRows, settlementRows, memberRows, trendRows);
|
||||
}
|
||||
|
||||
private async listByWhere(
|
||||
@@ -1259,7 +1292,8 @@ function publicManagerStatistics(
|
||||
statusRows: ManagerStatusStatsRow[],
|
||||
storeRows: ManagerStoreStatsRow[],
|
||||
settlementRows: ManagerSettlementStatsRow[],
|
||||
memberRows: ManagerMemberStatsRow[]
|
||||
memberRows: ManagerMemberStatsRow[],
|
||||
trendRows: ManagerDailyTrendRow[]
|
||||
) {
|
||||
const byStatus = statusRows.map((row) => ({
|
||||
status: row.status,
|
||||
@@ -1304,6 +1338,15 @@ function publicManagerStatistics(
|
||||
taskCount: Number(row.taskCount),
|
||||
pendingSettlementCents: Number(row.pendingSettlementCents ?? 0),
|
||||
settledRewardCents: Number(row.settledRewardCents ?? 0)
|
||||
})),
|
||||
trend: trendRows.map((row) => ({
|
||||
date: formatDateOnly(row.bucketDate),
|
||||
taskTotal: Number(row.taskTotal),
|
||||
pendingReview: Number(row.pendingReview),
|
||||
completed: Number(row.completed),
|
||||
rejected: Number(row.rejected),
|
||||
rewardCents: Number(row.rewardCents ?? 0),
|
||||
paidSettlementCents: Number(row.paidSettlementCents ?? 0)
|
||||
}))
|
||||
};
|
||||
}
|
||||
@@ -1315,6 +1358,11 @@ function countStatus(
|
||||
return rows.find((row) => row.status === status)?.total ?? 0;
|
||||
}
|
||||
|
||||
function formatDateOnly(value: string | Date) {
|
||||
if (value instanceof Date) return value.toISOString().slice(0, 10);
|
||||
return String(value).slice(0, 10);
|
||||
}
|
||||
|
||||
function minDate(values: Array<Date | null>) {
|
||||
const dates = values.filter((value): value is Date => value instanceof Date);
|
||||
if (dates.length === 0) return null;
|
||||
|
||||
@@ -188,6 +188,15 @@ const app = await buildApp({
|
||||
taskCount: 3,
|
||||
pendingSettlementCents: 1200,
|
||||
settledRewardCents: 600
|
||||
}],
|
||||
trend: [{
|
||||
date: '2026-06-30',
|
||||
taskTotal: 5,
|
||||
pendingReview: 2,
|
||||
completed: 1,
|
||||
rejected: 1,
|
||||
rewardCents: 3000,
|
||||
paidSettlementCents: 600
|
||||
}]
|
||||
};
|
||||
}
|
||||
@@ -291,6 +300,7 @@ assert.equal(managerStats.statusCode, 200);
|
||||
assert.equal(calls.at(-1)[0], 'managerStatistics');
|
||||
assert.equal(calls.at(-1)[1].storeId, '11');
|
||||
assert.equal(managerStats.json().data.summary.pendingSettlementCents, 1200);
|
||||
assert.equal(managerStats.json().data.trend[0].paidSettlementCents, 600);
|
||||
|
||||
const claim = await app.inject({
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user