import express from 'express' import crypto from 'crypto' import { orgs, nextOrgId, buildOrgTree, collectDescendantIds } from '../data/orgs.js' import { users, nextUserId } from '../data/users.js' import { roles } from '../data/roles.js' import { requireAuth } from '../middleware/requireAuth.js' const router = express.Router() router.use(requireAuth) function orgName(orgId) { return orgs.find((o) => o.id === orgId)?.name || '' } function roleNames(roleIds) { return (roleIds || []).map((id) => roles.find((r) => r.id === id)?.name).filter(Boolean) } function nowStr() { return new Date().toISOString().slice(0, 19).replace('T', ' ') } function genInitialPassword() { const upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ' const lower = 'abcdefghijkmnpqrstuvwxyz' const digit = '0123456789' const all = upper + lower + digit let pwd = upper[Math.floor(Math.random() * upper.length)] + digit[Math.floor(Math.random() * digit.length)] for (let i = 0; i < 6; i += 1) pwd += all[Math.floor(Math.random() * all.length)] return pwd } // ---------------- 机构管理 ---------------- router.get('/org/list', (req, res) => { const { name, id } = req.query || {} let list = orgs if (name || id) { const matched = orgs.filter( (o) => (name && o.name.includes(name)) || (id && o.id.includes(id)) ) const idSet = new Set() matched.forEach((m) => { let cur = m while (cur) { idSet.add(cur.id) cur = orgs.find((o) => o.id === cur.parentId) } }) list = orgs.filter((o) => idSet.has(o.id)) } res.json({ code: 200, msg: 'ok', data: buildOrgTree(list) }) }) router.post('/org', (req, res) => { const { name, parentId, remark } = req.body || {} if (!name) return res.json({ code: 40004, msg: '机构名称不能为空', data: null }) if (!parentId) return res.json({ code: 40004, msg: '请选择上级机构', data: null }) const parent = orgs.find((o) => o.id === parentId) if (!parent) return res.json({ code: 40004, msg: '上级机构不存在', data: null }) const duplicate = orgs.some((o) => o.parentId === parentId && o.name === name) if (duplicate) return res.json({ code: 40005, msg: '同一上级机构下机构名称不可重复', data: null }) const org = { id: nextOrgId(), name, parentId, level: parent.level + 1, remark: remark || '', builtin: false, createdAt: new Date().toISOString().slice(0, 19).replace('T', ' ') } orgs.push(org) res.json({ code: 200, msg: 'ok', data: org }) }) router.put('/org/:id', (req, res) => { const { id } = req.params const { name, parentId, remark } = req.body || {} const org = orgs.find((o) => o.id === id) if (!org) return res.json({ code: 40404, msg: '机构不存在', data: null }) if (!name) return res.json({ code: 40004, msg: '机构名称不能为空', data: null }) let nextParentId = org.parentId let level = org.level if (org.parentId !== null) { if (!parentId) return res.json({ code: 40004, msg: '请选择上级机构', data: null }) if (parentId === id) return res.json({ code: 40006, msg: '上级机构不可选择自身', data: null }) const descendantIds = collectDescendantIds(orgs, id) if (descendantIds.includes(parentId)) { return res.json({ code: 40006, msg: '上级机构不可选择自身下级', data: null }) } const parent = orgs.find((o) => o.id === parentId) if (!parent) return res.json({ code: 40004, msg: '上级机构不存在', data: null }) nextParentId = parentId level = parent.level + 1 } const duplicate = orgs.some((o) => o.id !== id && o.parentId === nextParentId && o.name === name) if (duplicate) return res.json({ code: 40005, msg: '同一上级机构下机构名称不可重复', data: null }) org.name = name org.parentId = nextParentId org.level = level org.remark = remark || '' res.json({ code: 200, msg: 'ok', data: org }) }) router.delete('/org/:id', (req, res) => { const { id } = req.params const index = orgs.findIndex((o) => o.id === id) if (index === -1) return res.json({ code: 40404, msg: '机构不存在', data: null }) const org = orgs[index] if (org.builtin) return res.json({ code: 40007, msg: '内置一级机构不可删除', data: null }) const hasChildren = orgs.some((o) => o.parentId === id) if (hasChildren) return res.json({ code: 40008, msg: '该机构存在下级机构,不可删除', data: null }) const hasUsers = users.some((u) => u.orgId === id) if (hasUsers) return res.json({ code: 40009, msg: '该机构已关联用户,不可删除', data: null }) orgs.splice(index, 1) res.json({ code: 200, msg: 'ok', data: null }) }) // ---------------- 用户管理 ---------------- router.get('/user/list', (req, res) => { const { orgId, username, phone, status, roleId, page = 1, pageSize = 10 } = req.query || {} let list = users if (orgId) list = list.filter((u) => u.orgId === orgId) if (username) list = list.filter((u) => u.username.includes(username)) if (phone) list = list.filter((u) => u.phone.includes(phone)) if (status) list = list.filter((u) => u.status === status) if (roleId) list = list.filter((u) => u.roleIds.includes(roleId)) const total = list.length const start = (Number(page) - 1) * Number(pageSize) const pageList = list.slice(start, start + Number(pageSize)).map((u) => ({ id: u.id, username: u.username, realName: u.realName, phone: u.phone, orgId: u.orgId, orgName: orgName(u.orgId), roleIds: u.roleIds, roleNames: roleNames(u.roleIds), status: u.status, createdAt: u.createdAt, lastLoginAt: u.lastLoginAt })) res.json({ code: 200, msg: 'ok', data: { list: pageList, total } }) }) router.post('/user', (req, res) => { const { username, phone, realName, orgId, roleIds } = req.body || {} if (!username) return res.json({ code: 40010, msg: '请输入用户名', data: null }) if (!/^1\d{10}$/.test(phone || '')) return res.json({ code: 40011, msg: '请输入正确的11位手机号', data: null }) if (users.some((u) => u.phone === phone)) return res.json({ code: 40012, msg: '该手机号已被注册', data: null }) if (!orgId || !orgs.some((o) => o.id === orgId)) return res.json({ code: 40013, msg: '请选择有效的所属机构', data: null }) if (!roleIds?.length) return res.json({ code: 40014, msg: '请选择用户角色', data: null }) const initialPassword = genInitialPassword() const user = { id: nextUserId(), username, password: initialPassword, realName: realName || '', phone, orgId, roleIds, status: 'enabled', failCount: 0, mustChangePassword: true, lastLoginAt: '', createdAt: nowStr() } users.push(user) res.json({ code: 200, msg: 'ok', data: { id: user.id, initialPassword } }) }) router.put('/user/:id', (req, res) => { const { id } = req.params const { phone, realName, orgId, roleIds } = req.body || {} const user = users.find((u) => u.id === id) if (!user) return res.json({ code: 40404, msg: '用户不存在', data: null }) if (!/^1\d{10}$/.test(phone || '')) return res.json({ code: 40011, msg: '请输入正确的11位手机号', data: null }) if (users.some((u) => u.id !== id && u.phone === phone)) { return res.json({ code: 40012, msg: '该手机号已被注册', data: null }) } if (!orgId || !orgs.some((o) => o.id === orgId)) return res.json({ code: 40013, msg: '请选择有效的所属机构', data: null }) if (!roleIds?.length) return res.json({ code: 40014, msg: '请选择用户角色', data: null }) user.phone = phone user.realName = realName || '' user.orgId = orgId user.roleIds = roleIds res.json({ code: 200, msg: 'ok', data: null }) }) router.delete('/user/:id', (req, res) => { const { id } = req.params const index = users.findIndex((u) => u.id === id) if (index === -1) return res.json({ code: 40404, msg: '用户不存在', data: null }) users.splice(index, 1) res.json({ code: 200, msg: 'ok', data: null }) }) router.put('/user/:id/lock', (req, res) => { const user = users.find((u) => u.id === req.params.id) if (!user) return res.json({ code: 40404, msg: '用户不存在', data: null }) user.status = 'locked' res.json({ code: 200, msg: 'ok', data: null }) }) router.put('/user/:id/unlock', (req, res) => { const user = users.find((u) => u.id === req.params.id) if (!user) return res.json({ code: 40404, msg: '用户不存在', data: null }) user.status = 'enabled' user.failCount = 0 res.json({ code: 200, msg: 'ok', data: null }) }) router.put('/user/:id/password', (req, res) => { const { newPassword } = req.body || {} const user = users.find((u) => u.id === req.params.id) if (!user) return res.json({ code: 40404, msg: '用户不存在', data: null }) const strongEnough = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$/.test(newPassword || '') if (!strongEnough) return res.json({ code: 40015, msg: '密码需8位以上,且包含大小写字母和数字', data: null }) user.password = newPassword user.mustChangePassword = false res.json({ code: 200, msg: 'ok', data: null }) }) router.put('/user/:id/password/reset', (req, res) => { const user = users.find((u) => u.id === req.params.id) if (!user) return res.json({ code: 40404, msg: '用户不存在', data: null }) user.password = '123456' user.mustChangePassword = true console.log(`[mock-server] 密码已重置,短信通知已发送至 ${user.phone}: 初始密码 123456`) res.json({ code: 200, msg: 'ok', data: null }) }) // ---------------- 角色管理(简版,供用户管理下拉使用;完整 CRUD 见后续路由) ---------------- router.get('/role/list', (req, res) => { const { status } = req.query || {} let list = roles if (status) list = list.filter((r) => r.status === status) res.json({ code: 200, msg: 'ok', data: list.map((r) => ({ id: r.id, name: r.name, orgId: r.orgId, status: r.status, builtin: r.builtin })) }) }) export default router