# 鑫潼智电系统 · 客户管理核心模块 (V1.0) - Product Requirement Document ## Overview - **Summary**: 基于数据库设计文档 D2 客户域,实现「客户信息 + 客户部门 + 客户访问账号」三个核心子模块的完整前后端功能,覆盖 CRUD、分页搜索、数据权限隔离、基础安全防护,并兼容业务员移动端 APP(复用同一套 REST API 和 sys_user 账号体系)。 - **Purpose**: 为业务员和运营管理员提供结构化的客户档案管理能力,是后续项目管理、派单管理、设备管理、用电统计等所有下游业务模块的数据基础。 - **Target Users**: 运营管理员(ADMIN 角色,全量数据)、业务员(SALESMAN 角色,仅本人客户数据)、业务员 APP(同 SALESMAN 权限,移动端使用)。 ## Goals - 实现客户信息(customer)完整 CRUD,含业务编号自动生成、省市区地址、状态流转、负责业务员分配 - 实现客户部门(customer_department)完整 CRUD,按客户归属过滤 - 实现客户访问账号(customer_account)完整 CRUD,按客户归属过滤,默认密码自动生成与 bcrypt 加密 - 接入现有 RBAC 鉴权体系:菜单/按钮权限 + 行级数据隔离(ADMIN 全量、SALESMAN 按 salesman_id 过滤) - 前端 UI 与现有用户管理页保持一致风格(搜索栏 + 表格 + 弹窗),省市区级联、表格内部滚动 - 接口防 SQL 注入(参数化查询),前端防 XSS(输入校验 + 避免 v-html),敏感字段脱敏 - 同一套 API 无差别兼容 Web 管理后台和业务员 APP ## Non-Goals (Out of Scope) - 客户拜访(customer_visit)、客户建筑(customer_building)、客户房间(customer_room)、客户设备(customer_equipment)四个子模块(后续迭代) - 共享电工 APP(WORKER 角色)和客户微信小程序(CUSTOMER 角色)的专属接口(文档确认本期不含) - 业务员 APP 的移动端 UI 页面(本期仅保证 API 可用,UI 由独立 uni-app 项目负责) - 客户状态驱动的自动流转、客户画像/报表分析(后续统计报表模块) - 文件上传(设备照片、合同文件)、短信通知初始密码、首次登录强制改密码 ## Background & Context - **现有架构**: 后端 Midway.js (Koa) + Sequelize-typescript + PostgreSQL;前端 Vue3 + Ant Design Vue 4 + Vite;鉴权 JWT + RBAC 中间件(`rbac.middleware.ts` 基于路径前缀匹配权限码) - **已有数据库种子**(`database/db_seed.sql`):4 个角色(ADMIN/SALESMAN/WORKER/CUSTOMER)、6 条顶级菜单(含「客户管理」parent_id=NULL id=1 permission=customer:list)、SALESMAN 角色已绑定客户/项目/统计报表 3 个顶级菜单 - **已有客户管理路由占位**(`client/src/router/index.ts`):`/customer` 路径指向 Placeholder.vue,permission=customer:list - **RBAC 中间件映射表**(`rbac.middleware.ts` PERMISSION_MAP):当前仅含 /api/users、/api/roles 等系统管理路由,需新增客户域 3 条映射 - **数据隔离要求**:SALESMAN 角色只能看到自己负责的客户;customer、customer_department、customer_account 三张表均需支持 salesman_id 行级过滤(customer_account 通过 customer_id → customer.salesman_id 关联) ## Functional Requirements ### FR-1:客户信息管理(customer) 系统 SHALL 提供客户信息的完整 CRUD 和查询能力: - 列表:分页(page/pageSize)、关键词搜索(客户名称/联系人/电话)、状态筛选(1意向/2签约/3运维中/4终止)、负责业务员筛选 - 字段:id、customer_no(自动生成,KH+YYYYMMDD+3位序号,UNIQUE)、customer_name(必填)、province/city/district(省市区级联)、address、contact_person、contact_phone、postal_code、fax、customer_level(学校/医院/企业等)、status(默认1意向)、salesman_id(关联 sys_user.id,允许 NULL 由 ADMIN 分配)、created_at、updated_at - 创建:必填校验、customer_no 自动生成、写入 salesman_id(若当前角色为 SALESMAN 则强制=当前用户ID) - 更新:字段白名单更新、customer_no 只读不可改 - 删除:硬删除,若存在关联 customer_department / customer_account 则提示禁止删除或级联删除(参照外键约束) - 列表返回不包含敏感字段(无密码类字段) ### FR-2:客户部门管理(customer_department) 系统 SHALL 提供客户部门的完整 CRUD,数据按所属客户过滤: - 列表:分页、关键词搜索(部门名称/联系人/电话)、按 customer_id 过滤(必选或必带权限过滤) - 字段:id、customer_id(FK customer,必填)、dept_name(必填)、contact_person、contact_phone、function_desc、created_at - 行级权限:SALESMAN 仅可操作自己客户下的部门(通过 customer.salesman_id 关联校验) - 删除:若存在关联 customer_room(虽本期不实现,但需保留外键约束友好提示) ### FR-3:客户访问账号管理(customer_account) 系统 SHALL 提供客户侧访问账号的完整 CRUD,数据按所属客户过滤: - 列表:分页、关键词搜索(电话)、按 customer_id 过滤、状态筛选(1启用/0禁用) - 字段:id、customer_id(FK customer,必填)、phone(UNIQUE,必填,登录账号)、password_hash(bcrypt)、wx_openid(本期可NULL)、status(默认1启用)、last_login_at、created_at - 创建:默认密码 = 手机号后6位(不足6位用 123456),自动 bcrypt 后存 password_hash - 更新:可改状态;修改密码需单独字段触发并重新 bcrypt - 行级权限:SALESMAN 仅可操作自己客户下的账号 - 列表返回不得携带 password_hash / wx_openid 等敏感字段 ### FR-4:接口鉴权与数据权限(复用现有体系扩展) 系统 SHALL 将客户域接口接入现有认证与权限体系: - 所有客户域接口要求登录(走 auth.middleware → JWT 校验) - RBAC 中间件新增 3 条路径映射:`/api/customers→customer:list`、`/api/customer-departments→customer:department`、`/api/customer-accounts→customer:account` - 超级管理员(ADMIN)直接放行 - 行级数据隔离:SALESMAN 角色的 customer 列表自动加 `salesman_id = ctx.state.user.userId` 条件;customer_department / customer_account 通过 customer_id JOIN customer.salesman_id 过滤 - 越权访问控制:SALESMAN 更新/删除时校验目标记录是否归属自己,否则返回 403 ### FR-5:菜单与按钮权限(db_seed.sql 扩展) 系统 SHALL 在种子数据中补全客户域菜单与按钮权限: - 将「客户管理」顶级菜单(id=1)改为目录类型 menu_type=1,作为父节点 - 新增 3 条子菜单:客户信息(/customer/info / customer/info/index / permission=customer:list)、客户部门(/customer/department / customer/department/index / permission=customer:department)、客户账号(/customer/account / customer/account/index / permission=customer:account) - 新增若干按钮权限(menu_type=3):各模块的 create / update / delete 按钮权限(如 customer:create、customer:update、customer:delete) - 角色-菜单绑定:ADMIN 全部绑定,SALESMAN 绑定客户域全部菜单和按钮 ### FR-6:前端页面(Web 端 3 个页面) 系统 SHALL 提供 3 个 Vue 页面,风格与现有用户管理完全一致: - 客户信息页(customer/info/index.vue):搜索栏(关键词、状态、业务员下拉)+ 表格(列含客户编号、名称、省市区、联系人、电话、级别、状态、创建时间、操作)+ 新增/编辑弹窗(含省市区 a-cascader + 业务员下拉 + 表单校验) - 客户部门页(customer/department/index.vue):搜索栏(关键词、客户下拉)+ 表格 + 弹窗(部门名称、联系人、电话、职能描述) - 客户账号页(customer/account/index.vue):搜索栏(关键词、客户下拉、状态)+ 表格(列含客户名称、电话、状态、创建时间、操作;提示默认密码=手机后6位)+ 弹窗(客户下拉、电话、状态;编辑时提供"重置密码为手机后6位"快捷操作) - 通用:表格内部滚动(复用 useTableScroll composable)、新增/编辑成功后页码重置到第一页并重新拉取列表、删除二次确认、状态 switch 一键切换 ### FR-7:前后端接口契约一致性(兼容 APP) 系统 SHALL 提供统一的 REST 接口,Web 后台和业务员 APP 共用: - 路径前缀:`/api/customers`、`/api/customer-departments`、`/api/customer-accounts`(与现有 /api/users 风格一致) - 通用返回结构:`{ code: 200, message: '...', data: { list, total, page, pageSize } }`(列表)或 `{ code: 200, message, data: {...} | null }`(单条/写操作) - 鉴权方式:统一 Authorization: Bearer (JWT),APP 端和 Web 端完全相同 - 错误码:401 未登录 / 403 无权限 / 400 参数错误 / 404 不存在 / 500 服务异常 - 分页参数:page、pageSize;搜索 keyword;筛选字段均为 Query 参数,后端统一做数字类型校验和上限保护(pageSize ≤ 200) - 接口幂等:写操作均通过 id 区分,UNIQUE 字段(customer_no、customer_account.phone)冲突返回 400 + 明确 message ## Non-Functional Requirements ### NFR-1:防 SQL 注入 所有数据库查询必须通过 Sequelize 参数化 API(`findAndCountAll`、`create`、`update`、`destroy` + `where` 对象),禁止任何字符串拼接 SQL;LIKE 模糊查询使用 `{ [Op.like]: \`%\${keyword}%\` }` 而非字符串模板。 ### NFR-2:前端防 XSS - 前端不使用 `v-html` 渲染任何用户输入字段,全部使用 `v-text` 或 AntDV 默认文本渲染(AntDV 默认转义) - 表单输入添加长度限制(VARCHAR 字段按数据库长度上限 + regex 过滤 `<` `>` `"` `'` `` - **When**: 保存后列表页渲染该客户名称 - **Then**: 名称以纯文本展示(脚本不执行);浏览器 Console 无异常报错 - **Verification**: `human-judgment`(页面观察) ### AC-8:业务员 APP 接口兼容 - **Given**: 使用任意 HTTP 客户端(curl/Postman)模拟 APP 请求,Authorization 头携带 SALESMAN 的有效 JWT - **When**: 调用客户域 GET/POST/PUT/DELETE 接口 - **Then**: 返回结构与 Web 端调用完全一致(同 code/message/data 结构);分页/搜索/鉴权行为无差别 - **Verification**: `programmatic` ### AC-9:前端列表刷新链路 - **Given**: 当前在客户列表第 3 页 - **When**: 新增/编辑/删除一条记录成功 - **Then**: 分页页码重置为 1,列表重新拉取显示最新数据;搜索筛选条件保留(不清空 keyword/status) - **Verification**: `human-judgment` + `programmatic`(网络面板确认请求 page=1) ## Open Questions - [ ] (已确认)功能范围:仅客户信息 + 客户部门 + 客户账号 3 个模块 - [ ] (已确认)customer.salesman_id 行级隔离 + ADMIN 全量 - [ ] (已确认)APP 兼容 = 业务员 APP 与后台共用 API,复用 sys_user 登录 - [ ] (已确认)customer.status 枚举 1意向/2签约/3运维中/4终止;customer_no 自动生成 - [ ] (已确认)customer_account 默认密码=手机后6位,bcrypt 加密 - [ ] (已确认)前端 UI:a-cascader 省市区 + 弹窗模式 + 表格内滚动