R62 A1-A6:2026年中国法定假期日历

这个提交包含在:
2026-04-12 21:31:24 +08:00
父节点 97b679033e
当前提交 f8d941ec64
共修改 10 个文件,包含 638 行新增和 31 行删除
+72 -6
查看文件
@@ -8,7 +8,7 @@ const { ensureCutoverAllowed } = require("../services/cutover");
const {
DEFAULT_ATTACHMENT_MIME_TYPES,
calculateRequestedDays,
calculateRequestedDaysByWorkingCalendar,
calculateRequestedDaysPreviewByWorkingCalendar,
createLeaveLedgerEntry,
formatDateOnly,
leaveRangesOverlap,
@@ -37,6 +37,13 @@ const createLeaveSchema = z
})
.strict();
const workingDaysPreviewQuerySchema = z.object({
startDate: DATE_ONLY_SCHEMA,
endDate: DATE_ONLY_SCHEMA,
startSession: SESSION_SCHEMA,
endSession: SESSION_SCHEMA,
});
const leaveListQuerySchema = z.object({
status: STATUS_SCHEMA.optional(),
scope: z.enum(["own", "all"]).optional(),
@@ -189,9 +196,49 @@ function parseCreateBody(body) {
};
}
async function resolveRequestedDaysForCreate(fastify, employee, body) {
function parseWorkingDaysPreviewQuery(query) {
const parsed = workingDaysPreviewQuerySchema.safeParse(query ?? {});
if (!parsed.success) {
throw buildValidationError(parsed.error.flatten());
}
const startDate = new Date(`${parsed.data.startDate}T00:00:00.000Z`);
const endDate = new Date(`${parsed.data.endDate}T00:00:00.000Z`);
if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) {
throw buildValidationError({ date: "invalid" });
}
const requestedDays = calculateRequestedDays({
startDate,
endDate,
startSession: parsed.data.startSession,
endSession: parsed.data.endSession,
});
return {
startDate,
endDate,
startSession: parsed.data.startSession,
endSession: parsed.data.endSession,
requestedDays,
};
}
function buildNaturalWorkingDaysPreview(body) {
return {
requestedDays: body.requestedDays,
naturalRequestedDays: body.requestedDays,
holidayDayCount: 0,
weekendDayCount: 0,
adjustedWorkdayCount: 0,
calendarApplied: false,
};
}
async function resolveWorkingDaysPreview(fastify, employee, body) {
if (!employee?.legal_entity_id) {
return body.requestedDays;
return buildNaturalWorkingDaysPreview(body);
}
const featureFlag = await evaluateFeatureFlag({
@@ -203,7 +250,7 @@ async function resolveRequestedDaysForCreate(fastify, employee, body) {
});
if (!featureFlag.enabled) {
return body.requestedDays;
return buildNaturalWorkingDaysPreview(body);
}
const legalEntity = await fastify.prisma.legalEntity.findUnique({
@@ -232,10 +279,10 @@ async function resolveRequestedDaysForCreate(fastify, employee, body) {
const calendar = legalEntity?.default_holiday_calendar;
if (!calendar || calendar.archived_at) {
return body.requestedDays;
return buildNaturalWorkingDaysPreview(body);
}
return calculateRequestedDaysByWorkingCalendar({
return calculateRequestedDaysPreviewByWorkingCalendar({
startDate: body.startDate,
endDate: body.endDate,
startSession: body.startSession,
@@ -244,6 +291,11 @@ async function resolveRequestedDaysForCreate(fastify, employee, body) {
});
}
async function resolveRequestedDaysForCreate(fastify, employee, body) {
const preview = await resolveWorkingDaysPreview(fastify, employee, body);
return preview.requestedDays;
}
async function collectCreatePayload(request) {
if (typeof request.isMultipart === "function" && request.isMultipart()) {
const fields = {};
@@ -519,6 +571,20 @@ async function leaveRoutes(fastify) {
},
);
fastify.get(
"/leave-requests/working-days",
{
schema: { tags: ["Leave"], summary: "Preview Leave Working Days" },
preHandler: fastify.authenticate,
},
async (request, reply) => {
const query = parseWorkingDaysPreviewQuery(request.query);
const preview = await resolveWorkingDaysPreview(fastify, request.employee, query);
return sendData(reply, request, preview);
},
);
fastify.post(
"/leave-requests",
{