import express from 'express' import { coreEnterprises, customerManagers, dictTypes, dictItems } from '../db/seedBaseConfig.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) // ---------------- 核心企业(渠道) ---------------- // 2026-07-20:后端核心企业接口真实字段为 channelName/channelNo(渠道名称/渠道编号), // 与此前依据的旧版 swagger 快照(enterpriseName/enterpriseCode)不同;此处兼容新旧两种字段名, // 便于本地演示环境同时兼容历史数据。 router.post('/core-enterprise/page', (req, res) => { const { channelName, channelNo, enterpriseName, enterpriseCode, organizationId, page, pageSize } = req.body || {} const nameKeyword = channelName || enterpriseName const noKeyword = channelNo || enterpriseCode let list = coreEnterprises if (nameKeyword) list = list.filter((e) => (e.channelName || e.enterpriseName)?.includes(nameKeyword)) if (noKeyword) list = list.filter((e) => (e.channelNo || e.enterpriseCode)?.includes(noKeyword)) if (organizationId) list = list.filter((e) => e.organizationId === organizationId) sendOk(res, paginate(list, page, pageSize)) }) router.post('/core-enterprise/create', (req, res) => { const body = req.body || {} const channelName = body.channelName || body.enterpriseName if (!channelName) return sendFail(res, '渠道名称不能为空', 400) if (!(body.channelNo || body.enterpriseCode)) return sendFail(res, '渠道编号不能为空', 400) const id = nextId('coreEnterprise') const entity = { id, createdAt: new Date().toISOString().slice(0, 19).replace('T', ' '), ...body } coreEnterprises.push(entity) sendOk(res, entity, '创建成功') }) router.post('/core-enterprise/update', (req, res) => { const { id, ...rest } = req.body || {} const entity = coreEnterprises.find((e) => e.id === id) if (!entity) return sendFail(res, '核心企业不存在', 404) Object.assign(entity, rest) sendOk(res, true, '更新成功') }) router.post('/core-enterprise/delete', (req, res) => { const { id } = req.body || {} const index = coreEnterprises.findIndex((e) => e.id === id) if (index === -1) return sendFail(res, '核心企业不存在', 404) coreEnterprises.splice(index, 1) sendOk(res, true, '删除成功') }) // ---------------- 客户经理 ---------------- router.post('/customer-manager/page', (req, res) => { const { managerName, page, pageSize } = req.body || {} let list = customerManagers if (managerName) list = list.filter((m) => m.managerName.includes(managerName)) sendOk(res, paginate(list, page, pageSize)) }) router.post('/customer-manager/create', (req, res) => { const body = req.body || {} if (!body.managerName) return sendFail(res, '姓名不能为空', 400) const id = nextId('customerManager') const entity = { id, status: 'ACTIVE', remark: '', ...body } customerManagers.push(entity) sendOk(res, entity, '创建成功') }) router.post('/customer-manager/update', (req, res) => { const { id, ...rest } = req.body || {} const entity = customerManagers.find((m) => m.id === id) if (!entity) return sendFail(res, '客户经理不存在', 404) Object.assign(entity, rest) sendOk(res, true, '更新成功') }) router.post('/customer-manager/delete', (req, res) => { const { id } = req.body || {} const index = customerManagers.findIndex((m) => m.id === id) if (index === -1) return sendFail(res, '客户经理不存在', 404) customerManagers.splice(index, 1) sendOk(res, true, '删除成功') }) // ---------------- 动态字典管理:字典类型 ---------------- // 2026-07-31:已按最新 swagger(swagger_project_scfs_2026-07-31_09-25-57.json,dict_management // 分组)核对确认字典类型管理全部 4 个接口的真实路径与字段,不再是前端占位契约: // POST /api/dict-management/dict-type/page FindDictTypePageQto{dictTypeCodeLike, dictTypeNameLike, dictCategoryList[], statusList[], page, pageSize} // POST /api/dict-management/dict-type/create CreateDictTypeBto{dictTypeCode, dictTypeName, dictCategory, remark, status} // POST /api/dict-management/dict-type/update UpdateDictTypeBto{id, dictTypeCode, dictTypeName, status, remark}(不含 dictCategory) // POST /api/dict-management/dict-type/delete DeleteDictTypeBto{id, dictTypeCode} // dictCategory: NORMAL/CASCADE,status: ENABLED/DISABLED。 router.post('/dict-management/dict-type/page', (req, res) => { const { dictTypeCodeLike, dictTypeNameLike, dictCategoryList, statusList, page, pageSize } = req.body || {} let list = dictTypes if (dictTypeCodeLike) list = list.filter((d) => d.dictTypeCode.includes(dictTypeCodeLike)) if (dictTypeNameLike) list = list.filter((d) => d.dictTypeName.includes(dictTypeNameLike)) if (dictCategoryList?.length) list = list.filter((d) => dictCategoryList.includes(d.dictCategory)) if (statusList?.length) list = list.filter((d) => statusList.includes(d.status)) sendOk(res, paginate(list, page, pageSize)) }) router.post('/dict-management/dict-type/create', (req, res) => { const { dictCategory, dictTypeCode, dictTypeName, status, remark } = req.body || {} if (!dictTypeCode) return sendFail(res, '字典编码不能为空', 400) if (!dictTypeName) return sendFail(res, '字典名称不能为空', 400) if (!['NORMAL', 'CASCADE'].includes(dictCategory)) return sendFail(res, '字典类别不合法', 400) if (dictTypes.some((d) => d.dictTypeCode === dictTypeCode)) return sendFail(res, '字典编码已存在', 400) const id = nextId('dictType') const entity = { id, dictCategory, dictTypeCode, dictTypeName, status: status || 'ENABLED', remark: remark || '', createdAt: new Date().toISOString().slice(0, 19).replace('T', ' ') } dictTypes.push(entity) sendOk(res, entity, '创建成功') }) router.post('/dict-management/dict-type/update', (req, res) => { const { id, dictTypeCode, dictTypeName, status, remark } = req.body || {} const entity = dictTypes.find((d) => d.id === id) if (!entity) return sendFail(res, '字典类型不存在', 404) if (dictTypeCode !== undefined) entity.dictTypeCode = dictTypeCode if (dictTypeName !== undefined) entity.dictTypeName = dictTypeName if (status !== undefined) entity.status = status if (remark !== undefined) entity.remark = remark sendOk(res, entity, '更新成功') }) router.post('/dict-management/dict-type/delete', (req, res) => { const { id } = req.body || {} const index = dictTypes.findIndex((d) => d.id === id) if (index === -1) return sendFail(res, '字典类型不存在', 404) const hasItems = dictItems.some((it) => it.dictTypeCode === dictTypes[index].dictTypeCode) if (hasItems) return sendFail(res, '该字典类型下还有字典项,不可删除', 400) dictTypes.splice(index, 1) sendOk(res, true, '删除成功') }) // ---------------- 动态字典管理:字典项 ---------------- // 2026-07-31:已按最新 swagger(dict_management 分组)核对确认路径与字段。真实后端的只读接口 // (dict-items/list、dict-items/tree)实测会返回 id/sortOrder(swagger 未完整声明,以实测为准), // 前端(DictItemTree.vue)已据此直接支持对已存在字典项的修改/删除/子级新增,mock 同步在这两个 // 只读接口的响应里附带 id,贴近真实行为。 function toOptionVo(it) { return { id: it.id, itemValue: it.itemValue, itemLabel: it.itemLabel, sortOrder: it.sortOrder } } function buildItemsTree(dictTypeCode) { const sorted = dictItems .filter((it) => it.dictTypeCode === dictTypeCode && it.status === 'ENABLED') .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)) const nodeMap = new Map( sorted.map((it) => [it.id, { id: it.id, itemValue: it.itemValue, itemLabel: it.itemLabel, sortOrder: it.sortOrder, children: [] }]) ) const idByItemValue = new Map(sorted.map((it) => [it.id, it])) const roots = [] nodeMap.forEach((node, id) => { const source = idByItemValue.get(id) if (source.parentId && nodeMap.has(source.parentId)) { nodeMap.get(source.parentId).children.push(node) } else { roots.push(node) } }) return roots } router.post('/dict-management/dict-items/list', (req, res) => { const { dictTypeCode } = req.body || {} if (!dictTypeCode) return sendFail(res, '字典类型编码不能为空', 400) const list = dictItems .filter((it) => it.dictTypeCode === dictTypeCode && it.status === 'ENABLED') .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)) .map(toOptionVo) sendOk(res, list) }) router.post('/dict-management/dict-items/tree', (req, res) => { const { dictTypeCode } = req.body || {} if (!dictTypeCode) return sendFail(res, '字典类型编码不能为空', 400) sendOk(res, buildItemsTree(dictTypeCode)) }) router.post('/dict-management/dict-items/children', (req, res) => { const { dictTypeCode, parentId } = req.body || {} if (!dictTypeCode) return sendFail(res, '字典类型编码不能为空', 400) const list = dictItems .filter((it) => it.dictTypeCode === dictTypeCode && it.status === 'ENABLED' && it.parentId === (parentId ?? null)) .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)) .map(toOptionVo) sendOk(res, list) }) router.post('/dict-management/dict-item/create', (req, res) => { const { dictTypeCode, dictItemBtoList } = req.body || {} if (!dictTypeCode) return sendFail(res, '字典类型编码不能为空', 400) const item = (dictItemBtoList || [])[0] if (!item) return sendFail(res, '字典项数据不能为空', 400) const { itemValue, itemLabel, parentId, sortOrder, remark } = item if (!itemValue) return sendFail(res, '字典项值不能为空', 400) if (!itemLabel) return sendFail(res, '字典项标签不能为空', 400) if (dictItems.some((it) => it.dictTypeCode === dictTypeCode && it.itemValue === itemValue)) { return sendFail(res, '字典项值已存在', 400) } const id = nextId('dictItem') const entity = { id, dictTypeCode, itemValue, itemLabel, parentId: parentId ?? null, sortOrder: sortOrder ?? 0, status: 'ENABLED', remark: remark || '' } dictItems.push(entity) sendOk(res, entity, '创建成功') }) router.post('/dict-management/dict-item/update', (req, res) => { const { id, itemValue, itemLabel, parentId, sortOrder, status, remark } = req.body || {} const entity = dictItems.find((it) => it.id === id) if (!entity) return sendFail(res, '字典项不存在', 404) if (itemValue !== undefined) entity.itemValue = itemValue if (itemLabel !== undefined) entity.itemLabel = itemLabel if (parentId !== undefined) entity.parentId = parentId if (sortOrder !== undefined) entity.sortOrder = sortOrder if (status !== undefined) entity.status = status if (remark !== undefined) entity.remark = remark sendOk(res, entity, '更新成功') }) router.post('/dict-management/dict-item/delete', (req, res) => { const { id } = req.body || {} const index = dictItems.findIndex((it) => it.id === id) if (index === -1) return sendFail(res, '字典项不存在', 404) const hasChildren = dictItems.some((it) => it.parentId === id) if (hasChildren) return sendFail(res, '存在子级字典项,不可删除', 400) dictItems.splice(index, 1) sendOk(res, true, '删除成功') }) export default router