实现企业银行账户与建行制单(corporate-bank-account-payment-order)

这个提交包含在:
2026-08-02 23:36:40 +08:00
父节点 f946d64539
当前提交 5261d2849b
共修改 46 个文件,包含 2751 行新增和 73 行删除
@@ -85,15 +85,16 @@ describe("user sensitive field permissions", () => {
await testApp.app.close();
});
it("returns only bank fields to finance and audits the authorized scope", async () => {
it("returns only masked bank fields to finance without a full-account view audit", async () => {
const testApp = await buildApp(["employee.bank.read"]);
const response = await testApp.app.inject({ method: "GET", url: `/api/users/${EMPLOYEE_ID}/sensitive` });
expect(response.statusCode).toBe(200);
expect(response.json().data).toEqual({
bankInstitution: null,
bankName: "合成银行",
bankAccount: "6222000012345678",
bankAccountMasked: "6222********5678",
});
expect(response.json().data).not.toHaveProperty("idNumber");
expect(testApp.employeeFindUnique).toHaveBeenCalledWith({
@@ -101,15 +102,12 @@ describe("user sensitive field permissions", () => {
select: {
id: true,
bank_name: true,
bank_institution_id: true,
bank_account_encrypted: true,
bank_institution: { select: { id: true, code: true, name: true } },
},
});
expect(testApp.auditCreate).toHaveBeenCalledWith({
data: expect.objectContaining({
action: "SYSTEM_USER_SENSITIVE_VIEWED",
extra: { scopes: ["bank"] },
}),
});
expect(testApp.auditCreate).not.toHaveBeenCalled();
await testApp.app.close();
});
@@ -119,7 +117,7 @@ describe("user sensitive field permissions", () => {
const response = await testApp.app.inject({
method: "PATCH",
url: `/api/users/${EMPLOYEE_ID}/sensitive`,
payload: { bankName: "新银行" },
payload: { bankAccount: "6222000099995678" },
});
expect(response.statusCode).toBe(403);
@@ -134,7 +132,7 @@ describe("user sensitive field permissions", () => {
const response = await testApp.app.inject({
method: "PATCH",
url: `/api/users/${EMPLOYEE_ID}/sensitive`,
payload: { bankName: "新银行", idNumber: "110101199001011234" },
payload: { bankAccount: "6222000099995678", idNumber: "110101199001011234" },
});
expect(response.statusCode).toBe(403);
@@ -143,6 +141,33 @@ describe("user sensitive field permissions", () => {
await testApp.app.close();
});
it("reveals a full bank account only after a reason and records the view audit", async () => {
const testApp = await buildApp(["employee.bank.read"]);
const missingReason = await testApp.app.inject({
method: "POST",
url: `/api/users/${EMPLOYEE_ID}/sensitive/bank/reveal`,
payload: {},
});
expect(missingReason.statusCode).toBe(400);
expect(testApp.auditCreate).not.toHaveBeenCalled();
const response = await testApp.app.inject({
method: "POST",
url: `/api/users/${EMPLOYEE_ID}/sensitive/bank/reveal`,
payload: { reason: "付款核对" },
});
expect(response.statusCode).toBe(200);
expect(response.json().data).toEqual({ bankAccount: "6222000012345678" });
expect(testApp.auditCreate).toHaveBeenCalledWith({
data: expect.objectContaining({
action: "SYSTEM_USER_BANK_ACCOUNT_REVEALED",
extra: { reason: "付款核对" },
}),
});
await testApp.app.close();
});
it("writes only authorized fields and keeps plaintext out of the audit", async () => {
const testApp = await buildApp(["employee.bank.manage", "employee.identity.manage"]);