feat(G2-1,G2-2,G2-3): 扩展预算编号与工作流跨域桥接
这个提交包含在:
+55
-46
@@ -2,11 +2,19 @@ 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) {
|
||||
@@ -31,6 +39,20 @@ function parseEnabledQuery(query) {
|
||||
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,
|
||||
@@ -47,39 +69,30 @@ function mapApprovalRule(item) {
|
||||
};
|
||||
}
|
||||
|
||||
function mapDocumentSequence(item, exists) {
|
||||
const fallback = {
|
||||
documentType: "reimbursement",
|
||||
prefix: "BX",
|
||||
dateFormat: "YYYYMMDD",
|
||||
separator: "-",
|
||||
numberLength: 4,
|
||||
resetPeriod: "none",
|
||||
currentNumber: 0,
|
||||
lastResetDate: null,
|
||||
updatedAt: null,
|
||||
updatedBy: null,
|
||||
};
|
||||
|
||||
if (!item) {
|
||||
return { exists, ...fallback };
|
||||
}
|
||||
|
||||
function mapDocumentSequence(config) {
|
||||
return {
|
||||
exists,
|
||||
documentType: item.document_type,
|
||||
prefix: item.prefix,
|
||||
dateFormat: item.date_format,
|
||||
separator: item.separator,
|
||||
numberLength: item.number_length,
|
||||
resetPeriod: item.reset_period,
|
||||
currentNumber: item.current_number,
|
||||
lastResetDate: item.last_reset_date?.toISOString?.()?.slice(0, 10) ?? null,
|
||||
updatedAt: item.updated_at?.toISOString?.() ?? null,
|
||||
updatedBy: item.updated_by ?? null,
|
||||
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",
|
||||
@@ -122,26 +135,22 @@ async function configReadRoutes(fastify) {
|
||||
preHandler: fastify.requirePermission("system.settings"),
|
||||
},
|
||||
async (request, reply) => {
|
||||
const sequence = await fastify.prisma.documentSequence.findUnique({
|
||||
where: { document_type: "reimbursement" },
|
||||
select: {
|
||||
document_type: true,
|
||||
prefix: true,
|
||||
date_format: true,
|
||||
separator: true,
|
||||
number_length: true,
|
||||
reset_period: true,
|
||||
current_number: true,
|
||||
last_reset_date: true,
|
||||
updated_at: true,
|
||||
updated_by: true,
|
||||
},
|
||||
});
|
||||
const config = await loadDocumentSequenceConfig(fastify, "reimbursement");
|
||||
return sendData(reply, request, mapDocumentSequence(config));
|
||||
},
|
||||
);
|
||||
|
||||
return sendData(reply, request, mapDocumentSequence(sequence, Boolean(sequence)));
|
||||
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;
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户