简介
本技术文档围绕 DouPHP 后台“用户管理”功能,系统阐述管理员对会员用户的增删改查、状态管理、列表查询与分页、批量操作、权限控制以及数据导入导出等能力。文档从控制器、服务层、模型层、请求校验与中间件等多维度进行剖析,并提供流程图与时序图帮助理解数据流与控制流。同时给出常见问题的定位思路与优化建议。
项目结构
用户管理在后台模块中采用典型的 MVC + Service 分层:
- 控制器:负责路由调度、取参、视图渲染与跳转
- 服务层:封装业务逻辑(列表构建、新增/更新/删除、状态变更、批量操作、Excel 导出)
- 模型层:定义数据表映射、字段白名单、关联与查询作用域
- 请求校验:集中处理表单参数校验与白名单过滤
- 中间件:统一认证与权限检查
graph TB
subgraph "后台入口"
A["路由/中间件<br/>认证与权限"]
end
subgraph "控制器层"
B["UserController"]
end
subgraph "服务层"
C["UserService"]
end
subgraph "模型层"
D["User / UserLevel"]
end
subgraph "外部依赖"
E["附件存储"]
F["审计日志"]
G["Excel 导出"]
end
A --> B --> C --> D
C --> E
C --> F
C --> G
核心组件
- 控制器 UserController:提供用户列表、新增、编辑、删除、批量操作、状态变更、登录参数初始化等接口
- 服务层 UserService:实现列表数据组装、新增/更新/删除、状态迁移、批量动作、Excel 导出、等级日志记录
- 模型 User:定义 user 表映射、字段白名单、性别展示、筛选作用域、默认排序、关联 user_level
- 模型 UserLevel:定义 user_level 表映射与常用查询方法
- 请求校验 UserFormRequest / UserActionFormRequest:表单字段校验、唯一性、必填规则
- 中间件 AuthMiddleware / PermissionMiddleware:后台登录态恢复与模块级权限控制
架构总览
后台用户管理的请求链路如下:
- 请求进入后先经过认证中间件校验登录态,再经权限中间件校验模块访问权限
- 控制器接收请求并调用服务层完成业务处理
- 服务层通过模型层读写数据库,必要时调用附件存储、审计日志、Excel 导出等外部能力
- 最终返回视图或重定向响应
sequenceDiagram
participant Client as "浏览器"
participant MW1 as "认证中间件"
participant MW2 as "权限中间件"
participant Ctrl as "UserController"
participant Svc as "UserService"
participant Model as "User/UserLevel"
participant Ext as "附件/审计/Excel"
Client->>MW1 : 请求 /admin/user/*
MW1-->>Client : 未登录则跳转登录页
MW1->>MW2 : 已登录放行
MW2-->>Client : 无权限则跳转首页
MW2->>Ctrl : 进入控制器
Ctrl->>Svc : 列表/新增/更新/删除/批量/状态
Svc->>Model : 查询/写入
Svc->>Ext : 附件上传/审计日志/Excel导出
Svc-->>Ctrl : 结果
Ctrl-->>Client : 视图/重定向
详细组件分析
控制器:UserController
职责与要点
- 列表页 index:解析分页与筛选条件,调用服务层构建列表数据,渲染模板
- 新增 create/store:生成默认表单数据;提交时由请求校验器校验,服务层插入并记录审计日志
- 编辑 edit/update:加载用户信息;支持头像上传;提交时更新用户信息与联系人快照,记录等级调整日志
- 删除 destroy:二次确认流程,删除用户及关联 SNS 数据,记录审计日志
- 批量 action:支持批量删除、导出选中、导出全部
- 状态 setStatus:封禁/解禁/注销,基于状态机迁移校验并写审计日志
- set:初始化登录相关参数项
关键路径参考
- 列表构建与渲染:UserController.php:77-119
- 新增流程:UserController.php:121-158
- 编辑流程:UserController.php:160-221
- 删除流程:UserController.php:223-238
- 批量操作:UserController.php:240-258
- 状态变更:UserController.php:260-277
服务层:UserService
职责与要点
- buildUserListData:按用户名关键字、状态筛选,分页获取用户列表,聚合最后登录时间、积分、余额、消费、推广、VIP 等信息,构造列表数据
- statusOptions:返回状态选项供前端下拉使用
- setStatus:状态迁移校验(合法迁移),更新状态并写审计日志
- insert:新增用户,生成用户编号,密码哈希,创建联系人快照,可选头像上传,记录审计日志
- update:更新用户基本信息、等级、联系人快照;若修改等级则记录等级日志;可选头像覆盖
- delete:二次确认删除,清理关联 SNS 数据,记录审计日志
- action:批量删除、导出选中、导出全部
- buildExcelUserList:构建 Excel 导出的头部与数据行
- writeManualUserLevelLog / writeManualDistributionLevelLog:手工调整等级时记录日志
关键路径参考
- 列表构建:UserService.php:74-177
- 状态选项:UserService.php:179-192
- 状态变更:UserService.php:194-229
- 新增:UserService.php:342-395
- 更新:UserService.php:397-465
- 删除:UserService.php:467-501
- 批量:UserService.php:503-544
- Excel 导出:UserService.php:567-601
- 等级日志:UserService.php:603-656
模型层:User / UserLevel
User 模型
- 表名与主键:user/id
- 字段类型转换 casts:level_id/login_count/status/created_at
- 可填充字段 fillable:user_sn、level_id、distribution_level_id、mobile、email、password、token、reset_token、sex、nickname、avatar、defined、pay_code、login_count、status、created_at 等
- 关联:belongsTo UserLevel(level_id)
- 访问器:getSexAttribute 将 sex 转为男/女文案
- 查询作用域:filterByUserId、filterByStatus、withLevelIfAvailable、applyDefaultOrder
- 静态方法:findByUserId、deleteUserSnsByUserId、getUserIdByEmail、getUserIdByMobile、getAvatarValue、getUserLogList
UserLevel 模型
- 表名与主键:user_level/id
- 可填充字段:name、upgrade_type、upgrade_condition、good_discount、icon、created_at
- 静态方法:selectAllOrdered、getNameById、getLevelIdByName
请求校验:UserFormRequest / UserActionFormRequest
UserFormRequest
- 场景合并:update 场景自动合并 user_id
- 规则:
- level_id/distribution_level_id:整数
- email/mobile:邮箱/手机号格式,唯一性校验(区分新增/编辑)
- phone/contact/address 等:非法字符校验
- sex:枚举 0/1
- password/password_confirm:新增必填且一致;编辑留空表示不修改 UserActionFormRequest
- 规则:action 限定为 del_all/excel/excel_all;checkbox 为数组
中间件:认证与权限
认证中间件 AuthMiddleware
- 从会话恢复管理员登录态,未登录抛异常跳转登录页 权限中间件 PermissionMiddleware
- 读取当前管理员信息,判断是否可访问当前模块与动作;超级管理员直接放行,普通管理员按白名单判定
类关系图
classDiagram
class UserController {
+index(request) Response
+create() Response
+store(formRequest, request) Response
+edit(request) Response
+update(formRequest, request) Response
+destroy(request) Response
+action(formRequest) Response
+setStatus(formRequest) Response
+set() Response
}
class UserService {
+buildUserListData(username, page, rejectFilter, statusFilter) array
+statusOptions() array
+setStatus(userId, newStatus, adminId) void
+insert(data) int
+update(data, avatar) void
+delete(userId, post) array
+action(data) array|null
+buildExcelUserList(checkbox) array
}
class User {
+table : string
+primary : string
+fillable : array
+level() Relation
+scopeFilterByUserId(query, userId) Builder
+scopeFilterByStatus(query, status) Builder
+scopeWithLevelIfAvailable(query) Builder
+scopeApplyDefaultOrder(query) Builder
+findByUserId(userId) Model|null
+deleteUserSnsByUserId(userId) mixed
+getUserIdByEmail(email) mixed
+getUserIdByMobile(mobile) mixed
+getAvatarValue(userId) mixed
+getUserLogList(userId, page) array
}
class UserLevel {
+table : string
+primary : string
+fillable : array
+selectAllOrdered() array
+getNameById(levelId) mixed
+getLevelIdByName(name) int
}
class UserFormRequest {
+rules() array
}
class UserActionFormRequest {
+rules() array
}
class AuthMiddleware {
+handle(next) mixed
}
class PermissionMiddleware {
+handle(next) mixed
}
UserController --> UserService : "调用"
UserService --> User : "读写"
User --> UserLevel : "关联"
UserController --> UserFormRequest : "校验"
UserController --> UserActionFormRequest : "校验"
AuthMiddleware --> PermissionMiddleware : "前置"
序列图:用户列表查询与分页
sequenceDiagram
participant R as "请求"
participant C as "UserController"
participant S as "UserService"
participant M as "User"
participant Q as "查询聚合"
R->>C : GET /admin/user?page=1&username=&status=
C->>S : buildUserListData(username, page, rejectFilter, statusFilter)
S->>M : withLevelIfAvailable()->filterByUserId()->filterByStatus()->order()->paginate()
S->>Q : 聚合最后登录/积分/余额/消费/推广/VIP
Q-->>S : 映射数据
S-->>C : 列表+分页
C-->>R : 渲染 user.htm
序列图:新增用户
sequenceDiagram
participant R as "请求"
participant C as "UserController"
participant V as "UserFormRequest"
participant S as "UserService"
participant U as "User"
participant A as "附件存储"
participant L as "审计日志"
R->>C : POST /admin/user/store
C->>V : validated()
V-->>C : 校验通过的数据
C->>S : insert(data)
S->>U : create(user_sn, level_id, email/mobile, password_hash, ...)
S->>A : store('user', id, avatar)
S->>L : writeAdminLog(CREATE)
S-->>C : 新ID
C-->>R : 重定向到编辑页
序列图:状态变更(封禁/解禁/注销)
sequenceDiagram
participant R as "请求"
participant C as "UserController"
participant V as "UserStatusFormRequest"
participant S as "UserService"
participant DB as "DB"
participant L as "审计日志"
R->>C : POST /admin/user/set_status
C->>V : validated()
V-->>C : {user_id, new_status}
C->>S : setStatus(userId, newStatus, adminId)
S->>DB : 读取当前状态
S->>S : canTransit(current, newStatus)
alt 允许迁移
S->>DB : update(status=newStatus)
S->>L : writeUserLog(set_status : current->new)
S-->>C : 成功
else 不允许迁移
S-->>C : 抛出异常
end
C-->>R : 重定向到编辑页
流程图:批量操作
flowchart TD
Start(["开始"]) --> Parse["解析 action 与 checkbox"]
Parse --> Check{"action 类型"}
Check --> |del_all| DelAll["校验ids非空 -> 批量删除SNS -> 批量删除用户 -> 审计日志"]
Check --> |excel| ExportSel["导出选中用户Excel"]
Check --> |excel_all| ExportAll["导出全部用户Excel"]
Check --> |其他| Error["抛出异常:选择为空或非法动作"]
DelAll --> End(["结束"])
ExportSel --> End
ExportAll --> End
Error --> End
依赖关系分析
- 控制器依赖服务层,服务层依赖模型层与外部能力(附件、审计、Excel)
- 模型层通过 ORM 与数据库交互,并通过配置开关决定是否预加载等级关联
- 中间件位于控制器之前,确保只有已认证且有权限的请求才能到达业务逻辑
graph LR
MW["中间件"] --> CTRL["控制器"]
CTRL --> SVC["服务层"]
SVC --> MOD["模型层"]
SVC --> ATT["附件存储"]
SVC --> AUD["审计日志"]
SVC --> XLS["Excel导出"]
性能考虑
- 列表查询避免 N+1:通过 withLevelIfAvailable 按需预加载等级关联
- 分页限制:每页固定条数,减少单次返回数据量
- 聚合查询分离:最后登录、积分、余额、消费、推广、VIP 等通过独立查询聚合,避免大表 JOIN
- 导出优化:Excel 导出仅选取必要字段,按主键降序稳定排序
- 索引建议:user 表的 email、mobile、user_sn、status、id 等字段应建立合适索引以提升查询效率
故障排查指南
- 无法访问用户管理页面
- 检查认证中间件是否成功恢复登录态
- 检查权限中间件是否允许当前管理员访问该模块与动作
- 新增用户失败
- 检查表单校验规则(邮箱/手机格式、唯一性、密码一致性)
- 检查必填字段(至少邮箱或手机)
- 编辑用户报错
- 检查 user_id 是否有效
- 检查邮箱/手机唯一性(排除自身)
- 状态变更无效
- 检查当前状态与目标状态是否允许迁移
- 查看审计日志确认是否执行成功
- 批量操作无效果
- 检查 action 是否为受支持的类型
- 检查 checkbox 是否为空或包含非法 ID
结论
DouPHP 后台用户管理采用清晰的分层架构与严格的输入校验,结合状态机迁移与审计日志,实现了安全可控的用户生命周期管理。列表查询与分页、批量操作、Excel 导出等功能完备,便于日常运营与维护。建议在大数据量场景下关注索引设计与聚合查询优化,进一步提升性能。
附录
用户数据模型设计
- 主表 user
- 标识:id(主键)
- 账号:email、mobile、user_sn
- 安全:password、token、reset_token、login_fail_count、login_locked_at
- 属性:sex、nickname、avatar、defined、pay_code、login_count、status、created_at
- 等级:level_id(关联 user_level)、distribution_level_id(分销等级)
- 关联表 user_level
- 标识:id(主键)
- 名称:name
- 升级策略:upgrade_type、upgrade_condition
- 权益:good_discount、icon
- 时间:created_at
- 验证规则
- 邮箱/手机号格式与唯一性(新增/编辑区分)
- 密码长度与一致性(新增必填,编辑留空不修改)
- 性别枚举 0/1
- 地址/电话等字段非法字符过滤
- 关联关系
- User belongsTo UserLevel(level_id)
- 删除用户时联动删除 user_sns
权限级别划分与访问控制
- 超级管理员:type 不为 defined,直接放行所有模块访问
- 普通管理员:type 为 defined,按 action_list 白名单判定模块与动作访问
- 中间件链:认证中间件优先恢复登录态,权限中间件随后校验模块访问
用户列表查询与分页显示
- 列表构建:按用户名关键字(手机/邮箱/user_sn/联系人)解析为用户 ID 过滤,支持状态筛选
- 分页:每页固定条数,返回 pager 对象供模板渲染
- 附加信息:聚合最后登录时间、积分、余额、消费、推广、VIP 信息
批量操作
- 批量删除:校验 IDs,删除 SNS 关联,批量删除用户,记录审计日志
- 导出选中/全部:构建 Excel 头部与数据行,直接输出二进制响应
高级功能方案
- 用户数据导入
- 可在现有 Excel 导出基础上扩展导入逻辑,复用字段映射与校验规则
- 建议分批导入并记录导入日志,失败行单独输出
- 密码重置
- 可通过设置 reset_token 与过期时间,配合前端重置流程更新密码
- 注意令牌唯一性与有效期校验
- 账户锁定/解锁
- 利用 login_fail_count 与 login_locked_at 字段实现失败次数锁定
- 解锁时可清零失败计数并清空锁定时间