文件
hyb-oa/server/src/routes/configRead.js
T

157 行
4.2 KiB
JavaScript

const { z } = require("zod");
const { AppError } = require("../utils/AppError");
const { sendData } = require("../utils/reply");
const {
normalizeDocumentType,
resolveDocumentSequenceConfig,
} = require("../services/documentSequence");
const ENABLED_QUERY_SCHEMA = z.object({
enabled: z.enum(["true", "false", "1", "0"]).optional(),
});
const DOCUMENT_TYPE_PARAM_SCHEMA = z.object({
documentType: z.string().trim().min(1).max(64).regex(/^[a-z_]+$/),
});
function parseEnabledQuery(query) {
const parsed = ENABLED_QUERY_SCHEMA.safeParse(query ?? {});
if (!parsed.success) {
throw new AppError({
code: "VALID_001",
message: "参数校验失败",
statusCode: 400,
details: parsed.error.flatten(),
});
}
const enabledRaw = parsed.data.enabled;
const enabled =
enabledRaw == null
? null
: ["true", "1"].includes(enabledRaw)
? true
: ["false", "0"].includes(enabledRaw)
? false
: null;
return { enabled };
}
function parseDocumentTypeParams(params) {
const parsed = DOCUMENT_TYPE_PARAM_SCHEMA.safeParse(params ?? {});
if (!parsed.success) {
throw new AppError({
code: "VALID_001",
message: "参数校验失败",
statusCode: 400,
details: parsed.error.flatten(),
});
}
return normalizeDocumentType(parsed.data.documentType);
}
function mapApprovalRule(item) {
return {
id: item.id,
name: item.name,
minAmount: item.min_amount?.toString?.() ?? String(item.min_amount ?? "0"),
maxAmount: item.max_amount == null ? null : item.max_amount?.toString?.() ?? String(item.max_amount),
level1Role: item.level1_role,
level2Role: item.level2_role ?? null,
level3Role: item.level3_role ?? null,
enabled: item.enabled,
priority: item.priority,
createdAt: item.created_at?.toISOString?.() ?? null,
updatedAt: item.updated_at?.toISOString?.() ?? null,
};
}
function mapDocumentSequence(config) {
return {
exists: Boolean(config.exists),
source: config.source,
documentType: config.documentType,
documentCategoryCode: config.documentCategoryCode ?? null,
prefix: config.prefix,
dateFormat: config.dateFormat,
separator: config.separator,
numberLength: config.numberLength,
resetPeriod: config.resetPeriod,
currentNumber: config.currentNumber,
lastResetDate: config.lastResetDate ?? null,
updatedAt: config.updatedAt ?? null,
updatedBy: config.updatedBy ?? null,
};
}
async function loadDocumentSequenceConfig(fastify, documentType) {
return resolveDocumentSequenceConfig(fastify.prisma, {
documentType,
});
}
async function configReadRoutes(fastify) {
fastify.get(
"/approval-rules",
{
preHandler: fastify.authenticate,
},
async (request, reply) => {
const query = parseEnabledQuery(request.query);
const items = await fastify.prisma.approvalRule.findMany({
where: {
...(query.enabled == null ? {} : { enabled: query.enabled }),
},
orderBy: [{ priority: "asc" }, { min_amount: "asc" }],
select: {
id: true,
name: true,
min_amount: true,
max_amount: true,
level1_role: true,
level2_role: true,
level3_role: true,
enabled: true,
priority: true,
created_at: true,
updated_at: true,
},
});
return sendData(reply, request, {
enabled: query.enabled,
items: items.map(mapApprovalRule),
});
},
);
fastify.get(
"/document-sequences/reimbursement",
{
preHandler: fastify.requirePermission("system.settings"),
},
async (request, reply) => {
const config = await loadDocumentSequenceConfig(fastify, "reimbursement");
return sendData(reply, request, mapDocumentSequence(config));
},
);
fastify.get(
"/document-sequences/:documentType",
{
preHandler: fastify.requirePermission("system.settings"),
},
async (request, reply) => {
const documentType = parseDocumentTypeParams(request.params);
const config = await loadDocumentSequenceConfig(fastify, documentType);
return sendData(reply, request, mapDocumentSequence(config));
},
);
}
module.exports = configReadRoutes;