feat(P19AI1-1,P19AI1-2): 实现AI建议引擎与权限门禁后端

这个提交包含在:
2026-03-10 14:20:59 +08:00
父节点 fdf3c43244
当前提交 6f534e9a3c
共修改 16 个文件,包含 2158 行新增和 5 行删除
+115
查看文件
@@ -0,0 +1,115 @@
const { z } = require("zod");
const { AppError } = require("../utils/AppError");
const { sendData } = require("../utils/reply");
const {
getAiStatus,
generateReimbursementSuggestion,
generatePurchaseSuggestion,
generateAssetSuggestion,
} = require("../services/ai/suggestions");
const UUID_SCHEMA = z.string().uuid();
function parseUuid(value, fieldName) {
const parsed = UUID_SCHEMA.safeParse(value);
if (!parsed.success) {
throw new AppError({
code: "VALID_001",
message: "参数校验失败",
statusCode: 400,
details: { [fieldName]: "invalid_uuid" },
});
}
return parsed.data;
}
async function aiRoutes(fastify) {
fastify.get(
"/ai/status",
{
preHandler: fastify.authenticate,
schema: {
tags: ["ai"],
summary: "获取 AI 建议工作台状态",
security: [{ bearerAuth: [] }],
},
},
async (request, reply) => {
const status = await getAiStatus(fastify.prisma, fastify.config, request.employee);
return sendData(reply, request, status);
},
);
fastify.post(
"/ai/suggestions/reimbursements/:reimbursementId",
{
preHandler: fastify.authenticate,
schema: {
tags: ["ai"],
summary: "生成报销详情 AI 建议",
security: [{ bearerAuth: [] }],
},
},
async (request, reply) => {
const reimbursementId = parseUuid(request.params?.reimbursementId, "reimbursementId");
const result = await generateReimbursementSuggestion(
fastify.prisma,
fastify.config,
request,
reimbursementId,
);
return sendData(reply, request, result);
},
);
fastify.post(
"/ai/suggestions/purchase-requests/:purchaseRequestId",
{
preHandler: fastify.authenticate,
schema: {
tags: ["ai"],
summary: "生成采购详情 AI 建议",
security: [{ bearerAuth: [] }],
},
},
async (request, reply) => {
const purchaseRequestId = parseUuid(request.params?.purchaseRequestId, "purchaseRequestId");
const result = await generatePurchaseSuggestion(
fastify.prisma,
fastify.config,
request,
purchaseRequestId,
);
return sendData(reply, request, result);
},
);
fastify.post(
"/ai/suggestions/assets/:assetId",
{
preHandler: fastify.authenticate,
schema: {
tags: ["ai"],
summary: "生成资产详情 AI 建议",
security: [{ bearerAuth: [] }],
},
},
async (request, reply) => {
const assetId = parseUuid(request.params?.assetId, "assetId");
const result = await generateAssetSuggestion(
fastify.prisma,
fastify.config,
request,
assetId,
);
return sendData(reply, request, result);
},
);
}
module.exports = aiRoutes;
module.exports.__testables = {
parseUuid,
};