WARCH1 实现企微审批最小历史留存
这个提交包含在:
@@ -10,7 +10,7 @@ const TOKEN = "callback-token";
|
||||
const AES_KEY = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
|
||||
const CORP_ID = "ww-callback-corp";
|
||||
|
||||
async function buildApp({ config = {}, evidenceStore } = {}) {
|
||||
async function buildApp({ config = {}, evidenceStore, prisma } = {}) {
|
||||
const app = Fastify();
|
||||
app.decorate("config", {
|
||||
wecomCorpId: CORP_ID,
|
||||
@@ -18,6 +18,9 @@ async function buildApp({ config = {}, evidenceStore } = {}) {
|
||||
wecomCallbackEncodingAesKey: AES_KEY,
|
||||
...config,
|
||||
});
|
||||
if (prisma) {
|
||||
app.decorate("prisma", prisma);
|
||||
}
|
||||
await app.register(wecomApprovalCallbackRoutes, { prefix: "/api", evidenceStore });
|
||||
return app;
|
||||
}
|
||||
@@ -106,6 +109,61 @@ describe("wecomApprovalCallback routes", () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("uses the default DB evidence store when prisma is decorated", async () => {
|
||||
const prisma = buildArchiveMockPrisma();
|
||||
const app = await buildApp({ prisma });
|
||||
const sample = buildEncryptedApprovalEventSample({
|
||||
token: TOKEN,
|
||||
encodingAesKey: AES_KEY,
|
||||
receiveId: CORP_ID,
|
||||
timestamp: "1770000000",
|
||||
nonce: "nonce",
|
||||
spNo: "SPFULLIDENTIFIERSHOULDNOTLEAK",
|
||||
templateId: "TPLFULLIDENTIFIERSHOULDNOTLEAK",
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
method: "POST",
|
||||
url: `/api/wecom/approval/callback?${urlQuery(sample)}`,
|
||||
headers: { "content-type": "text/xml" },
|
||||
payload: sample.encryptedXml,
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(prisma.wecomApprovalCallbackEvent.upsert).toHaveBeenCalledTimes(1);
|
||||
expect(prisma.wecomApprovalArchive.upsert.mock.calls[0][0].create.sp_no).toBe("SPFULLIDENTIFIERSHOULDNOTLEAK");
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("returns non-200 when default DB evidence persistence fails so WeCom can retry", async () => {
|
||||
const prisma = {
|
||||
$transaction: vi.fn(async () => {
|
||||
throw new Error("db unavailable");
|
||||
}),
|
||||
};
|
||||
const app = await buildApp({ prisma });
|
||||
const sample = buildEncryptedApprovalEventSample({
|
||||
token: TOKEN,
|
||||
encodingAesKey: AES_KEY,
|
||||
receiveId: CORP_ID,
|
||||
timestamp: "1770000000",
|
||||
nonce: "nonce",
|
||||
spNo: "SPFULLIDENTIFIERSHOULDNOTLEAK",
|
||||
templateId: "TPLFULLIDENTIFIERSHOULDNOTLEAK",
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
method: "POST",
|
||||
url: `/api/wecom/approval/callback?${urlQuery(sample)}`,
|
||||
headers: { "content-type": "text/xml" },
|
||||
payload: sample.encryptedXml,
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(500);
|
||||
expect(response.body).toBe("callback error");
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("reports missing callback config without treating it as pass", async () => {
|
||||
const app = await buildApp({ config: { wecomCallbackToken: "" } });
|
||||
const response = await app.inject({
|
||||
@@ -118,3 +176,31 @@ describe("wecomApprovalCallback routes", () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
function buildArchiveMockPrisma() {
|
||||
const prisma = {
|
||||
wecomIdentityBinding: {
|
||||
findUnique: vi.fn(async () => null),
|
||||
},
|
||||
wecomApprovalArchive: {
|
||||
findUnique: vi.fn(async () => null),
|
||||
upsert: vi.fn(async (args) => ({ id: "archive-1", ...args.create })),
|
||||
},
|
||||
wecomApprovalFieldSummary: {
|
||||
deleteMany: vi.fn(async () => ({ count: 0 })),
|
||||
createMany: vi.fn(async () => ({ count: 0 })),
|
||||
},
|
||||
wecomApprovalParticipant: {
|
||||
deleteMany: vi.fn(async () => ({ count: 0 })),
|
||||
createMany: vi.fn(async () => ({ count: 0 })),
|
||||
},
|
||||
wecomApprovalAction: {
|
||||
upsert: vi.fn(async () => ({ id: "action-1" })),
|
||||
},
|
||||
wecomApprovalCallbackEvent: {
|
||||
upsert: vi.fn(async () => ({ id: "event-1", retry_count: 0 })),
|
||||
},
|
||||
};
|
||||
prisma.$transaction = vi.fn(async (callback) => callback(prisma));
|
||||
return prisma;
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户