P25L-1~P25L-5 实现预算控制与AP最小可用运营
合同 CSV 已回填 reviewer/QA 结果;运行时回归受当前环境缺少 node/npm 阻塞。
这个提交包含在:
@@ -0,0 +1,53 @@
|
||||
const { z } = require("zod");
|
||||
|
||||
const { AppError } = require("../utils/AppError");
|
||||
const { sendData } = require("../utils/reply");
|
||||
const {
|
||||
getPrecheckPermissionCode,
|
||||
resolveEntityPrecheck,
|
||||
} = require("../services/feeControl");
|
||||
|
||||
const PRECHECK_BODY_SCHEMA = z.object({
|
||||
entityType: z.enum(["reimbursement", "purchase_request", "travel_request"]),
|
||||
entityId: z.string().uuid(),
|
||||
}).strict();
|
||||
|
||||
function buildValidationError(details, statusCode = 400) {
|
||||
return new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode,
|
||||
details,
|
||||
});
|
||||
}
|
||||
|
||||
function parseBody(body) {
|
||||
const parsed = PRECHECK_BODY_SCHEMA.safeParse(body ?? {});
|
||||
if (!parsed.success) {
|
||||
throw buildValidationError(parsed.error.flatten());
|
||||
}
|
||||
return parsed.data;
|
||||
}
|
||||
|
||||
async function feeControlRoutes(fastify) {
|
||||
fastify.post("/fee-control/precheck", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
const body = parseBody(request.body);
|
||||
const permissionCode = getPrecheckPermissionCode(body.entityType);
|
||||
if (!permissionCode || !fastify.hasPermission(request.employee, permissionCode)) {
|
||||
throw new AppError({ code: "PERM_001", message: "无权限", statusCode: 403 });
|
||||
}
|
||||
|
||||
const precheck = await resolveEntityPrecheck({
|
||||
prisma: fastify.prisma,
|
||||
config: fastify.config,
|
||||
logger: fastify.log,
|
||||
entityType: body.entityType,
|
||||
entityId: body.entityId,
|
||||
actorDepartmentId: request.employee.department_id ?? null,
|
||||
});
|
||||
|
||||
return sendData(reply, request, precheck);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = feeControlRoutes;
|
||||
在新工单中引用
屏蔽一个用户