import { ElMessage } from 'element-plus' import { getAppConfig } from '@/utils/appConfig' import { DISK_RETURN_FAIL_REASONS } from '@/constants/diskReturn' import type { BatchMemberPerson, HandleAlertPayload, OpLog, Person, VillageBatch, VillageMonthlyBatch, } from '@/types/biz' import { buildDashboardByRole } from '@/utils/buildDashboard' import * as real from './sjnmtybt/realBiz' import { mockAccounts, mockAlerts, mockApprovals, mockDeaths, mockDisks, mockDiskFailPeople, mockLogs, mockMonthlyBatches, mockPeople, mockSpecials, mockStandards, mockVillageBatches, mockAdjustReissueRoster, nowStr, getVillagesByTown, getStandardByTown, } from './mock/data' /** 是否走 mock:改 public/config/config.js 的 useMock 即可切换 */ function useMock() { return getAppConfig().useMock } function delay(data: T, ms = 200): Promise { return new Promise((resolve) => setTimeout(() => resolve(data), ms)) } function mockPaged(list: T[], pageNum = 1, pageSize = 10) { const start = (pageNum - 1) * pageSize return delay({ list: list.slice(start, start + pageSize), total: list.length, pageNum, pageSize, }) } /* ========== 工作台 ========== */ export function fetchDashboard(params: { roleKey: string currentMonth: string }) { if (useMock()) { const role = getAppConfig().roles.find((r) => r.key === params.roleKey) || getAppConfig().roles[0] return delay(buildDashboardByRole(role, params.currentMonth)) } return real.fetchDashboard(params) } /** 工作台待办已读 */ export function markWorkbenchTodoRead(todoKey: string) { if (useMock()) return delay(undefined) return real.markWorkbenchTodoRead(todoKey) } /* ========== 基础信息 / 人员新增录入 ========== */ export function fetchVillageBatches() { if (useMock()) return delay([...mockVillageBatches]) return real.fetchVillageBatches() } export function submitRetireQualify( personnelId: string, payload?: { opinion?: string; fileUrls?: string }, config?: { silent?: boolean }, ) { if (useMock()) return delay({ ok: true }) return real.submitRetireQualify(personnelId, payload, config) } export function submitRetireQualifyBatch(payload: { personnelIds: string[] opinion?: string fileUrls?: string }) { if (useMock()) return delay({ ok: true, successCount: payload.personnelIds.length, pendingCount: 0, failCount: 0, message: '' }) return real.submitRetireQualifyBatch(payload) } export function generateDistrictMonthBatch( payMonth?: string, matchInsurance = true, simulateFirstDay = false, ) { if (useMock()) return delay({ ok: true }) return real.generateDistrictMonthBatch(payMonth, matchInsurance, simulateFirstDay) } export function fetchPendingSpecialCount(town?: string) { if (useMock()) return delay(0) return real.fetchPendingSpecialCount(town) } export function fetchPeople(params?: { batchNo?: string town?: string village?: string }) { if (useMock()) { let list = [...mockPeople] if (params?.batchNo) list = list.filter((p) => p.batchNo === params.batchNo) if (params?.town) list = list.filter((p) => p.town === params.town) if (params?.village) list = list.filter((p) => p.village === params.village) return delay(list) } return real.fetchPeople(params) } export function fetchPeoplePage(params?: real.PeoplePageParams) { if (useMock()) { let list = [...mockPeople] if (params?.town) list = list.filter((p) => p.town === params.town) if (params?.village) list = list.filter((p) => p.village === params.village) if (params?.name) list = list.filter((p) => p.name?.includes(params.name!)) if (params?.idCard) list = list.filter((p) => p.idCard?.includes(params.idCard!)) if (params?.phone) { list = list.filter((p) => String((p as Person).phoneNumber || '').includes(params.phone!)) } if (params?.source) list = list.filter((p) => String(p.source || '').includes(params.source!)) if (params?.bizStatus) { const code = String(params.bizStatus).toUpperCase() list = list.filter( (p) => String((p as Person).bizStatus || '').toUpperCase() === code || (code === 'NORMAL' && p.status === '正常在库') || (code === 'PAUSED' && p.status === '暂停') || (code === 'STOPPED' && p.status === '停止') || (code === 'FUNERAL' && String(p.status).includes('死亡')), ) } if (params?.insuranceMatchResult) { const code = String(params.insuranceMatchResult).toUpperCase() list = list.filter( (p) => String((p as Person).insuranceMatchResult || '').toUpperCase() === code, ) } if (params?.retireQualifyType) { const code = String(params.retireQualifyType).toUpperCase() list = list.filter((p) => { const t = String((p as Person).retireQualifyType || '').toUpperCase() if (code === 'UNSET') return !t || t === 'UNSET' || t === 'NONE' || t === 'CLEARED' if (code === 'WITH_LAND') return t === 'WITH_LAND' || (p as Person).retireQualifyType === '有土地退养' if (code === 'WITHOUT_LAND') return t === 'WITHOUT_LAND' || (p as Person).retireQualifyType === '无土地有资格' if (code === 'INELIGIBLE') return t === 'INELIGIBLE' || (p as Person).retireQualifyType === '无资格' return t === code }) } const pageNum = params?.pageNum || 1 const pageSize = params?.pageSize || 10 const start = (pageNum - 1) * pageSize return delay({ list: list.slice(start, start + pageSize), total: list.length, pageNum, pageSize, }) } return real.fetchPeoplePage(params) } export function downloadPersonnelImportTemplate(town?: string, village?: string) { if (useMock()) { ElMessage.info('模拟模式:请切换联调后下载正式模板') return delay(undefined) } return real.downloadPersonnelImportTemplate(town, village) } export function importPersonnelExcel(file: File, town?: string, village?: string) { if (useMock()) { ElMessage.success('模拟模式:已假装导入成功') return delay({ successCount: 0, failCount: 0, errors: [], failRows: [] }) } return real.importPersonnelExcel(file, town, village) } export function exportPersonnelImportFailRows(failRows: real.PersonnelImportFailRow[]) { if (useMock()) { ElMessage.info('模拟模式:请切换联调后导出失败行') return delay(undefined) } return real.exportPersonnelImportFailRows(failRows) } export function fetchPersonnelImportHistory(limit = 50) { if (useMock()) { return delay([] as real.PersonnelImportHistoryItem[]) } return real.fetchPersonnelImportHistory(limit) } export function fetchAlertDetail(alertId: string) { if (useMock()) { const hit = mockAlerts.find((a) => String(a.id) === String(alertId)) return delay( hit ? { ...hit, lifecycle: [ { actionLabel: '产生告警', status: '待处理', operator: '系统', result: hit.content, operateTime: hit.createdAt, }, ...(hit.status === '已关闭' ? [ { actionLabel: '关闭告警', status: '已关闭', operator: '业务部门', result: hit.result || '已核实并处理', operateTime: hit.createdAt, }, ] : []), ], } : null, ) } return real.fetchAlertDetail(alertId) } /** 确保本村本月批次;草稿/退回时纠正误标「待镇初审」人员 */ export function ensureVillageMonthBatch(town?: string, village?: string, payMonth?: string) { if (useMock()) return delay(undefined) return real.ensureVillageMonthBatch(town, village, payMonth) } export function fetchPersonDetail(personnelId: string) { if (useMock()) { const hit = mockPeople.find((p) => String(p.id) === String(personnelId)) return delay(hit ? { ...hit } : null) } return real.fetchPersonDetail(personnelId) } export function savePerson(payload: Partial) { if (useMock()) { const month = getAppConfig().currentMonth if (payload.id != null && payload.id !== '') { const hit = mockPeople.find((p) => String(p.id) === String(payload.id)) if (hit) { Object.assign(hit, { ...payload, name: payload.name ?? hit.name, idCard: payload.idCard ?? hit.idCard, town: payload.town ?? hit.town, village: payload.village ?? hit.village, bankCard: payload.bankCard ?? hit.bankCard, bankName: payload.bankName ?? hit.bankName, socialInsuranceCode: payload.socialInsuranceCode ?? hit.socialInsuranceCode, standard: payload.standard ?? hit.standard, startMonth: payload.startMonth ?? hit.startMonth, remark: payload.remark ?? hit.remark, source: payload.source ?? hit.source, }) return delay({ ok: true }) } } const id = Math.max(0, ...mockPeople.map((p) => Number(p.id) || 0)) + 1 mockPeople.push({ id, batchNo: month.replace('-', ''), name: payload.name || '', idCard: payload.idCard || '', town: payload.town || '叶榭镇', village: payload.village || '叶榭村', bankCard: payload.bankCard || '', bankName: payload.bankName || '', socialInsuranceCode: payload.socialInsuranceCode || '', standard: payload.standard ?? 1280, startMonth: payload.startMonth || month, status: '草稿', source: '村合作社录入', insuranceStatus: '未匹配', remark: payload.remark, }) return delay({ ok: true }) } return real.savePerson(payload) } /** 更新批次纳入人员 */ export function updateBatchMembers(batchId: string, personnelIds: string[]) { if (useMock()) { const batch = mockVillageBatches.find((b) => b.id === batchId) if (batch) { batch.peopleCount = personnelIds.length batch.totalAmount = personnelIds.length * 1280 } return delay({ ok: true }) } return real.updateBatchMembers(batchId, personnelIds) } /** 批次已纳入人员 ID */ export function fetchBatchMemberIds(batchId: string) { if (useMock()) { const batch = mockVillageBatches.find((b) => b.id === batchId) if (!batch) return delay([] as string[]) const ids = mockPeople .filter( (p) => p.town === batch.town && p.village === batch.village && p.batchNo === batch.batchNo && !['暂停', '停止', '死亡丧葬', '已归档'].includes(p.status), ) .map((p) => String(p.id)) return delay(ids) } return real.fetchBatchMemberIds(batchId) } /** 本批次人员明细(按发放明细,非全村人员库) */ export function fetchBatchMemberPeoplePage( batchId: string, opts?: { batchNo?: string town?: string village?: string pruneDefaults?: boolean parentBatchId?: string pageNum?: number pageSize?: number }, ) { if (useMock()) { const batch = mockVillageBatches.find((b) => b.id === batchId) if (!batch) return delay({ list: [] as BatchMemberPerson[], total: 0, pageNum: 1, pageSize: 10 }) const list = mockPeople .filter( (p) => p.town === batch.town && p.village === batch.village && p.batchNo === batch.batchNo && !['暂停', '停止', '死亡丧葬', '已归档'].includes(p.status), ) .map((p) => mockAsBatchMember(p)) return mockPaged(list, opts?.pageNum, opts?.pageSize) } return real.fetchBatchMemberPeoplePage(batchId, opts) } export function fetchBatchMemberPeople( batchId: string, opts?: { batchNo?: string town?: string village?: string pruneDefaults?: boolean parentBatchId?: string pageNum?: number pageSize?: number }, ) { if (useMock()) { return fetchBatchMemberPeoplePage(batchId, opts).then((p) => p.list) } return real.fetchBatchMemberPeople(batchId, opts) } /** 本村发放历史(发放明细行;金额来自明细,非人员底数冗余标准) */ export function fetchVillagePaymentHistory(params?: { town?: string; village?: string }) { if (useMock()) { let list = [...mockPeople] if (params?.town) list = list.filter((p) => p.town === params.town) if (params?.village) list = list.filter((p) => p.village === params.village) return delay(list.map((p) => mockAsBatchMember(p))) } return real.fetchVillagePaymentHistory(params) } /** 指定人员历史发放明细 */ export function fetchPersonnelPaymentHistory(personnelId: string) { if (useMock()) { const p = mockPeople.find((x) => String(x.id) === String(personnelId)) return delay(p ? [mockAsBatchMember(p)] : []) } return real.fetchPersonnelPaymentHistory(personnelId) } /** 指定人员告警/处理记录 */ export function fetchPersonnelAlerts(params: { personnelId?: string personName?: string town?: string village?: string }) { if (useMock()) { return delay( mockAlerts.filter( (a) => (!params.personName || a.personName === params.personName) && (!params.town || a.town === params.town) && (!params.village || !a.village || a.village === params.village), ), ) } return real.fetchPersonnelAlerts(params) } function mockAsBatchMember(p: Person): BatchMemberPerson { const monthlyAmount = Number(p.standard || 0) const supplementAmount = 0 return { ...p, monthlyAmount, oneTimeAmount: 0, supplementAmount, funeralAmount: 0, deductAmount: 0, payableAmount: monthlyAmount + supplementAmount, } } /** 村合作社提交批次 → 镇经发中心初审 */ export function submitVillageBatch(batchId: string) { if (useMock()) { const batch = mockVillageBatches.find((b) => b.id === batchId) if (batch && (batch.stage === '人员新增录入' || batch.stage === '退回修改')) { batch.stage = '镇经发中心初审' batch.submittedAt = nowStr() mockApprovals.unshift({ id: `AP${Date.now()}`, bizType: '月度批次人员申请', batchNo: batch.batchNo, town: batch.town, village: batch.village, title: `${batch.village}${batch.month}批次人员申请`, applicant: `${batch.village}合作社`, node: '镇经发中心初审', status: '待审批', createdAt: nowStr(), }) mockLogs.unshift({ id: Date.now(), time: nowStr(), user: '村合作社', role: '村合作社', module: '批次', action: `提交初审 ${batch.batchNo}`, result: '成功', }) } return delay({ ok: true }) } return real.submitVillageBatch(batchId) } /** 批次详情 */ export function fetchBatchDetail(batchId: string) { if (useMock()) { const b = mockVillageBatches.find((x) => x.id === batchId) return delay(b ? { ...b } : null) } return real.fetchBatchDetail(batchId) } /** 批次流程总览 */ export function fetchBatchOverview(batchId: string) { if (useMock()) { const b = mockVillageBatches.find((x) => x.id === batchId) return delay( b ? { batchId: b.id, batchNo: b.batchNo, currentStage: b.stage, status: b.stage, stageDescription: `当前处于「${b.stage}」`, availableActions: b.stage === '人员新增录入' || b.stage === '退回修改' ? ['SUBMIT_BATCH'] : [], recentLogs: [ { id: '1', stage: b.stage, action: '创建批次', operator: b.submitter || '系统', result: '成功', operateTime: b.submittedAt || nowStr(), }, ], } : null, ) } return real.fetchBatchOverview(batchId) } /** 批次流程时间线 */ export function fetchBatchTimeline(batchId: string) { if (useMock()) { const b = mockVillageBatches.find((x) => x.id === batchId) if (!b) return delay([]) return delay([ { id: 'm1', batchId: b.id, batchNo: b.batchNo, stage: '人员新增录入', action: '创建本月村批', actionLabel: '创建本月村批', operator: '系统', result: '成功', operateTime: nowStr(), }, ...(b.submittedAt ? [ { id: 'm2', batchId: b.id, batchNo: b.batchNo, stage: b.stage, action: '提交/流转', actionLabel: '提交/流转', operator: b.submitter || '经办人', result: '成功', operateTime: b.submittedAt, }, ] : []), ]) } return real.fetchBatchTimeline(batchId) } /** 人员状态变更 / 特殊业务时间线 */ export function fetchPersonnelTimeline(personnelId: string | number) { if (useMock()) { const p = mockPeople.find((x) => String(x.id) === String(personnelId)) return delay([ { id: `pl-${personnelId}-1`, action: 'STATUS_NORMAL', actionLabel: '纳入在册', operator: '系统', operatorRole: '业务部门', opinion: p ? `${p.name} 基础信息建档` : '建档', result: '成功', operateTime: nowStr(), remarks: p?.remark, }, ] as import('@/types/biz').ProcessLogItem[]) } return real.fetchPersonnelTimeline(String(personnelId)) } /** 补充流程备注 */ export function addBatchProcessNote(payload: { batchId: string opinion: string fileUrls?: string }) { if (useMock()) { mockLogs.unshift({ id: Date.now(), time: nowStr(), user: '演示用户', role: getAppConfig().currentRole, module: '流程备注', action: payload.opinion, result: '成功', }) return delay({ ok: true }) } return real.addBatchProcessNote(payload) } /* ========== 居保匹配 ========== */ export function fetchInsuranceBatchesPage(params?: { tab?: 'todo' | 'history' batchNo?: string stage?: string pageNum?: number pageSize?: number }) { if (useMock()) { const todo = new Set(['居保状态匹配', '比对结果已生成']) const history = new Set([ '分管领导审批', '待线下发放', '回盘信息确认', '已回盘确认', '已归档', ]) const stages = params?.tab === 'history' ? history : todo let list = mockVillageBatches.filter((b) => stages.has(b.stage)) if (params?.batchNo) list = list.filter((b) => b.batchNo.includes(params.batchNo!)) if (params?.stage) list = list.filter((b) => b.stage === params.stage) return mockPaged(list, params?.pageNum, params?.pageSize) } return real.fetchInsuranceBatchesPage(params) } export function fetchInsuranceBatches(params?: { tab?: 'todo' | 'history' }) { if (useMock()) { return fetchInsuranceBatchesPage(params).then((p) => p.list) } return real.fetchInsuranceBatches(params) } /** 查询居保匹配结果;可按批次号 / 街镇 / 村居过滤 */ export function fetchInsuranceResultsPage(params?: { batchId?: string batchNo?: string town?: string village?: string pageNum?: number pageSize?: number }) { if (useMock()) { let list = [...mockPeople] if (params?.batchNo) list = list.filter((p) => p.batchNo === params.batchNo) if (params?.town) list = list.filter((p) => p.town === params.town) if (params?.village) list = list.filter((p) => p.village === params.village) return mockPaged(list, params?.pageNum, params?.pageSize) } return real.fetchInsuranceResultsPage(params) } export function fetchInsuranceResults(params?: { batchId?: string batchNo?: string town?: string village?: string pageNum?: number pageSize?: number }) { if (useMock()) { return fetchInsuranceResultsPage(params).then((p) => p.list) } return real.fetchInsuranceResults(params) } /** 导出居保匹配结果 xlsx */ export function exportInsuranceMatch(params: { batchId?: string town?: string townId?: string }) { if (useMock()) { return delay({ ok: false as const, fileName: '', message: '演示模式不支持导出' }) } return real.exportInsuranceMatch(params) } /** 触发居保匹配:产出比对结果清单,批次进入「比对结果已生成」;支持单条或批量 */ export function triggerInsuranceMatch(batchIds?: string | string[]) { if (useMock()) { const ids = batchIds == null ? null : Array.isArray(batchIds) ? batchIds : [batchIds] const targets = ids ? mockVillageBatches.filter((b) => ids.includes(b.id) && b.stage === '居保状态匹配') : mockVillageBatches.filter((b) => b.stage === '居保状态匹配') for (const b of targets) { b.stage = '比对结果已生成' } return delay({ ok: true, count: targets.length, lastSummary: null }) } return real.triggerInsuranceMatch(batchIds) } /** 居保匹配任务执行日志 */ export function fetchInsuranceTaskLogs(batchId: string, limit = 30) { if (useMock()) return delay([] as Record[]) return real.fetchInsuranceTaskLogs(batchId, limit) } /** 人工确认居保匹配结果 */ export function confirmInsuranceManual( personId: number | string, opts?: { batchId?: string; pass?: boolean; remark?: string }, ) { if (useMock()) { const p = mockPeople.find((x) => x.id === personId) if (p) { if (opts?.pass === false) { p.insuranceStatus = '匹配失败' p.status = '暂停' p.remark = opts.remark || '人工确认暂停发放' } else { p.insuranceStatus = '匹配通过' p.status = '正常在库' p.remark = opts?.remark || '人工确认匹配成功' } } return delay({ ok: true }) } return real.confirmInsuranceManual(personId, opts) } /** 业务部门确认比对结果清单后提交分管领导审批 */ export function submitToLeader(batchId: string) { if (useMock()) { const b = mockVillageBatches.find((x) => x.id === batchId) if (!b || b.stage !== '比对结果已生成') { return delay({ ok: false, message: '仅待提交批次可提交分管领导审核' }) } b.stage = '分管领导审批' b.submittedAt = nowStr() mockApprovals.unshift({ id: `AP${Date.now()}${b.id}`, bizType: '月补贴清单确认', batchNo: b.batchNo, town: b.town, village: b.village, title: `${b.town}${b.village}月补贴清单提交分管领导`, applicant: `${b.town}社区事务受理中心`, node: '分管领导线上审批', status: '待审批', createdAt: nowStr(), }) return delay({ ok: true }) } return real.submitToLeader(batchId) } /** 导出比对结果清单(演示) */ export function exportCompareList(_batchId?: string) { if (useMock()) return delay({ ok: true, fileName: `${getAppConfig().currentMonth.replace('-', '')}-比对结果清单.xlsx` }) return real.exportCompareList(_batchId) } /** 居保异常批次退回镇经发中心 */ export function returnBatchToTown(batchId: string, reason?: string) { if (useMock()) { const batch = mockVillageBatches.find((b) => b.id === batchId) if (batch) { batch.stage = '镇经发中心初审' mockApprovals.unshift({ id: `AP${Date.now()}`, bizType: '居保匹配退回', batchNo: batch.batchNo, town: batch.town, village: batch.village, title: `${batch.village}居保匹配异常退回初审`, applicant: `${batch.town}社区事务受理中心`, node: '镇经发中心初审', status: '待审批', opinion: reason || '居保匹配异常,退回镇经发中心', createdAt: nowStr(), }) } return delay({ ok: true }) } return real.returnBatchToTown(batchId, reason) } /* ========== 审批 ========== */ export function fetchApprovals(params?: { tab?: string node?: string /** 街镇(镇经发 / 分管领导仅看本镇) */ town?: string village?: string /** 统一批次号,如 202608 */ batchNo?: string /** 业务类型包含关键字,如「月度批次」 */ bizTypeIncludes?: string status?: string pageNum?: number pageSize?: number }) { if (useMock()) { let list = [...mockApprovals] if (params?.tab === 'pending') list = list.filter((a) => a.status === '待审批') if (params?.tab === 'history') { list = list.filter((a) => a.status === '已通过' || a.status === '已驳回') } if (params?.node) list = list.filter((a) => a.node === params.node) if (params?.town) list = list.filter((a) => a.town === params.town) if (params?.village) list = list.filter((a) => a.village === params.village) if (params?.batchNo) list = list.filter((a) => a.batchNo === params.batchNo) if (params?.status) list = list.filter((a) => a.status === params.status) if (params?.bizTypeIncludes) { list = list.filter((a) => a.bizType.includes(params.bizTypeIncludes!)) } return mockPaged(list, params?.pageNum, params?.pageSize) } return real.fetchApprovals(params) } /** * 镇经发中心:核验新增人员退养资格 * 写入 retireQualifyType;人员状态仍跟 bizStatus(无资格除外) */ export function reviewPersonQualify(payload: { personId: number | string retireQualifyType: '有土地退养' | '无土地有资格' | '无资格' }) { if (useMock()) { const p = mockPeople.find((x) => x.id === payload.personId) if (!p) return delay({ ok: false }) p.retireQualifyType = payload.retireQualifyType if (payload.retireQualifyType === '无资格') { p.status = '无资格' p.remark = [p.remark, '无资格;不具备土地退养补助资格'].filter(Boolean).join(';') } else if (payload.retireQualifyType === '无土地有资格') { p.remark = p.remark?.includes('无土地') ? p.remark : [p.remark, '无承包地,按政策认定具备退养补助资格'].filter(Boolean).join(';') } return delay({ ok: true, person: { ...p } }) } return real.reviewPersonQualify(payload) } export function decideApproval(payload: { id: string pass: boolean opinion?: string fileUrls?: string retireQualifyType?: '有土地退养' | '无土地有资格' | '无资格' }) { if (useMock()) { const appr = mockApprovals.find((a) => a.id === payload.id) if (!appr) return delay({ ok: false }) appr.status = payload.pass ? '已通过' : '已驳回' appr.opinion = payload.opinion || (payload.pass ? '同意' : '退回修改') const batch = mockVillageBatches.find( (b) => b.batchNo === appr.batchNo && b.town === appr.town && (!appr.village || b.village === appr.village), ) if (appr.node.includes('镇经发')) { if (payload.pass && batch) { batch.stage = '居保状态匹配' } else if (!payload.pass && batch) { batch.stage = '退回修改' } } else if (appr.node.includes('分管领导')) { if (payload.pass && batch) { // 分管领导通过后进入「已审核」,由业务部门执行出盘 batch.stage = '待线下发放' const monthly = mockMonthlyBatches.find( (m) => m.batchNo === batch.batchNo && m.town === batch.town, ) if (monthly) monthly.status = '已审核' } else if (!payload.pass && batch) { batch.stage = '居保状态匹配' } // 特殊业务(一般/丧葬/调标补发):通过后状态回写,驳回回业务受理 const sp = mockSpecials.find( (s) => s.status === '待分管领导审批' && s.town === appr.town && (appr.title.includes(s.personName) || appr.bizType.includes(s.type.slice(0, 2))), ) if (sp) { sp.status = payload.pass ? '已通过' : '已驳回(补充/修改)' } } return delay({ ok: true }) } return real.decideApproval(payload) } export function decideApprovals(payload: { ids: string[] pass: boolean opinion?: string fileUrls?: string retireQualifyType?: '有土地退养' | '无土地有资格' | '无资格' }) { if (useMock()) { return Promise.all(payload.ids.map((id) => decideApproval({ ...payload, id }))).then(() => ({ ok: true, })) } return real.decideApprovals(payload) } /* ========== 人员状态维护 ========== */ export function fetchStatusBatches() { if (useMock()) { return delay( mockVillageBatches.filter( (b) => b.stage === '分管领导审批' || b.stage === '待线下发放' || b.stage === '回盘信息确认', ), ) } return real.fetchStatusBatches() } /** 补助标准列表;传 town 时仅返回该镇各年度标准 */ export function fetchStandards(town?: string) { if (useMock()) { const list = town ? mockStandards.filter((s) => s.town === town) : [...mockStandards] return delay( [...list].sort((a, b) => (b.year !== a.year ? b.year - a.year : b.date.localeCompare(a.date))), ) } return real.fetchStandards(town) } /** 本镇当前有效丧葬补贴金额(元) */ export async function fetchCurrentFuneralAmount(town: string): Promise { if (useMock()) { const list = town ? mockStandards.filter((s) => s.town === town) : [...mockStandards] const sorted = [...list].sort((a, b) => b.year !== a.year ? b.year - a.year : b.date.localeCompare(a.date), ) const hit = sorted.find((s) => s.funeral > 0) return delay(hit ? hit.funeral : null) } return real.fetchCurrentFuneralAmount(town) } /** 新增年度补助标准 */ export function createStandard(payload: { town: string year: number monthly: number funeral: number date: string reason: string }) { if (useMock()) { const dup = mockStandards.find((s) => s.town === payload.town && s.year === payload.year) if (dup) { return delay({ ok: false as const, message: `${payload.year} 年度标准已存在,请勿重复新增` }) } const id = Math.max(0, ...mockStandards.map((s) => s.id)) + 1 mockStandards.unshift({ id, ...payload }) return delay({ ok: true as const, id }) } return real.createStandard(payload) } /** 调整最新补助标准 */ export function updateStandard(payload: { id: number monthly: number funeral: number date: string reason: string rawId?: string town?: string }) { if (useMock()) { const row = mockStandards.find((s) => s.id === payload.id) if (!row) return delay({ ok: false as const, message: '未找到该标准记录' }) row.monthly = payload.monthly row.funeral = payload.funeral row.date = payload.date row.reason = payload.reason return delay({ ok: true as const }) } return real.updateStandard(payload) } /** 管理员删除补助标准 */ export function deleteStandard(payload: { id: number; rawId?: string }) { if (useMock()) { const idx = mockStandards.findIndex((s) => s.id === payload.id) if (idx < 0) return delay({ ok: false as const, message: '未找到该标准记录' }) mockStandards.splice(idx, 1) return delay({ ok: true as const, message: '已删除' }) } return real.deleteStandard(payload) } /** 死亡状态更新(业务部门) */ export function updateDeathStatus(deathId: string) { if (useMock()) { const death = mockDeaths.find((d) => d.id === deathId) if (death) { death.status = '已停止发放' const alert = mockAlerts.find( (a) => a.type === '民政死亡比对' && a.personName === death.personName, ) if (alert) { alert.status = '已关闭' alert.result = '已完成死亡状态更新,停止发放' } } return delay({ ok: true }) } return real.updateDeathStatus(deathId) } export function applyPersonStatusAction(payload: { personId: number | string action: string /** 补发金额,可为负数 */ amount?: number reason?: string fileUrls?: string }) { if (useMock()) { const p = mockPeople.find((x) => x.id === payload.personId) if (p) { if (payload.action === '停止' || payload.action === '暂停/停止') { p.status = '停止' } else if (payload.action === '恢复') { p.status = '正常在库' } else if (payload.action === '丧葬' || payload.action === '死亡状态更新') { p.status = '死亡丧葬' } else if (payload.action === '补发' || payload.action === '调标补发') { const amountTip = typeof payload.amount === 'number' ? `金额 ${payload.amount} 元` : '' const reasonTip = payload.reason ? `:${payload.reason}` : '' p.remark = `${p.remark || ''}(补发待审批${amountTip}${reasonTip})`.trim() } } return delay({ ok: true }) } return real.applyPersonStatusAction(payload) } /* ========== 特殊业务 ========== */ export function fetchSpecialsPage(params?: { town?: string bizType?: string approveStatus?: string pageNum?: number pageSize?: number }) { if (useMock()) { let list = params?.town ? mockSpecials.filter((s) => s.town === params.town) : [...mockSpecials] if (params?.bizType === 'FUNERAL') list = list.filter((s) => s.type === '丧葬补贴') if (params?.bizType === 'REFUND') list = list.filter((s) => s.type === '多发退款') return mockPaged(list, params?.pageNum, params?.pageSize) } return real.fetchSpecialsPage(params) } export function fetchSpecials(params?: { town?: string }) { if (useMock()) { return fetchSpecialsPage(params).then((p) => p.list) } return real.fetchSpecials(params) } /** 特殊业务审批详情:关联人员 */ export function fetchSpecialBizPerson(personnelId: string) { if (useMock()) { const p = mockPeople.find((x) => String(x.id) === String(personnelId)) return delay(p || null) } return real.fetchSpecialBizPerson(personnelId) } export function fetchDeathsPage(params?: { town?: string; pageNum?: number; pageSize?: number }) { if (useMock()) { const list = params?.town ? mockDeaths.filter((d) => d.town === params.town) : [...mockDeaths] return mockPaged(list, params?.pageNum, params?.pageSize) } return real.fetchDeathsPage(params) } export function fetchDeaths(params?: { town?: string; pageNum?: number; pageSize?: number }) { if (useMock()) { return fetchDeathsPage(params).then((p) => p.list) } return real.fetchDeaths(params) } /** 镇社区事务中心:死亡人员信息录入 → 提交分管领导审批(通过后改人员状态) */ export function createDeathRecord(payload: { personId?: string | number personName: string idCard: string town: string village: string deathDate: string heirName?: string heirRelation?: string heirIdCard?: string heirBankCard?: string heirContact?: string fileUrls?: string }) { if (useMock()) { const dup = mockDeaths.find( (d) => (d.status === '待分管领导审批' || d.status === '待死亡状态更新') && (d.idCard === payload.idCard || (d.personName === payload.personName && d.town === payload.town && d.village === payload.village)), ) if (dup) { return delay({ ok: false, message: '该人员已有待审批的死亡登记,请勿重复提交', code: 'DUPLICATE_DEATH', }) } const id = `SP${Date.now()}` mockDeaths.unshift({ id, personnelId: payload.personId != null ? String(payload.personId) : undefined, personName: payload.personName, idCard: payload.idCard, town: payload.town, village: payload.village, deathDate: payload.deathDate, heirName: payload.heirName || '', heirIdCard: payload.heirIdCard || '', heirBankCard: payload.heirBankCard || '', heirContact: payload.heirContact || '', source: '社区事务受理中心手工录入', status: '待分管领导审批', createdAt: nowStr(), fileUrls: payload.fileUrls, reason: `死亡登记;死亡日期 ${payload.deathDate}`, }) return delay({ ok: true, id }) } return real.createDeathRecord(payload) } export function uploadBizFile(file: File) { if (useMock()) { return delay({ url: `mock://${file.name}`, name: file.name }) } return real.uploadBizFile(file) } /** 发起一般特殊业务 / 丧葬补贴 */ export function createSpecialBiz(payload: { type: string personId?: string | number personName: string town: string village: string reason: string amount: number heirName?: string fileUrls?: string refundMethod?: 'DEDUCT_NEXT' | 'RETURN_FISCAL' }) { if (useMock()) { const month = getAppConfig().currentMonth const id = `SP${Date.now()}` mockSpecials.unshift({ id, batchNo: month.replace('-', ''), town: payload.town, village: payload.village, type: payload.type, personName: payload.personName, reason: payload.reason, amount: payload.amount, status: '待分管领导审批', heirName: payload.heirName, refundMethod: payload.refundMethod, refundMethodLabel: payload.refundMethod === 'RETURN_FISCAL' ? '退缴财政' : payload.refundMethod ? '下月扣减' : undefined, fileUrls: payload.fileUrls, createdAt: nowStr(), }) mockApprovals.unshift({ id: `AP${Date.now()}`, bizType: payload.type, batchNo: month.replace('-', ''), town: payload.town, village: payload.village, title: `${payload.personName}${payload.type}申请`, applicant: `${payload.town}社区事务受理中心`, node: '分管领导线上审批', status: '待审批', createdAt: nowStr(), }) return delay({ ok: true, id }) } return real.createSpecialBiz(payload) } /** 特殊业务汇总文件导出(调标补发/丧葬/一般) */ export function exportSpecialSummary(type?: string) { if (useMock()) { const name = type ? `${getAppConfig().currentMonth.replace('-', '')}-${type}汇总文件.xlsx` : '特殊业务汇总文件.xlsx' return delay({ ok: true, fileName: name }) } return real.exportSpecialSummary(type) } /** * 发起调标补发申请: * 1. 在月补贴清单按本镇各村自动生成批次(待提交) * 2. 生成各村调标补发名单(后续对接接口由后端返回) */ export function triggerAdjustReissue(town?: string) { if (useMock()) { const month = getAppConfig().currentMonth const batchNo = month.replace('-', '') const targetTown = town || '叶榭镇' const villages = getVillagesByTown(targetTown) if (!villages.length) { return delay({ ok: false, message: '未找到本镇村居配置', batchNo, count: 0, villageCount: 0 }) } const newStandard = getStandardByTown(targetTown) /** 演示:调标差额默认按新标准 - 原标准,原标准按新标准减 60 */ const diffPerPerson = 60 let personCount = 0 const stamp = Date.now() // 清空本镇本批次旧名单后重建(演示可重复触发) for (let i = mockAdjustReissueRoster.length - 1; i >= 0; i--) { const row = mockAdjustReissueRoster[i] if (row.town === targetTown && row.batchNo === batchNo) { mockAdjustReissueRoster.splice(i, 1) } } villages.forEach((village, vIdx) => { // 月补贴清单:各村生成/更新为「待提交」批次(不覆盖已审核及之后状态) let batch = mockVillageBatches.find( (b) => b.batchNo === batchNo && b.town === targetTown && b.village === village, ) const villagePeople = mockPeople.filter( (p) => p.town === targetTown && p.village === village && p.batchNo === batchNo && !['死亡丧葬', '已停止发放'].includes(p.status), ) const peopleCount = villagePeople.length || 5 + (vIdx % 4) const totalAmount = peopleCount * diffPerPerson const canResetStage = !batch || [ '人员新增录入', '镇经发中心初审', '居保状态匹配', '比对结果已生成', '退回修改', ].includes(batch.stage) if (batch && canResetStage) { batch.stage = '比对结果已生成' batch.peopleCount = Math.max(batch.peopleCount, peopleCount) batch.totalAmount = Math.max(batch.totalAmount, totalAmount) batch.submitter = `${targetTown}社区事务受理中心` batch.submittedAt = nowStr() } else if (!batch) { batch = { id: `VB${batchNo}ADJ${stamp}${vIdx}`, batchNo, month, town: targetTown, village, peopleCount, totalAmount, stage: '比对结果已生成', submitter: `${targetTown}社区事务受理中心`, submittedAt: nowStr(), } mockVillageBatches.unshift(batch) } else { // 已进入审核/出盘等后续环节:保留原批次,仅补充名单 batch.peopleCount = Math.max(batch.peopleCount, peopleCount) } // 各村调标补发名单 const sourcePeople = villagePeople.length > 0 ? villagePeople : mockPeople .filter((p) => p.town === targetTown && p.village === village) .slice(0, Math.min(8, 3 + vIdx)) sourcePeople.forEach((p, pIdx) => { const oldStandard = Math.max(0, (p.standard || newStandard) - diffPerPerson) mockAdjustReissueRoster.push({ id: `AR${batchNo}${village}${p.id || pIdx}${stamp}`, batchNo, personId: p.id, name: p.name, idCard: p.idCard, town: targetTown, village, oldStandard, newStandard: p.standard || newStandard, amount: diffPerPerson, status: '待提交', remark: '年度调标差额补发', }) personCount += 1 }) // 若该村暂无人员,生成演示名单 if (!sourcePeople.length) { for (let i = 0; i < 3; i++) { mockAdjustReissueRoster.push({ id: `AR${batchNo}${village}D${i}${stamp}`, batchNo, personId: 0, name: `演示人员${village}${i + 1}`, idCard: `3101171958010${1000 + vIdx * 10 + i}`, town: targetTown, village, oldStandard: newStandard - diffPerPerson, newStandard, amount: diffPerPerson, status: '待提交', remark: '年度调标差额补发(演示)', }) personCount += 1 } } }) return delay({ ok: true, batchNo, villageCount: villages.length, count: personCount, }) } return real.triggerAdjustReissue(town) } /** 查询调标补发名单(可按镇/批次/村过滤) */ export function fetchAdjustReissueRoster(params?: { town?: string batchNo?: string village?: string }) { if (useMock()) { let list = [...mockAdjustReissueRoster] if (params?.town) list = list.filter((r) => r.town === params.town) if (params?.batchNo) list = list.filter((r) => r.batchNo === params.batchNo) if (params?.village) list = list.filter((r) => r.village === params.village) return delay(list) } return real.fetchAdjustReissueRoster(params) } /* ========== 月补贴清单 ========== */ /** 村级批次内部阶段 → 清单展示状态 */ const MONTHLY_STAGE_STATUS: Record = { 比对结果已生成: '待提交', 分管领导审批: '待审核', 待线下发放: '已审核', 回盘信息确认: '已出盘', 已回盘确认: '已回盘', 已归档: '已回盘', } const MONTHLY_LIST_STAGES = Object.keys(MONTHLY_STAGE_STATUS) function buildVillageMonthlyItem(b: VillageBatch): VillageMonthlyBatch { const people = mockPeople.filter( (p) => p.batchNo === b.batchNo && p.town === b.town && p.village === b.village, ) const stopped = new Set(['暂停', '停止', '死亡丧葬', '已停止发放']) let normalAmount = 0 let deductAmount = 0 for (const p of people) { if (stopped.has(p.status)) deductAmount += p.standard else normalAmount += p.standard } if (!people.length) { normalAmount = b.totalAmount } const extraAmount = Math.max(0, b.totalAmount - normalAmount) return { id: b.id, batchNo: b.batchNo, month: b.month, town: b.town, village: b.village, peopleCount: b.peopleCount || people.length, normalAmount, extraAmount, deductAmount, totalAmount: b.totalAmount || normalAmount + extraAmount - deductAmount, status: MONTHLY_STAGE_STATUS[b.stage] || b.stage, stage: b.stage, submitter: b.submitter, submittedAt: b.submittedAt, } } /** 本镇各村月补贴清单批次(待提交/待审核/已审核/已出盘/已回盘) */ export function fetchVillageMonthlyBatchesPage(params?: { town?: string pageNum?: number pageSize?: number }) { if (useMock()) { let list = mockVillageBatches.filter((b) => MONTHLY_LIST_STAGES.includes(b.stage)) if (params?.town) list = list.filter((b) => b.town === params.town) const items = list.map(buildVillageMonthlyItem) items.sort((a, b) => { const ta = a.submittedAt || '' const tb = b.submittedAt || '' if (ta !== tb) return tb.localeCompare(ta) return a.village.localeCompare(b.village) }) return mockPaged(items, params?.pageNum, params?.pageSize) } return real.fetchVillageMonthlyBatchesPage(params) } export function fetchVillageMonthlyBatches(town?: string) { if (useMock()) { let list = mockVillageBatches.filter((b) => MONTHLY_LIST_STAGES.includes(b.stage)) if (town) list = list.filter((b) => b.town === town) const items = list.map(buildVillageMonthlyItem) // 同批次同村保留最新一条(按 submittedAt) items.sort((a, b) => { const ta = a.submittedAt || '' const tb = b.submittedAt || '' if (ta !== tb) return tb.localeCompare(ta) return a.village.localeCompare(b.village) }) return delay(items) } return real.fetchVillageMonthlyBatches(town) } export function fetchMonthlyBatches() { if (useMock()) return delay([...mockMonthlyBatches]) return real.fetchMonthlyBatches() } export function calculateMonthly() { if (useMock()) { const month = getAppConfig().currentMonth const batchNo = month.replace('-', '') const exists = mockMonthlyBatches.find((m) => m.batchNo === batchNo) if (!exists) { mockMonthlyBatches.unshift({ id: `MB${batchNo}01`, batchNo, month, town: '叶榭镇', peopleCount: mockPeople.filter((p) => p.batchNo === batchNo).length, normalAmount: 199680, extraAmount: 0, deductAmount: 0, totalAmount: 199680, status: '待复核', }) } return delay({ ok: true }) } return real.calculateMonthly() } export function confirmMonthly(id: string) { if (useMock()) { const row = mockMonthlyBatches.find((m) => m.id === id) if (row) { row.status = '已生成拟发放文件' const disk = mockDisks.find((d) => d.batchNo === row.batchNo && d.town === row.town) if (disk) { disk.status = '待回盘' disk.returnStatus = '待确认' disk.fileName = `${row.batchNo}-${row.town}月补贴发放文件.xlsx` } else { mockDisks.unshift({ id: `DP${row.batchNo}${row.town}`, batchNo: row.batchNo, town: row.town, fileName: `${row.batchNo}-${row.town}月补贴发放文件.xlsx`, peopleCount: row.peopleCount, amount: row.totalAmount, status: '待回盘', returnStatus: '待确认', failCount: 0, }) } } return delay({ ok: true }) } return real.confirmMonthly(id) } /** 导出月度拟发放文件(供线下签字与财务发放) */ export function exportMonthlyFile(id?: string) { if (useMock()) { const row = id ? mockVillageBatches.find((m) => m.id === id) || mockMonthlyBatches.find((m) => m.id === id) : mockMonthlyBatches[0] const name = row ? `${row.batchNo}-${row.town}${'village' in row && row.village ? row.village : ''}月补贴发放文件.xlsx` : '月补贴发放文件.xlsx' return delay({ ok: true, fileName: name }) } return real.exportMonthlyFile(id) } /** * 出盘:已审核村批次导出拟发放文件,进入「已出盘」 */ export function issueVillageMonthlyDisk(batchId: string) { if (useMock()) { const batch = mockVillageBatches.find((b) => b.id === batchId) if (!batch || batch.stage !== '待线下发放') { return delay({ ok: false, message: '仅已审核批次可出盘' }) } batch.stage = '回盘信息确认' const diskId = `DP${batch.batchNo}${batch.town}${batch.village}` const exists = mockDisks.find((d) => d.id === diskId) const fileName = `${batch.batchNo}-${batch.town}${batch.village}月补贴发放文件.xlsx` if (exists) { exists.status = '待回盘' exists.returnStatus = '待确认' exists.fileName = fileName exists.peopleCount = batch.peopleCount exists.amount = batch.totalAmount } else { mockDisks.unshift({ id: diskId, batchNo: batch.batchNo, town: batch.town, fileName, peopleCount: batch.peopleCount, amount: batch.totalAmount, status: '待回盘', returnStatus: '待确认', failCount: 0, }) } return delay({ ok: true, fileName }) } return real.issueVillageMonthlyDisk(batchId) } /** * 确认回盘:已出盘村批次登记回盘结果,进入「已回盘」 */ export function confirmVillageMonthlyReturn(payload: { batchId: string failCount?: number }) { if (useMock()) { const batch = mockVillageBatches.find((b) => b.id === payload.batchId) if (!batch || batch.stage !== '回盘信息确认') { return delay({ ok: false, message: '仅已出盘批次可确认回盘' }) } const fail = payload.failCount ?? 0 batch.stage = '已回盘确认' const diskId = `DP${batch.batchNo}${batch.town}${batch.village}` let disk = mockDisks.find((d) => d.id === diskId) if (!disk) { disk = mockDisks.find( (d) => d.batchNo === batch.batchNo && d.town === batch.town && d.returnStatus === '待确认', ) } if (disk) { disk.failCount = fail disk.returnStatus = fail > 0 ? '部分失败' : '成功' disk.status = '已回盘' } if (fail > 0) { mockAlerts.unshift({ id: `AL${Date.now()}`, batchNo: batch.batchNo, personName: '回盘失败人员', town: batch.town, village: batch.village, type: '回盘发放失败', level: '高', failReason: DISK_RETURN_FAIL_REASONS[0], content: `回盘发放失败:${DISK_RETURN_FAIL_REASONS[0]},需纳入下月处理(${batch.town}${batch.village})`, status: '待处理', createdAt: nowStr(), }) } return delay({ ok: true }) } return real.confirmVillageMonthlyReturn(payload) } /* ========== 回盘信息确认 ========== */ /** 回盘批次列表(按批次号从新到旧) */ export function fetchDisksPage(params?: { town?: string; pageNum?: number; pageSize?: number }) { if (useMock()) { const list = params?.town ? mockDisks.filter((d) => d.town === params.town) : [...mockDisks] return mockPaged( [...list].sort((a, b) => b.batchNo.localeCompare(a.batchNo)), params?.pageNum, params?.pageSize, ) } return real.fetchDisksPage(params) } export function fetchDisks(town?: string) { if (useMock()) { return fetchDisksPage({ town }).then((p) => p.list) } return real.fetchDisks(town) } /** 导出回盘来源:月度拟发放文件(线上清单) */ export function exportDisk() { if (useMock()) { const pending = mockDisks.find((d) => d.returnStatus === '待确认') if (pending) { pending.fileName = pending.fileName || `${pending.batchNo}-${pending.town}月补贴发放文件.xlsx` } return delay({ ok: true }) } return real.exportDisk() } /** 回盘信息确认(登记线下发放结果,兼容旧调用) */ export function confirmReturnDisk(payload?: { id?: string failCount?: number }) { if (useMock()) { const row = (payload?.id && mockDisks.find((d) => d.id === payload.id)) || mockDisks.find((d) => d.returnStatus === '待确认') if (row) { const fail = payload?.failCount ?? 0 row.failCount = fail row.returnStatus = fail > 0 ? '部分失败' : '成功' row.status = '已回盘' if (fail > 0) { const townPeople = mockPeople.filter( (p) => p.town === row.town && p.batchNo === row.batchNo, ) const byVillage = new Map() for (const p of townPeople) { const list = byVillage.get(p.village) || [] list.push(p) byVillage.set(p.village, list) } const villages = byVillage.size > 0 ? [...byVillage.entries()].slice(0, Math.min(fail, byVillage.size)) : ([['同建村', []]] as [string, typeof townPeople][]) villages.forEach(([village, people], idx) => { const person = people[0] const reason = DISK_RETURN_FAIL_REASONS[idx % DISK_RETURN_FAIL_REASONS.length] mockAlerts.unshift({ id: `AL${Date.now()}${idx}`, batchNo: row.batchNo, personName: person?.name || '回盘失败人员', town: row.town, village, type: '回盘发放失败', level: '高', failReason: reason, content: `回盘发放失败:${reason},需纳入下月处理(${row.town}${village})`, status: '待处理', createdAt: nowStr(), idCard: person?.idCard, bankCard: person?.bankCard, bankName: person?.bankName, socialInsuranceCode: person?.socialInsuranceCode, }) mockDiskFailPeople.unshift({ id: `DF${Date.now()}${idx}`, diskId: row.id, batchNo: row.batchNo, town: row.town, village, personName: person?.name || '回盘失败人员', idCard: person?.idCard || '', bankCard: person?.bankCard, failReason: reason, amount: person?.standard, payStatus: '待确认', }) }) } } return delay({ ok: true }) } return real.confirmReturnDisk(payload) } /** * 上传回盘文件:选择批次后上传本镇回盘文件 * 后台匹配失败人员名单、生成告警,并更新清单/回盘状态 */ export function uploadReturnDiskFile(payload: { id: string; file: File }) { if (useMock()) { const row = mockDisks.find((d) => d.id === payload.id) if (!row) { return delay({ ok: false as const, message: '未找到回盘记录', failCount: 0, alertCount: 0 }) } if (row.returnStatus !== '待确认' || row.status !== '待回盘') { return delay({ ok: false as const, message: '该批次已上传回盘文件,请勿重复上传', failCount: 0, alertCount: 0, }) } const townPeople = mockPeople.filter( (p) => p.town === row.town && p.batchNo === row.batchNo && p.status === '正常在库', ) const pool = townPeople.length ? townPeople : mockPeople.filter((p) => p.town === row.town).slice(0, 5) /** 演示:按文件名哈希模拟失败人数(0~3) */ const nameSeed = [...payload.file.name].reduce((s, c) => s + c.charCodeAt(0), 0) const failCount = pool.length ? Math.min(pool.length, nameSeed % 4) : 0 const failed = pool.slice(0, failCount) // 清理该盘旧失败明细后重建 for (let i = mockDiskFailPeople.length - 1; i >= 0; i--) { if (mockDiskFailPeople[i].diskId === row.id) mockDiskFailPeople.splice(i, 1) } row.returnFileName = payload.file.name row.failCount = failCount row.returnStatus = failCount > 0 ? '部分失败' : '成功' row.status = '已回盘' failed.forEach((person, idx) => { const reason = DISK_RETURN_FAIL_REASONS[idx % DISK_RETURN_FAIL_REASONS.length] mockDiskFailPeople.unshift({ id: `DF${Date.now()}${idx}`, diskId: row.id, batchNo: row.batchNo, town: person.town, village: person.village, personName: person.name, idCard: person.idCard, bankCard: person.bankCard, failReason: reason, amount: person.standard, payStatus: '待确认', }) mockAlerts.unshift({ id: `AL${Date.now()}${idx}`, batchNo: row.batchNo, personName: person.name, town: person.town, village: person.village, type: '回盘发放失败', level: '高', failReason: reason, content: `回盘发放失败:${reason}(来源文件:${payload.file.name})`, status: '待处理', createdAt: nowStr(), idCard: person.idCard, bankCard: person.bankCard, bankName: person.bankName, socialInsuranceCode: person.socialInsuranceCode, }) }) return delay({ ok: true as const, failCount, alertCount: failCount, message: failCount > 0 ? `回盘文件已解析:${failCount} 人发放失败,已匹配人员并生成告警` : '回盘文件已解析:全部发放成功', }) } const form = new FormData() form.append('id', payload.id) form.append('file', payload.file) return real.uploadReturnDiskFile(payload) } /** 查询某回盘批次的发放失败人员清单 */ export function fetchDiskFailPeople(diskId: string) { if (useMock()) { return delay(mockDiskFailPeople.filter((p) => p.diskId === diskId)) } return real.fetchDiskFailPeople(diskId) } /** 确认失败人员已完成发放 */ export function confirmDiskFailPaid(failPersonId: string) { if (useMock()) { const row = mockDiskFailPeople.find((p) => p.id === failPersonId) if (!row) return delay({ ok: false as const, message: '未找到该失败人员记录' }) if (row.payStatus === '已确认发放') { return delay({ ok: false as const, message: '该人员已确认发放' }) } row.payStatus = '已确认发放' return delay({ ok: true as const }) } return real.confirmDiskFailPaid(failPersonId) } /** @deprecated 兼容旧调用,等同 confirmReturnDisk */ export function importReturnDisk() { return confirmReturnDisk() } /** 确认进入下月周期 */ export function archiveToNextMonth(id?: string) { if (useMock()) { const row = (id && mockDisks.find((d) => d.id === id)) || mockDisks.find((d) => d.status === '已回盘') if (row) { row.status = '已归档' const monthly = mockMonthlyBatches.find( (m) => m.batchNo === row.batchNo && m.town === row.town, ) if (monthly) monthly.status = '已归档' const batches = mockVillageBatches.filter( (b) => b.batchNo === row.batchNo && mTownVillageMatch(b, row), ) for (const b of batches) { if (b.stage === '回盘信息确认') b.stage = '已归档' } } return delay({ ok: true }) } return real.archiveToNextMonth(id) } function mTownVillageMatch( b: { town: string; village?: string }, row: { town: string; village?: string }, ) { return b.town === row.town && (!row.village || b.village === row.village) } /* ========== 告警 ========== */ export function fetchAlertsPage(params?: { town?: string village?: string pageNum?: number pageSize?: number }) { if (useMock()) { let list = [...mockAlerts] if (params?.town) list = list.filter((a) => a.town === params.town) if (params?.village) list = list.filter((a) => a.village === params.village) return mockPaged(list, params?.pageNum, params?.pageSize) } return real.fetchAlertsPage(params) } export function fetchAlerts() { if (useMock()) return delay([...mockAlerts]) return real.fetchAlerts() } export function handleAlert(payload: HandleAlertPayload | string, result?: string) { if (useMock()) { // 兼容旧调用:handleAlert(id, result) const data: HandleAlertPayload = typeof payload === 'string' ? { id: payload, personName: '', idCard: '', town: '', village: '', bankCard: '', bankName: '', socialInsuranceCode: '', heirName: '', heirIdCard: '', heirBankCard: '', heirContact: '', result, } : payload const a = mockAlerts.find((x) => x.id === data.id) if (a) { const oldName = a.personName if (data.personName) a.personName = data.personName if (data.town) a.town = data.town if (data.village) a.village = data.village a.idCard = data.idCard || a.idCard a.bankCard = data.bankCard || a.bankCard a.bankName = data.bankName || a.bankName a.socialInsuranceCode = data.socialInsuranceCode || a.socialInsuranceCode a.heirName = data.heirName a.heirIdCard = data.heirIdCard a.heirBankCard = data.heirBankCard a.heirContact = data.heirContact a.status = '已关闭' a.result = data.result || (a.type === '民政死亡比对' ? `已登记继承人 ${data.heirName || '-'},完成死亡状态更新并停止发放` : data.heirName ? `已核实人员信息并登记继承人 ${data.heirName}` : '已核实并处理人员信息') a.remarks = data.remarks || a.remarks a.fileUrls = data.fileUrls || a.fileUrls const prev = Array.isArray(a.lifecycle) ? a.lifecycle : [] a.lifecycle = [ ...prev, { actionLabel: '关闭告警', status: '已关闭', operator: getAppConfig().roles.find((r) => r.key === getAppConfig().currentRole)?.label || '业务部门', result: a.result, remark: data.remarks, fileUrls: data.fileUrls, operateTime: nowStr(), }, ] // 同步更新关联人员档案(按原姓名+街镇+村居匹配) const person = mockPeople.find( (p) => (p.name === oldName || p.name === data.personName) && p.town === (data.town || a.town) && p.village === (data.village || a.village), ) if (person) { if (data.personName) person.name = data.personName if (data.idCard) person.idCard = data.idCard if (data.bankCard) person.bankCard = data.bankCard if (data.bankName) person.bankName = data.bankName if (data.socialInsuranceCode !== undefined) { person.socialInsuranceCode = data.socialInsuranceCode } if (data.town) person.town = data.town if (data.village) person.village = data.village if (a.type === '民政死亡比对') person.status = '死亡丧葬' if (String(a.type || '').includes('退养资格驳回')) { person.status = '待提交资格' person.bizStatus = 'DRAFT' person.retireQualifyType = undefined } } const role = getAppConfig().roles.find((r) => r.key === getAppConfig().currentRole) || getAppConfig().roles[0] mockLogs.unshift({ id: mockLogs.length ? Math.max(...mockLogs.map((l) => l.id)) + 1 : 1, time: nowStr(), user: role.label, role: role.label, module: '监控告警', action: `处理告警 ${a.id}(${a.town}${a.village}·${a.personName}${data.heirName ? `,继承人${data.heirName}` : ''})`, result: '成功(村/镇告警状态已同步)', }) } return delay({ ok: true }) } return real.handleAlert(payload, result) } /* ========== 查询统计 ========== */ export function fetchReportBatches() { if (useMock()) return delay([...mockVillageBatches]) return real.fetchReportBatches() } export function fetchWorkbenchStats(params?: { payMonth?: string town?: string village?: string months?: number roleKey?: string }) { if (useMock()) { return delay({ personnelCount: mockPeople.length, withLandCount: mockPeople.filter((p) => p.retireQualifyType === '有土地退养').length, withoutLandCount: mockPeople.filter((p) => p.retireQualifyType === '无土地有资格').length, qualifiedCount: mockPeople.length, monthAmount: 0, townQualify: [], villageCharts: [], }) } return real.fetchWorkbenchStats(params) } /* ========== 系统管理 ========== */ export function fetchAccounts() { if (useMock()) return delay([...mockAccounts]) return real.fetchAccounts() } export function fetchOpLogs() { if (useMock()) return delay([...mockLogs]) return real.fetchOpLogs() } export function fetchOpLogsPage(params?: { pageNum?: number pageSize?: number operator?: string operatorRole?: string bizType?: string action?: string result?: string operateTimeFrom?: number operateTimeTo?: number }) { const pageNum = params?.pageNum || 1 const pageSize = params?.pageSize || 10 if (useMock()) { let list = [...mockLogs] const op = params?.operator?.trim() if (op) list = list.filter((r) => r.user.includes(op) || r.role.includes(op)) if (params?.result) list = list.filter((r) => r.result.includes(params.result!)) if (params?.bizType) list = list.filter((r) => r.module.includes(params.bizType!)) if (params?.action) list = list.filter((r) => r.action.includes(params.action!)) const start = (pageNum - 1) * pageSize return delay({ list: list.slice(start, start + pageSize), total: list.length, pageNum, pageSize, }) } return real.fetchOpLogsPage(params) } export function factoryResetBusinessData() { if (useMock()) return delay({ ok: true as const, destroyedTotal: 0, detail: {}, message: 'mock 跳过' }) return real.factoryResetBusinessData() } /** —— 系统管理员运维菜单 —— */ export function listRegionsAdmin(params?: { level?: string; parentId?: string }) { if (useMock()) return delay([]) return real.listRegionsAdmin(params) } export function saveRegionAdmin(payload: { id?: string code: string name: string level: string parentId?: string entrustPlanCode?: string defaultBank?: string enabled?: boolean remarks?: string districtSharePercent?: number townSharePercent?: number }) { if (useMock()) return delay({ ok: true as const }) return real.saveRegionAdmin(payload) } export function deleteRegionAdmin(id: string) { if (useMock()) return delay({ ok: true as const }) return real.deleteRegionAdmin(id) } export function pageAdminBatches(params?: { payMonth?: string townId?: string villageId?: string batchLevel?: string status?: string pageNum?: number pageSize?: number }) { if (useMock()) return delay({ list: [], total: 0, pageNum: 1, pageSize: 10 }) return real.pageAdminBatches(params) } export function purgeBatchAdmin(batchId: string) { if (useMock()) return delay({ ok: true as const }) return real.purgeBatchAdmin(batchId) } export function deletePersonnelAdmin(personnelId: string) { if (useMock()) return delay({ ok: true as const }) return real.deletePersonnelAdmin(personnelId) } export function deletePersonnelBatchAdmin(ids: string[]) { if (useMock()) return delay({ ok: true as const, successCount: ids.length, failCount: 0, message: `已删除 ${ids.length} 人` }) return real.deletePersonnelBatchAdmin(ids) } export function clearAllPersonnelAdmin() { if (useMock()) return delay({ ok: true as const, successCount: 0, failCount: 0, message: '已清空 0 人' }) return real.clearAllPersonnelAdmin() } export function updatePersonnelStatusAdmin(payload: { personnelId: string bizStatus: string payThisMonth?: boolean remarks?: string }) { if (useMock()) return delay({ ok: true as const }) return real.updatePersonnelStatusAdmin(payload) } export function initPersonnelStatusAdmin(payload: { bizStatus: string insuranceMatchResult: string retireQualifyType?: string payThisMonth?: boolean townId?: string villageId?: string }) { if (useMock()) { return delay({ ok: true as const, updatedCount: 0, skippedArchived: 0, skippedSame: 0, failCount: 0, message: '已初始化(演示)', }) } return real.initPersonnelStatusAdmin(payload) } /* ========== 核算 / 退款 / 报表 / 运维扩展 ========== */ export function snapshotCalcRound(batchId: string) { if (useMock()) return delay({ ok: true }) return real.snapshotCalcRound(batchId) } export function listCalcRounds(batchId: string) { if (useMock()) return delay([] as Record[]) return real.listCalcRounds(batchId) } export function lockCalc(batchId: string, locked: boolean) { if (useMock()) return delay({ ok: true, locked }) return real.lockCalc(batchId, locked) } export function advanceRefundProgress(id: string, stage: string) { if (useMock()) return delay({ ok: true }) return real.advanceRefundProgress(id, stage) } export function fetchRefundStats() { if (useMock()) return delay({ total: 0, byStage: {} }) return real.fetchRefundStats() } export function fetchReportTopic( type: real.ReportTopicType, params?: { townId?: string; payMonth?: string; bizType?: string }, ) { if (useMock()) return delay([] as Record[]) return real.fetchReportTopic(type, params) } export function exportReportSummary(payMonth?: string) { if (useMock()) { ElMessage.info('演示模式:请切换联调后导出') return delay({ ok: false as const }) } return real.exportReportSummary(payMonth) } export function fetchYoy(payMonth?: string) { if (useMock()) return delay({} as Record) return real.fetchYoy(payMonth) } export function downloadStoredFile(fileUrl: string, fileName?: string) { if (useMock()) { ElMessage.info('演示模式:请切换联调后下载') return delay(undefined) } return real.downloadStoredFile(fileUrl, fileName) } export function saveAccount(payload: { username: string password?: string displayName?: string role?: string }) { if (useMock()) return delay({ ok: true, id: String(Date.now()) }) return real.saveAccount(payload) } export function setAccountStatus(id: string, enabled: boolean) { if (useMock()) return delay({ ok: true }) return real.setAccountStatus(id, enabled) } export function resetAccountPassword(id: string, password?: string) { if (useMock()) return delay({ ok: true }) return real.resetAccountPassword(id, password) } export function fetchLoginLogs(limit = 100) { if (useMock()) return delay([] as OpLog[]) return real.fetchLoginLogs(limit) } export function fetchLoginLogsPage(params?: { pageNum?: number; pageSize?: number }) { const pageNum = params?.pageNum || 1 const pageSize = params?.pageSize || 10 if (useMock()) { return delay({ list: [] as OpLog[], total: 0, pageNum, pageSize }) } return real.fetchLoginLogsPage(params) } export function recordLoginAudit(payload: { username?: string roleKey?: string roleLabel?: string ip?: string result?: string }) { if (useMock()) return delay({ ok: true as const }) return real.recordLoginAudit(payload) } export function exportProcessTimeline(params: { batchId?: string; personnelId?: string }) { if (useMock()) { ElMessage.info('演示模式:请切换联调后导出') return delay({ ok: false as const }) } return real.exportProcessTimeline(params) } export function fetchBatchProgress(batchId: string) { if (useMock()) return delay([] as Array>) return real.fetchBatchProgress(batchId) } export function exportOpLogs() { if (useMock()) { ElMessage.info('演示模式:请切换联调后导出') return delay({ ok: false as const }) } return real.exportOpLogs() } export function cleanupOpLogs(days = 90) { if (useMock()) return delay({ ok: true, removed: 0, days }) return real.cleanupOpLogs(days) } export function fetchValidateRules() { if (useMock()) { return delay({ idCardValidate: true, bankCardRequired: true, deathStillPayScan: true, }) } return real.fetchValidateRules() } export function saveValidateRules(rules: Record) { if (useMock()) return delay({ ok: true }) return real.saveValidateRules(rules) } export function fetchStandardPendingTip(town?: string) { if (useMock()) return delay('') return real.fetchStandardPendingTip(town) }