大改动:全部人员用ID关联(JWT带uid + audit actor_id + assessorId改用户ID + 看板按ID匹配 + 历史回填)

- JWT 载荷增加 uid;登录返回 id;前端持久化 uid 并在变更请求中携带 userId
- 操作人服务端解析(优先JWT.uid,回退body.userId),审计写入 actor_id + 当时显示名
- audit_logs 增加 actor_id 列;持久化与加载带 actorId
- assessments.assessorId 改存用户ID,列表/详情按ID解析显示名(assessorName)
- 看板待办「分给我的」改为按 userId 匹配;发起人显示真实姓名
- 审批线指派按 salesId(用户ID) 计算
- scripts/backfill-actor-ids.sql 回填历史(旧账号名→当前用户ID),本地+生产已执行
This commit is contained in:
freedakgmail
2026-06-13 19:18:17 +08:00
parent e86e60208f
commit 5afe021b56
10 changed files with 194 additions and 47 deletions
+7 -6
View File
@@ -159,7 +159,7 @@ export function Dashboard(): JSX.Element {
fetch(`${API_BASE}/api/accuracy`).then((r) => r.json()).then(setAccuracy).catch(() => undefined);
// 草稿箱(仅销售展示):列出当前用户的向导草稿。
if (role === '商务/销售') {
listDrafts(user?.username ?? undefined).then(setDrafts).catch(() => setDrafts([]));
listDrafts(user?.id ?? undefined).then(setDrafts).catch(() => setDrafts([]));
} else {
setDrafts([]);
}
@@ -207,7 +207,7 @@ export function Dashboard(): JSX.Element {
<span style={{ fontWeight: 700, color: colorVar('color.text.primary'), whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{display}
</span>
<span style={{ ...typographyStyle('caption'), color: colorVar('color.text.secondary'), whiteSpace: 'nowrap' }}>{r.assessorId}</span>
<span style={{ ...typographyStyle('caption'), color: colorVar('color.text.secondary'), whiteSpace: 'nowrap' }}>{r.assessorName ?? r.assessorId}</span>
</div>
);
},
@@ -517,16 +517,16 @@ export function Dashboard(): JSX.Element {
)}
{todoItems.length > 0 && (() => {
const myName = user?.username;
const myId = user?.id;
const assignedToMe = (id: string): boolean => {
const a = assignments[id];
if (a === undefined) return false;
return role === '风控' ? a.riskReviewerName === myName : role === '管理层' ? a.managerName === myName : false;
return role === '风控' ? a.riskReviewerId === myId : role === '管理层' ? a.managerId === myId : false;
};
const isAssigned = (id: string): boolean => {
const a = assignments[id];
if (a === undefined) return false;
return role === '风控' ? a.riskReviewerName !== null : role === '管理层' ? a.managerName !== null : false;
return role === '风控' ? a.riskReviewerId !== null : role === '管理层' ? a.managerId !== null : false;
};
// 软约束:默认只看分给我的;未指派的也展示(避免遗漏)。
const shown = onlyMine ? todoItems.filter((t) => assignedToMe(t.id) || !isAssigned(t.id)) : todoItems;
@@ -536,8 +536,9 @@ export function Dashboard(): JSX.Element {
render: (r) => {
const a = assignments[r.id];
const name = a !== undefined ? (role === '管理层' ? a.managerName : a.riskReviewerName) : null;
const aid = a !== undefined ? (role === '管理层' ? a.managerId : a.riskReviewerId) : null;
if (name === null || name === undefined) return <span style={{ color: colorVar('color.text.secondary') }}></span>;
const mine = name === myName;
const mine = aid === myId;
return <span style={{ ...typographyStyle('caption'), fontWeight: 600, color: mine ? '#15803D' : colorVar('color.text.primary') }}>{name}{mine ? '(我)' : ''}</span>;
},
};