汇总/分析卡按用户口径对齐:销售统计仅本人;隐藏面向管理的组合分析

- countsByStatus 支持 assessorId 过滤;summary 端点对销售按 JWT.uid 强制本人统计
- 看板汇总卡销售看本人(我的评估/被驳回/我的待风控/我的已通过)
- 预测准确度/驳回Top/到期/超时 仅风控与管理层展示,销售不再看到跨人数据
This commit is contained in:
freedakgmail
2026-06-13 19:50:49 +08:00
parent a3906fc1b6
commit aff293d40e
4 changed files with 34 additions and 18 deletions
+9 -4
View File
@@ -316,13 +316,17 @@ export class PgAssessmentStore implements AssessmentStore {
return { items, total };
}
/** 各工作流状态的记录数(用于工作台统计卡)。仅统计未归档项,另返回归档总数。 */
async countsByStatus(): Promise<{ total: number; byStatus: Record<string, number>; archived: number }> {
/** 各工作流状态的记录数(用于工作台统计卡)。仅统计未归档项,另返回归档总数。可按发起人(assessorId)过滤。 */
async countsByStatus(assessorId?: string): Promise<{ total: number; byStatus: Record<string, number>; archived: number }> {
const scoped = assessorId !== undefined && assessorId !== '';
const cond = scoped ? `AND a.assessment->>'assessorId' = $1` : '';
const params = scoped ? [assessorId] : [];
const res = await this.pool.query(
`SELECT COALESCE(ws.status, 'pending_risk_review') AS status, count(*)::int AS n
FROM assessments a LEFT JOIN workflow_status ws ON ws.assessment_id = a.id
WHERE a.archived = false
WHERE a.archived = false ${cond}
GROUP BY 1`,
params,
);
const byStatus: Record<string, number> = {};
let total = 0;
@@ -331,7 +335,8 @@ export class PgAssessmentStore implements AssessmentStore {
total += Number(r.n);
}
const arc = await this.pool.query(
'SELECT count(*)::int AS n FROM assessments WHERE archived = true',
`SELECT count(*)::int AS n FROM assessments a WHERE archived = true ${cond}`,
params,
);
const archived = Number((arc.rows[0] as { n: number }).n);
return { total, byStatus, archived };
+6 -1
View File
@@ -1678,13 +1678,18 @@ app.get('/api/assessments', async (c) => {
/** 工作台统计:各状态评估数。 */
app.get('/api/assessments/summary', async (c) => {
// 行级隔离:销售只统计本人发起的;风控/管理层全局。
let scopeId = c.req.query('assessorId');
const authU = (c as import('hono').Context).get('user') as AuthPayload | undefined;
if (authU?.role === '商务/销售' && authU.uid) scopeId = authU.uid;
if (store instanceof PgAssessmentStore) {
return c.json(await store.countsByStatus());
return c.json(await store.countsByStatus(scopeId));
}
const byStatus: Record<string, number> = {};
let archivedCount = 0;
let activeTotal = 0;
for (const r of store.getAll()) {
if (scopeId !== undefined && scopeId !== '' && (r.assessment as unknown as { assessorId?: string }).assessorId !== scopeId) continue;
if (archivedById.get(r.assessment.id) === true) {
archivedCount += 1;
continue;