import express from 'express' import { individualCustomers, enterpriseCustomers, enterpriseRelatedPersons } from '../db/seedCustomer.js' import { nextId } from '../utils/id.js' import { paginate } from '../utils/pagination.js' import { sendOk, sendFail } from '../utils/response.js' import { requireAuth } from '../middleware/requireAuth.js' const router = express.Router() router.use(requireAuth) function toIndividualListVo(c) { return { id: c.id, organizationId: c.organizationId, customerName: c.customerName, certificateType: c.certificateType, certificateNumber: c.certificateNumber, mobilePhone: c.mobilePhone, ocrStatus: c.ocrStatus, customerCode: c.customerCode } } // ---------------- 个人客户 ---------------- router.post('/customer-info/personal/list', (req, res) => { const { organizationIdEq, customerCodeEq, customerNameLike, certificateNumberEq, page, pageSize } = req.body || {} let list = individualCustomers if (organizationIdEq) list = list.filter((c) => c.organizationId === organizationIdEq) if (customerCodeEq) list = list.filter((c) => c.customerCode === customerCodeEq) if (customerNameLike) list = list.filter((c) => c.customerName.includes(customerNameLike)) if (certificateNumberEq) list = list.filter((c) => c.certificateNumber === certificateNumberEq) const result = paginate(list, page, pageSize) sendOk(res, { ...result, records: result.records.map(toIndividualListVo) }) }) router.post('/customer-info/personal/detail', (req, res) => { const { id, customerCode } = req.body || {} const customer = individualCustomers.find((c) => (id ? c.id === id : c.customerCode === customerCode)) if (!customer) return sendFail(res, '个人客户不存在', 404) sendOk(res, customer) }) router.post('/customer-info/personal/create', (req, res) => { const body = req.body || {} if (!body.customerName) return sendFail(res, '客户名称不能为空', 400) const id = nextId('individualCustomer') const now = new Date().toISOString().slice(0, 19).replace('T', ' ') const customer = { id, customerCode: `PC${String(id).padStart(4, '0')}`, creatorId: req.userId, createTime: now, updateTime: now, ...body } individualCustomers.push(customer) sendOk(res, id, '创建成功') }) router.post('/customer-info/personal/update', (req, res) => { const { id, ...rest } = req.body || {} const customer = individualCustomers.find((c) => c.id === id) if (!customer) return sendFail(res, '个人客户不存在', 404) Object.assign(customer, rest, { updateTime: new Date().toISOString().slice(0, 19).replace('T', ' ') }) sendOk(res, true, '更新成功') }) // OCR身份证识别:mock固定返回一份示例识别结果,不做真实图像识别 router.post('/customer-info/personal/ocr-idcard', (req, res) => { const { channelNo, fileBase64, fileType } = req.body || {} if (!channelNo || !fileBase64 || !fileType) return sendFail(res, 'channelNo/fileBase64/fileType不能为空', 400) sendOk(res, { name: '张三', sex: '男', nation: '汉', birth: '19900101', idcard: '310101199001010001', address: '上海市浦东新区世纪大道1号', authority: '上海市公安局浦东分局', validDate: '20200101-20400101' }) }) // 通用文件上传:2026-08-06 起移至不挂 requireAuth 的 mock-server/routes/uploadFile.js, // 与 face-recognize 同理——该接口同时被管理后台(已登录)与 fryl_h5 个人开户移动端H5"相关协议" // 步骤(匿名,用于上传填好客户信息的《用户支付服务协议》PDF)调用,若挂在本文件会被 // requireAuth 拦截H5侧的匿名请求返回401。详见 uploadFile.js 顶部注释。 // ---------------- 企业客户 ---------------- function toEnterpriseVo(e) { return { id: e.id, organizationId: e.organizationId, enterpriseName: e.enterpriseName, businessLicense: e.businessLicense, certificateType: e.certificateType, certificateNumber: e.certificateNumber, certificateEffectiveDate: e.certificateEffectiveDate, certificateExpiryDate: e.certificateExpiryDate, mobilePhone: e.mobilePhone, certificateAddress: e.certificateAddress, businessScope: e.businessScope, enterpriseCode: e.enterpriseCode, ocrStatus: e.ocrStatus, createTime: e.createTime } } router.post('/enterprise-customer/page', (req, res) => { const { enterpriseName, businessLicense, mobilePhone, page, pageSize } = req.body || {} let list = enterpriseCustomers if (enterpriseName) list = list.filter((e) => e.enterpriseName.includes(enterpriseName)) if (businessLicense) list = list.filter((e) => e.businessLicense === businessLicense) if (mobilePhone) list = list.filter((e) => e.mobilePhone === mobilePhone) const result = paginate(list, page, pageSize) sendOk(res, { ...result, records: result.records.map(toEnterpriseVo) }) }) router.post('/enterprise-customer/detail', (req, res) => { const { id } = req.body || {} const enterprise = enterpriseCustomers.find((e) => e.id === id) if (!enterprise) return sendFail(res, '企业客户不存在', 404) sendOk(res, { ...enterprise, legalRepresentativeId: enterprise.legalRepresentativeId }) }) router.post('/enterprise-customer/create', (req, res) => { const { createEnterpriseCustomerBto, enableOcr } = req.body || {} if (!createEnterpriseCustomerBto || !createEnterpriseCustomerBto.enterpriseName) { return sendFail(res, '企业名称不能为空', 400) } const { enterpriseRelatedPersonBtoList, ...enterpriseFields } = createEnterpriseCustomerBto const id = nextId('enterpriseCustomer') const now = new Date().toISOString().slice(0, 19).replace('T', ' ') const enterprise = { id, enterpriseCode: `EC${String(id).padStart(4, '0')}`, ocrStatus: enableOcr ? 'SUCCESS' : 'PENDING', creatorId: req.userId, createTime: now, ...enterpriseFields } enterpriseCustomers.push(enterprise) ;(enterpriseRelatedPersonBtoList || []).forEach((p) => { enterpriseRelatedPersons.push({ id: nextId('enterpriseRelatedPerson'), enterpriseId: id, ...p }) }) sendOk(res, { id }, '创建成功') }) router.post('/enterprise-customer/update', (req, res) => { const { id, ...rest } = req.body || {} const enterprise = enterpriseCustomers.find((e) => e.id === id) if (!enterprise) return sendFail(res, '企业客户不存在', 404) Object.assign(enterprise, rest) sendOk(res, true, '更新成功') }) export default router