追回证据刷新期间禁用选择并修复员工资料登录误报(09-17-company-loan-principal-repayment)

- 上传银行证据后、刷新完成前,证据下拉框与确认按钮禁用并提示,避免空证据提交
- 员工来源版本去掉 updated_at(登录会刷新),保留全部真实资料字段的拦截
- 补登录误报回归、字段变更拦截、两项变异及刷新挂起的浏览器检查
- 任务记录 E 节与 AC52–55,独立只读复核无阻断项

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
2026-10-11 09:22:34 +08:00
共同撰写人 Claude Opus 5.5
父节点 91fe31f0db
当前提交 3c5cc544f5
共修改 7 个文件,包含 104 行新增和 32 行删除
+7 -4
查看文件
@@ -42,7 +42,7 @@ async function assertUniqueEmployeeLender(tx, employeeId, legalEntityId, lenderI
async function readEmployeeSource(db, config, employeeId, legalEntityId) {
const employee = await db.employee.findUnique({ where: { id: employeeId }, select: {
id: true, name: true, employee_no: true, legal_entity_id: true, status: true, archived_at: true, updated_at: true,
id: true, name: true, employee_no: true, legal_entity_id: true, status: true, archived_at: true,
bank_name: true, bank_institution_id: true, bank_account_encrypted: true,
legal_entity: { select: { name: true } },
bank_institution: { select: { id: true, code: true, name: true, enabled: true, archived_at: true } },
@@ -67,10 +67,13 @@ async function readEmployeeSource(db, config, employeeId, legalEntityId) {
else if (!/^\d{10,30}$/.test(bankAccount)) problems.push("account_invalid");
} catch { problems.push("account_unavailable"); }
}
// Bind to all source fields, not just the millisecond timestamp. Ciphertext is
// hashed without exposing it or making a plaintext account hash enumerable.
// Bind to every mutable employee field the preview shows or the save copies
// (employee_no is immutable in the database). updated_at is left out: a
// successful login resets the failure counters and touches it without
// changing any of these. Ciphertext is hashed without exposing it or making
// a plaintext account hash enumerable.
const sourceVersion = createHash("sha256").update(JSON.stringify([
"loan-employee-source-v1", legalEntityId, employee.id, employee.updated_at,
"loan-employee-source-v2", legalEntityId, employee.id,
employee.name, employee.legal_entity_id, employee.status, employee.archived_at,
employee.bank_name, employee.bank_institution_id, employee.bank_account_encrypted, bank,
])).digest("hex");