SFT/mock-server/routes/baseConfig.js

250 lines
11 KiB
JavaScript

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