feat: 完成P13治理收尾并创建P14执行合同(Issue: P13-1,P13-2,P13-3,P13-4,P13-5,P13-6,P13-7,P13-8,P14-0)

这个提交包含在:
2026-02-13 00:25:04 +08:00
父节点 3b221a2202
当前提交 1412fead5d
共修改 30 个文件,包含 3988 行新增和 105 行删除
+63 -1
查看文件
@@ -3,16 +3,74 @@ const { z } = require("zod");
const { AppError } = require("../utils/AppError");
const { sendData } = require("../utils/reply");
const { buildAuditLogData } = require("../utils/audit");
const {
buildDataEnvelopeSchema,
buildErrorEnvelopeSchema,
} = require("../utils/openapi");
const { enqueueIntegrationEvent } = require("../services/integrations/outbox");
const { buildErpPayload } = require("../services/integrations/erp");
const { FEATURE_FLAG_KEYS, evaluateFeatureFlag } = require("../services/featureFlags");
const { runIntegrationOutboxOnce } = require("../jobs/integrationOutboxJob");
const ERP_SYNC_RESPONSE_SCHEMA = {
type: "object",
required: ["outboxId", "status", "duplicated"],
properties: {
outboxId: { type: "string", format: "uuid" },
status: { type: "string" },
duplicated: { type: "boolean" },
},
};
async function integrationsRoutes(fastify) {
fastify.post(
"/integrations/erp/sync/:reimbursementId",
{
preHandler: fastify.requirePermission("integration.manage"),
schema: {
tags: ["integrations"],
summary: "手动触发 ERP 同步",
security: [{ bearerAuth: [] }],
params: {
type: "object",
required: ["reimbursementId"],
properties: {
reimbursementId: { type: "string", format: "uuid" },
},
},
querystring: {
type: "object",
properties: {
runNow: {
anyOf: [{ type: "string", enum: ["1", "true"] }, { type: "boolean" }],
},
},
additionalProperties: true,
},
response: {
200: buildDataEnvelopeSchema(ERP_SYNC_RESPONSE_SCHEMA, "ERP 同步任务已入队"),
400: buildErrorEnvelopeSchema({
description: "参数校验失败",
codeExample: "VALID_001",
messageExample: "参数校验失败",
}),
404: buildErrorEnvelopeSchema({
description: "报销单不存在",
codeExample: "SYS_404",
messageExample: "Not Found",
}),
409: buildErrorEnvelopeSchema({
description: "状态不满足或开关阻断",
codeExample: "VALID_001",
messageExample: "参数校验失败",
detailsExample: {
integration: "feature_flag_blocked",
featureFlag: "erp_sync",
reason: "department_not_allowed",
},
}),
},
},
},
async (request, reply) => {
const parsedId = z.string().uuid().safeParse(request.params?.reimbursementId);
@@ -106,7 +164,11 @@ async function integrationsRoutes(fastify) {
}),
});
const runNow = request.query?.runNow === "1" || request.query?.runNow === 1 || request.query?.runNow === true || request.query?.runNow === "true";
const runNow =
request.query?.runNow === "1" ||
request.query?.runNow === 1 ||
request.query?.runNow === true ||
request.query?.runNow === "true";
if (runNow) {
await runIntegrationOutboxOnce(fastify.prisma, fastify.config, fastify.log);
}