文件
hyb-oa/client/src/api/errorMessages.ts
T

548 行
20 KiB
TypeScript

import { formatInvoiceOcrFieldLabels } from "../lib/invoiceOcr";
import { resolveErrorReasonMessage, UNKNOWN_VALIDATION_MESSAGE } from "./errorReasons";
function readStringList(value: unknown): string[] {
if (!Array.isArray(value)) {
return [];
}
return value
.map((item) => (typeof item === "string" ? item.trim() : ""))
.filter((item) => item.length > 0);
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value != null && typeof value === "object" && !Array.isArray(value);
}
function countGuardAmountUnavailableItems(value: unknown): number {
return Array.isArray(value)
? value.filter((item) => (
isRecord(item)
&& typeof item.itemId === "string"
&& item.itemId.length > 0
&& readStringList(item.invoiceIds).length > 0
)).length
: 0;
}
function countGuardAmountExceededItems(value: unknown): number {
return Array.isArray(value)
? value.filter((item) => (
isRecord(item)
&& typeof item.itemId === "string"
&& item.itemId.length > 0
&& typeof item.declaredAmount === "string"
&& item.declaredAmount.length > 0
&& typeof item.invoiceTotal === "string"
&& item.invoiceTotal.length > 0
)).length
: 0;
}
const FEATURE_FLAG_DISABLED_MESSAGES: Record<string, string> = {
portal_home_v1_disabled: "工作台首页当前未启用,请联系管理员检查环境开关与功能开关配置。",
announcements_v1_disabled: "公告模块当前未启用,请联系管理员检查环境开关与功能开关配置。",
presence_board_v1_disabled: "团队状态模块当前未启用,请联系管理员检查环境开关与功能开关配置。",
attendance_v1_disabled: "历史 OA 自建打卡已关闭,请在企业微信打卡。",
attendance_unified_v1_disabled: "企微考勤统一汇总当前未启用,请联系管理员检查环境开关与功能开关配置。",
weekly_report_v1_disabled: "周报模块当前未启用,请联系管理员检查环境开关与功能开关配置。",
employee_lifecycle_v1_disabled: "人员生命周期模块当前未启用,请联系管理员检查环境开关与功能开关配置。",
wecom_hr_master_sync_v1_disabled: "企微 HR 主数据镜像当前未启用,请联系管理员检查环境开关与功能开关配置。",
todo_board_v1_disabled: "个人事项功能当前未启用,请联系管理员检查环境开关与功能开关配置。",
project_domain_v1_disabled: "项目模块当前未启用,请联系管理员检查环境开关与功能开关配置。",
project_linking_v1_disabled: "项目关联功能当前未启用,请联系管理员检查环境开关与功能开关配置。",
project_communication_log_v1_disabled: "项目沟通回填当前未启用,请联系管理员检查环境开关与功能开关配置。",
project_closure_checklist_v1_disabled: "项目收口清单当前未启用,请联系管理员检查环境开关与功能开关配置。",
confidential_doc_refs_v1_disabled: "涉密资料引用当前未启用,请联系管理员检查环境开关与功能开关配置。",
};
function resolveFeatureFlagDisabledMessage(value: unknown): string | null {
if (typeof value !== "string" || !value.endsWith("_disabled")) {
return null;
}
return (
FEATURE_FLAG_DISABLED_MESSAGES[value] ??
"当前功能未启用,请联系管理员检查环境开关与功能开关配置。"
);
}
export function mapApiErrorMessage(
code: string,
details?: Record<string, unknown>,
fallbackMessage?: string,
): string {
if (code === "VALID_001" && details?.reasonCode === "reimbursement.items_invoice_item_guard_failed") {
return mapApiErrorMessage(code, { ...details, reasonCode: undefined, items: "invoice_item_guard_failed" });
}
if (code === "VALID_001" && details?.reasonCode !== undefined) {
if (details.reasonCode === "invoice_pool.ocr_fields_missing") {
const missingFields = readStringList(details.missingFields).filter((field) =>
["invoiceCode", "invoiceNumber", "invoiceDate", "totalAmount", "sellerName"].includes(field));
if (missingFields.length) {
return mapApiErrorMessage(code, { ocr: "missing_required_fields", missingFields });
}
}
if (details.reasonCode === "workflow.remind_cooldown") {
const nextAllowedAt = details.nextAllowedAt;
if (typeof nextAllowedAt === "string"
&& /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/.test(nextAllowedAt)
&& Number.isFinite(new Date(nextAllowedAt).getTime())) {
return mapApiErrorMessage(code, { workflow: "remind_too_frequent", nextAllowedAt });
}
}
if (details.reasonCode === "auth.password_policy_failed") {
const password = readStringList(details.password).filter((rule) => [
"密码长度必须 ≥ 12", "密码必须包含小写字母", "密码必须包含大写字母",
"密码必须包含数字", "密码过于常见/弱",
].includes(rule));
if (password.length) {
return mapApiErrorMessage(code, { password });
}
}
return resolveErrorReasonMessage(details.reasonCode) ?? UNKNOWN_VALIDATION_MESSAGE;
}
if (code === "AUTH_001") {
return "账号或密码错误,请检查后重试。";
}
if (code === "AUTH_002") {
const lockedUntil = typeof details?.lockedUntil === "string" ? details.lockedUntil : null;
if (lockedUntil) {
const until = new Date(lockedUntil);
if (!Number.isNaN(until.getTime())) {
return `账号已锁定至 ${until.toLocaleString()},请稍后重试。`;
}
}
return "账号已锁定,请稍后重试。";
}
if (code === "AUTH_003") {
return "账号已离职/禁用,请联系管理员。";
}
if (code === "AUTH_004") {
return "登录状态失效,请重新登录。";
}
if (code === "AUTH_005") {
return "登录状态已失效,请重新登录。";
}
if (code === "PERM_001") {
return "当前账号无权限执行该操作。";
}
if (code === "PERM_002") {
return "你不是当前任务处理人,无法执行该操作。";
}
if (code === "SYS_404") {
return "目标数据不存在或已被删除。";
}
if (code === "SYS_001" && details?.email === "delivery_not_configured") {
return "邮件验证码暂不可用,请使用密码登录或联系管理员。";
}
if (code === "SYS_001") {
return "服务暂不可用,请稍后重试或联系管理员。";
}
if (code === "SYS_429") {
return "请求过于频繁,请稍后重试。";
}
if (code === "VALID_002") {
return "输入内容长度不足,请补充后再提交。";
}
if (code === "VALID_003") {
return "票据疑似重复,请核对后再上传或联系财务处理。";
}
if (code === "VALID_004") {
return "原始单据已审批通过/付款,当前操作不允许。";
}
if (code === "VALID_005") {
return "票据已绑定报销单,无需重复绑定。";
}
if (code === "VALID_006") {
return "票据识别重试次数已达上限,请稍后人工处理。";
}
if (code === "VALID_007") {
return "票据识别重试过于频繁,请稍后再试。";
}
if (code === "VALID_001") {
if (details?.amount === "must_be_positive") {
return "明细金额必须大于 0。";
}
if (details?.items === "at_least_one_required") {
return "请先添加至少 1 条报销明细。";
}
if (details?.items === "positive_amount_required") {
return "所有报销明细金额都必须大于 0,请修改后再提交。";
}
if (details?.items === "invoice_item_guard_failed") {
const messages: string[] = [];
const rawUnlinkedCount = details.unlinkedInvoiceCount;
const unlinkedCount = typeof rawUnlinkedCount === "number"
&& Number.isInteger(rawUnlinkedCount)
&& rawUnlinkedCount >= 0
? rawUnlinkedCount
: 0;
const missingCount = readStringList(details.missingInvoiceItemIds).length;
const unavailableCount = countGuardAmountUnavailableItems(details.amountUnavailableItems);
const exceededCount = countGuardAmountExceededItems(details.amountExceededItems);
if (unlinkedCount > 0) messages.push(`${unlinkedCount} 张发票未选择报销明细`);
if (missingCount > 0) messages.push(`${missingCount} 条需票明细尚未关联发票`);
if (unavailableCount > 0) messages.push(`${unavailableCount} 条明细的发票金额未识别`);
if (exceededCount > 0) messages.push(`${exceededCount} 条明细的申报金额高于发票合计`);
return messages.length
? `票据检查未通过:${messages.join(";")}。请按下方提示处理。`
: "票据检查未通过,请核对每条明细与发票的关联和金额后重试。";
}
if (details?.invoice === "invalid_unlinked_item_state") {
return "这张发票的关联状态已变化,请刷新后重试。";
}
if (details?.reimbursement === "invalid_status_for_invoice_item_link") {
return "当前报销单状态已变化,不能再关联发票,请刷新页面。";
}
if (details?.linkedItemId === "item_not_in_reimbursement") {
return "所选明细已不存在或不属于本单,请刷新后重新选择。";
}
const passwordErrors = readStringList(details?.password);
if (passwordErrors.length) {
return `新密码不符合要求:${passwordErrors.join(";")}。`;
}
const featureFlagMessage = resolveFeatureFlagDisabledMessage(details?.featureFlag);
if (featureFlagMessage) {
return featureFlagMessage;
}
if (details?.["x-device-fingerprint"] === "missing") {
return "当前设备校验信息缺失,请刷新页面后重试。";
}
if (details?.code === "invalid_or_expired") {
return "验证码无效或已过期,请重新获取。";
}
if (details?.payment === "already_paid_conflict") {
return "该单据已由其他付款动作处理,请刷新后核对付款记录。";
}
if (details?.payment === "source_amount_changed") {
return "源单金额已发生变化,本次付款未执行,请刷新后重新制单。";
}
if (details?.payment === "payment_batch_v1_required") {
return "付款批次已启用,请从付款页面完成制单、复核和执行。";
}
if (details?.entityIds === "contains_active_payment_batch") {
return "所选报销已进入其他有效付款批次,请刷新候选列表。";
}
if (details?.receiptAttachment === "void_required") {
return "银行回单不能直接删除,请填写原因后作废。";
}
if (details?.status === "invalid_pay_status") {
return "当前状态不允许执行付款。";
}
if (details?.review === "maker_checker_conflict") {
return "复核人与批次创建人不能是同一人。";
}
if (details?.review === "batch_review_required") {
return "当前批次尚未完成复核,不能执行。";
}
if (details?.review === "batch_not_reviewable") {
return "当前批次状态不允许继续复核。";
}
if (details?.review === "no_eligible_reviewer") {
return "当前没有可复核人(复核人必须是非制单人且在职),请联系管理员。";
}
if (details?.receiptAttachments === "bank_receipt_required") {
return "请先上传银行回单,再登记付款完成。";
}
if (details?.transfer === "chain_limit_exceeded") {
return "当前任务转审次数已达上限。";
}
if (details?.transfer === "same_assignee_not_allowed") {
return "不能将任务转审给自己。";
}
if (details?.workflow === "self_approval_not_allowed") {
return "自己的申请不能审批,请等待下一审批人处理。";
}
if (details?.workflow === "self_remind_not_allowed") {
return "不能催办分配给自己的审批任务。";
}
if (details?.workflow === "remind_owner_required") {
return "只有申请人或提交人可以催办该审批。";
}
if (details?.workflow === "remind_too_frequent") {
const nextAllowedAt = typeof details?.nextAllowedAt === "string" ? details.nextAllowedAt : null;
return nextAllowedAt
? `24 小时内已催办过,请在 ${nextAllowedAt.slice(0, 19).replace("T", " ")} 后再试。`
: "24 小时内已催办过。";
}
if (details?.workflow === "business_approval_role_required") {
return "系统管理员不自动获得业务审批权,请配置正式审批角色后处理。";
}
if (details?.workflow === "no_actionable_approver") {
return "当前审批规则未找到审批人,请联系管理员修正规则。";
}
if (details?.toApproverId === "target_not_approver") {
return "目标人员不具备审批权限。";
}
if (details?.toApproverId === "invalid_target") {
return "目标审批人不存在或已停用。";
}
if (details?.task === "already_processed") {
return "该任务已被处理,请刷新列表。";
}
if (details?.invoices === "bound_invoice_required") {
return "提交前请先绑定至少 1 张发票。";
}
if (details?.invoice === "not_bound") {
return "该票据未绑定报销单,无法解绑。";
}
if (details?.reimbursement === "invalid_status_for_unbind") {
return "仅草稿/驳回状态的报销单允许解绑票据。";
}
if (details?.ocr === "disabled") {
return "当前环境未开启票据识别,请联系管理员处理。";
}
if (details?.ocr === "missing_required_fields") {
const labels = formatInvoiceOcrFieldLabels(readStringList(details?.missingFields));
if (labels.length) {
return `票据仍缺少关键字段:${labels.join("、")},补齐后才能标记为可用。`;
}
return "票据仍缺少关键字段,补齐后才能标记为可用。";
}
if (details?.verification === "disabled") {
return "当前环境未开启发票验真。";
}
if (details?.code === "duplicate") {
return "编码已存在,请更换后重试。";
}
if (details?.legalEntityId === "not_found") {
return "所选公司不存在。";
}
if (details?.legalEntityId === "archived") {
return "所选公司已归档,无法继续使用。";
}
if (details?.ownerEmployeeId === "not_found") {
return "负责人不存在或已停用。";
}
if (details?.parentId === "not_found") {
return "上级成本中心不存在。";
}
if (details?.parentId === "archived") {
return "上级成本中心已归档,无法继续使用。";
}
if (details?.parentId === "self_not_allowed") {
return "上级成本中心不能选择自身。";
}
if (details?.parentId === "cycle_not_allowed") {
return "上级成本中心不能形成循环层级。";
}
if (details?.parentId === "cross_legal_entity_not_allowed") {
return "上级成本中心必须与当前公司一致。";
}
if (details?.legalEntity === "has_cost_centers") {
return "当前公司下仍存在未归档的成本中心,无法归档。";
}
if (details?.costCenter === "has_children") {
return "当前成本中心存在下级节点,无法归档。";
}
if (details?.holidayCalendar === "bound_as_default") {
return `当前日历仍被公司 ${details?.legalEntityCode ?? ""} 作为默认日历引用,无法归档。`;
}
if (details?.date === "duplicate") {
return "同一日历内不允许存在重复日期。";
}
if (details?.verification === "already_pending") {
return "已有验真任务正在处理中,请稍后查看结果。";
}
if (details?.verification === "too_frequent") {
return "验真触发过于频繁,请稍后重试。";
}
if (details?.verification === "attempts_exceeded") {
return "验真重试次数已达上限,请联系财务人工核验。";
}
if (details?.budget === "exceeded_block_mode") {
return "当前报销金额超出预算,系统已阻断提交。";
}
if (details?.budget === "missing_budget_pool") {
return "未匹配到预算池;当前强预算校验已阻断提交,请联系管理员补充预算配置。";
}
if (details?.leave === "conflict_range") {
return "当前时间段与已有请假记录冲突,请调整后再提交。";
}
if (details?.leave === "insufficient_balance") {
return "当前假种可用额度不足,无法提交。";
}
if (details?.leave === "invalid_range") {
return "请假开始/结束日期与半天时段不合法。";
}
if (details?.leave === "non_working_range") {
return "所选时间段均为非工作日,无需请假。";
}
if (details?.leave === "missing_reserve_ledger") {
return "请假冻结账本状态异常,请联系管理员处理。";
}
if (details?.attachment === "security_validation_failed") {
if (details?.reason === "quarantined") {
return "附件已隔离,无法下载,请联系管理员。";
}
if (details?.reason === "missing_object") {
return "附件文件不存在或已缺失,请联系管理员。";
}
return "附件未通过安全校验,请重新上传允许格式或联系管理员。";
}
if (details?.file === "empty_file") {
return "附件内容为空,请重新选择文件。";
}
if (details?.file === "size_limit" || details?.file === "file_too_large") {
return "附件超过大小限制,请压缩后重新上传。";
}
if (details?.file === "too_many_files") {
const maxFiles = details?.maxFiles;
return typeof maxFiles === "number" && Number.isSafeInteger(maxFiles) && maxFiles > 0
? `单次最多上传 ${maxFiles} 个附件,请分批上传。`
: "一次选择的附件过多,请减少数量后分批上传。";
}
if (details?.file === "dangerous_filename") {
return "附件名称不安全,请重命名后重新上传。";
}
if (details?.file === "unsupported_type") {
return "仅支持 PDF、JPG、PNG、WEBP 文件,请重新选择。";
}
if (details?.file === "type_mismatch") {
return "附件扩展名、声明类型与实际内容不一致,请重新选择原始文件或转换为支持格式。";
}
if (details?.file === "corrupt_content") {
return "附件已损坏或内容不完整,请重新导出后上传。";
}
if (details?.file === "pixel_limit") {
return "图片尺寸过大,请压缩后重新上传。";
}
if (details?.file === "multiple_pages") {
return "暂不支持多页或动画图片,请转换为 PDF 或单页安全图片。";
}
if (details?.file === "only_single_file_supported") {
return "一次只能上传一个附件,请重新选择。";
}
if (details?.file === "required") {
return "当前假种要求上传附件后才能提交。";
}
if (details?.file === "pdf_invoice_required") {
return "请上传 PDF 发票文件。";
}
if (details?.file === "unsupported_mime_type") {
return "附件类型不被当前假种接受,请重新选择。";
}
if (details?.withdraw === "no_pending_instance") {
return "当前请假单不存在可撤回的审批实例。";
}
if (details?.withdraw === "already_processed_by_approver") {
return "审批人已处理该请假单,当前不能撤回。";
}
if (details?.leaveTypeId === "missing_active_policy") {
return "当前假种未配置可用策略,请联系 HR。";
}
if (details?.integration === "erp_sync_disabled") {
return "该同步功能未开启。";
}
if (details?.integration === "travel_sync_disabled") {
return "差旅同步功能未开启。";
}
if (details?.secret === "field_encryption_key_missing") {
return "当前环境未配置 FIELD_ENCRYPTION_KEY,无法保存或更新 Webhook Secret。";
}
}
return code === "VALID_001" ? UNKNOWN_VALIDATION_MESSAGE : fallbackMessage || "请求失败,请稍后重试。";
}