R71-A4 澄清报销缺票提示并修复发票上传挂起
这个提交包含在:
+49
-44
@@ -203,13 +203,16 @@ async function invoicePoolRoutes(fastify) {
|
||||
preHandler: fastify.requirePermission("invoice.upload"),
|
||||
},
|
||||
async (request, reply) => {
|
||||
let filePart = null;
|
||||
let fileBuffer = null;
|
||||
let fileName = null;
|
||||
let fileMimeType = null;
|
||||
let fileSize = 0;
|
||||
const fields = {};
|
||||
|
||||
try {
|
||||
for await (const part of request.parts()) {
|
||||
if (part.type === "file") {
|
||||
if (filePart) {
|
||||
if (fileBuffer) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
@@ -217,10 +220,46 @@ async function invoicePoolRoutes(fastify) {
|
||||
details: { file: "only_single_file_supported" },
|
||||
});
|
||||
}
|
||||
filePart = part;
|
||||
} else {
|
||||
fields[part.fieldname] = part.value;
|
||||
fileName = part.filename ?? "upload";
|
||||
fileMimeType = part.mimetype;
|
||||
|
||||
if (!MIME_ALLOWLIST.has(fileMimeType)) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode: 400,
|
||||
details: { file: "unsupported_mime_type" },
|
||||
});
|
||||
}
|
||||
|
||||
const chunks = [];
|
||||
for await (const chunk of part.file) {
|
||||
fileSize += chunk.length;
|
||||
if (fileSize > fastify.config.maxUploadSizeBytes) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode: 400,
|
||||
details: { file: "file_too_large" },
|
||||
});
|
||||
}
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
if (fileSize === 0) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode: 400,
|
||||
details: { file: "empty_file" },
|
||||
});
|
||||
}
|
||||
|
||||
fileBuffer = Buffer.concat(chunks);
|
||||
continue;
|
||||
}
|
||||
|
||||
fields[part.fieldname] = part.value;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error?.code === "FST_REQ_FILE_TOO_LARGE") {
|
||||
@@ -234,7 +273,7 @@ async function invoicePoolRoutes(fastify) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!filePart) {
|
||||
if (!fileBuffer) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
@@ -243,15 +282,6 @@ async function invoicePoolRoutes(fastify) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!MIME_ALLOWLIST.has(filePart.mimetype)) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode: 400,
|
||||
details: { file: "unsupported_mime_type" },
|
||||
});
|
||||
}
|
||||
|
||||
const ownerEmployeeId =
|
||||
typeof fields.owner_employee_id === "string" && fields.owner_employee_id.trim()
|
||||
? fields.owner_employee_id.trim()
|
||||
@@ -315,40 +345,15 @@ async function invoicePoolRoutes(fastify) {
|
||||
});
|
||||
}
|
||||
|
||||
const chunks = [];
|
||||
let fileSize = 0;
|
||||
for await (const chunk of filePart.file) {
|
||||
fileSize += chunk.length;
|
||||
if (fileSize > fastify.config.maxUploadSizeBytes) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode: 400,
|
||||
details: { file: "file_too_large" },
|
||||
});
|
||||
}
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
if (fileSize === 0) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode: 400,
|
||||
details: { file: "empty_file" },
|
||||
});
|
||||
}
|
||||
|
||||
const fileBuffer = Buffer.concat(chunks);
|
||||
const fileSha256 = sha256Hex(fileBuffer);
|
||||
const datePrefix = compactDateString(fastify.config.appTimezone);
|
||||
const extension = path.extname(filePart.filename ?? "").toLowerCase();
|
||||
const extension = path.extname(fileName ?? "").toLowerCase();
|
||||
const objectKey = path.posix.join(datePrefix, `${crypto.randomUUID()}${extension}`);
|
||||
const storagePath = await writeBuffer({
|
||||
config: fastify.config,
|
||||
key: objectKey,
|
||||
buffer: fileBuffer,
|
||||
contentType: filePart.mimetype,
|
||||
contentType: fileMimeType,
|
||||
});
|
||||
|
||||
const status = isOcrEnabledForType(fastify.config, ocrType) ? "pending_ocr" : "available";
|
||||
@@ -360,8 +365,8 @@ async function invoicePoolRoutes(fastify) {
|
||||
owner_employee_id: ownerEmployeeId,
|
||||
uploaded_by_employee_id: request.employee.id,
|
||||
storage_path: storagePath,
|
||||
original_name: filePart.filename ?? "upload",
|
||||
mime_type: filePart.mimetype,
|
||||
original_name: fileName ?? "upload",
|
||||
mime_type: fileMimeType,
|
||||
file_size: fileSize,
|
||||
file_sha256: fileSha256,
|
||||
ocr_type: ocrType,
|
||||
|
||||
在新工单中引用
屏蔽一个用户