3814 字
约 12 分钟
2
用户中心后端完整业务流程

用户中心后端完整业务流程

1. 文档说明

本文档以当前仓库中的后端源码为准,完整梳理用户中心项目从请求进入、业务处理、数据持久化到响应返回的全过程。

项目当前实现了以下功能:

  • 用户注册
  • 用户登录
  • 获取当前用户
  • 用户注销
  • 管理员搜索用户
  • 管理员删除用户
  • 统一响应和异常处理

本文档描述的是当前代码已经实现的行为,同时会把源码中值得继续完善的地方单独标出来。


2. 项目整体业务链路

所有用户接口大体都遵循下面的调用链:

客户端
  │
  │ HTTP 请求
  ▼
UserController
  │
  │ 请求绑定、基础校验、部分权限判断
  ▼
UserService / UserServiceImpl
  │
  │ 业务校验、密码摘要、Session 操作
  ├──────────────────────► HttpSession
  ▼
UserMapper / MyBatis-Plus
  │
  │ 条件查询、插入、更新、逻辑删除
  ▼
MySQL.user
  │
  ▼
User / BaseResponse
  │
  ▼
客户端 JSON 响应

异常路径如下:

Controller / Service 抛出 BusinessException
                    │
                    ▼
GlobalExceptionHandler
                    │
                    ▼
ResultUtils.error(...)
                    │
                    ▼
BaseResponse 错误响应

当前项目存在少量例外:部分失败分支直接返回 null 或 -1,没有完全走统一异常响应。


3. 核心角色和状态

3.1 用户角色

Value Role Permission
0 普通用户 使用普通用户功能
1 管理员 搜索和删除用户

角色常量定义在 UserConstant.java:

  • DEFAULT_ROLE = 0
  • ADMIN_ROLE = 1

管理员判断依赖登录后写入 Session 的用户对象,而不是依赖客户端自行提交的角色字段。

3.2 登录状态

登录成功后,系统将脱敏用户对象写入 Session:

Session["userLoginState"] = safetyUser

后续需要登录的接口会从 Session 中读取该对象。客户端必须保存并携带相同的 Session Cookie。

3.3 用户数据状态

Field Meaning
userStatus 用户状态,数据库脚本中默认 0
userRole 用户角色,0 为普通用户,1 为管理员
isDelete 逻辑删除标记,0 为未删除,1 为已删除

当前登录流程会读取账号和密码,但源码中尚未明确校验 userStatus 是否允许登录。


4. 用户注册流程

4.1 接口信息

POST /api/user/register

其中 /api 来自 server.servlet.context-path,/user/register 来自 UserController 的请求映射。

4.2 请求参数

{
  "userAccount": "testuser",
  "userPassword": "12345678",
  "checkPassword": "12345678",
  "planetCode": "1001"
}

请求对象是 UserRegisterRequest,包含:

Field Purpose
userAccount 用户登录账号
userPassword 原始密码
checkPassword 确认密码,只用于校验
planetCode 星球编号

4.3 详细执行过程

客户端提交注册请求
        │
        ▼
UserController.userRegister
        │
        ├─ 请求对象为空?──────► BusinessException(PARAMS_ERROR)
        ├─ 必填字段为空?──────► 当前代码 return null
        │
        ▼
UserServiceImpl.userRegister
        │
        ├─ 参数为空?──────────► BusinessException
        ├─ 账号长度小于 4?────► BusinessException
        ├─ 密码长度小于 8?────► BusinessException
        ├─ planetCode 大于 5?─► BusinessException
        ├─ 账号含特殊字符?────► 返回 -1
        ├─ 两次密码不一致?────► 返回 -1
        ├─ 账号已存在?────────► BusinessException
        ├─ 编号已存在?────────► BusinessException
        │
        ▼
密码摘要:MD5(SALT + password)
        │
        ▼
创建 User 对象
        │
        ├─ 设置 userAccount
        ├─ 设置摘要后的 userPassword
        └─ 设置 planetCode
        │
        ▼
this.save(user)
        │
        ├─ 保存失败 ───────────► 返回 -1
        └─ 保存成功 ───────────► 返回新用户 id

4.4 注册校验规则

Rule Current behavior
参数为空 抛出 PARAMS_ERROR
账号长度不足 小于 4 时抛出 PARAMS_ERROR
密码长度不足 密码或确认密码小于 8 时抛出 PARAMS_ERROR
星球编号过长 长度大于 5 时抛出 PARAMS_ERROR
账号含特殊字符 返回 -1
两次密码不一致 返回 -1
账号重复 查询数量大于 0 时抛出业务异常
星球编号重复 查询数量大于 0 时抛出业务异常
数据库保存失败 返回 -1

4.5 数据库写入

注册成功时,Service 创建新的 User 对象,主要设置:

  • userAccount
  • 摘要后的 userPassword
  • planetCode

数据库中的 id 通过自增策略生成。其他字段使用数据库默认值或保持为空。

4.6 注册响应

成功响应:

{
  "code": 0,
  "data": 123,
  "message": "ok",
  "description": ""
}

data 是新用户 id。

4.7 注册后的状态

注册成功后,当前代码不会自动登录,也不会向 Session 写入登录态。用户还需要单独调用登录接口。


5. 用户登录流程

5.1 接口信息

POST /api/user/login

5.2 请求参数

{
  "userAccount": "testuser",
  "userPassword": "12345678"
}

请求对象是 UserLoginRequest,包含账号和密码两个字段。

5.3 详细执行过程

客户端提交账号和密码
        │
        ▼
UserController.userLogin
        │
        ├─ 请求对象为空?──────► PARAMS_ERROR
        └─ 账号或密码为空?────► PARAMS_ERROR
        │
        ▼
UserServiceImpl.userLogin
        │
        ├─ 账号长度小于 4?────► 返回 null
        ├─ 密码长度小于 8?────► 返回 null
        ├─ 账号含特殊字符?────► 返回 null
        │
        ▼
计算 MD5(SALT + password)
        │
        ▼
按账号和密码摘要查询数据库
        │
        ├─ 查不到用户 ─────────► 记录日志,返回 null
        └─ 查到用户
                │
                ▼
          getSafetyUser(user)
                │
                ▼
      Session.setAttribute(...)
                │
                ▼
        返回脱敏用户对象

5.4 密码验证

注册和登录使用相同的摘要逻辑:

storedPassword = MD5(SALT + rawPassword)

登录时,系统用用户输入的原始密码计算摘要,再根据账号和摘要密码查询用户:

WHERE userAccount = ?
  AND userPassword = ?

当前源码中的盐值是 Service 内部的固定字符串。固定盐值加 MD5 适合用于理解流程,但不适合作为生产级密码存储方案。

5.5 用户脱敏

查询到原始用户后,Service 不直接返回数据库对象,而是调用 getSafetyUser 创建新对象。

会复制的字段:

  • id
  • username
  • userAccount
  • avatarUrl
  • gender
  • phone
  • email
  • planetCode
  • userRole
  • userStatus
  • createTime

不会复制的字段:

  • userPassword
  • updateTime
  • isDelete

5.6 Session 写入

登录成功后,Service 执行:

request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);

USER_LOGIN_STATE 的实际值是 userLoginState。

后续请求必须携带同一个 Session Cookie,否则系统无法识别当前用户。

5.7 登录响应

登录成功时,Controller 返回脱敏后的 User 对象:

{
  "code": 0,
  "data": {
    "id": 123,
    "userAccount": "testuser",
    "userRole": 0
  },
  "message": "ok",
  "description": ""
}

实际返回字段以脱敏对象和 JSON 序列化结果为准。

当前登录失败时,Service 可能返回 null,Controller 随后仍调用 ResultUtils.success(user)。因此密码错误目前不一定得到明确的登录失败错误码。


6. 获取当前用户流程

6.1 接口信息

GET /api/user/current

该接口需要携带登录成功后的 Session Cookie。

6.2 详细执行过程

客户端请求 /api/user/current
        │
        ▼
从 Session 读取 userLoginState
        │
        ├─ 没有登录态 ───────► BusinessException(NOT_LOGIN)
        └─ 存在登录态
                │
                ▼
        读取 Session 用户 id
                │
                ▼
        userService.getById(userId)
                │
                ▼
        getSafetyUser(user)
                │
                ▼
        返回当前脱敏用户

6.3 为什么还要查询数据库

Session 中保存的是登录时的脱敏对象,但用户信息可能已经发生变化。因此 current 接口会根据 Session 中的 id 再查一次数据库,然后重新脱敏。

6.4 当前待完善点

源码中保留了“校验用户是否合法”的 TODO,目前还没有完整处理:

  • Session 中的用户已经被删除
  • 用户状态被禁用
  • 用户角色或资料发生变化后的边界行为
  • Session 中的用户对象已经过期

7. 用户注销流程

7.1 接口信息

POST /api/user/logout

7.2 详细执行过程

客户端提交注销请求
        │
        ▼
UserController.userLogout
        │
        ├─ request 为 null?────► PARAMS_ERROR
        │
        ▼
UserServiceImpl.userLogout
        │
        ▼
移除 Session["userLoginState"]
        │
        ▼
返回 1

注销的核心操作是移除 Session 中的 userLoginState 属性,而不是销毁整个 Session。

成功响应:

{
  "code": 0,
  "data": 1,
  "message": "ok",
  "description": ""
}

注销之后再次访问 current,由于 Session 中已经没有 userLoginState,会被视为未登录。


8. 管理员搜索用户流程

8.1 接口信息

GET /api/user/search?username=demo

这是管理员接口。

8.2 权限判断

Controller 调用 isAdmin(request):

Session 中读取 userLoginState
        │
        ├─ 用户为空 ───────► false
        └─ 用户存在
                │
                ▼
        user.getUserRole() == ADMIN_ROLE
                │
                ├─ 等于 1 ─────► true
                └─ 其他值 ────► false

权限不通过时,当前搜索接口抛出 PARAMS_ERROR;从语义上说更适合使用 NO_AUTH。

8.3 查询流程

管理员请求搜索接口
        │
        ▼
检查 Session 中的 userRole
        │
        ├─ 非管理员 ─────────► 拒绝请求
        └─ 管理员
                │
                ▼
        创建 QueryWrapper<User>
                │
                ├─ username 非空
                │      ▼
                │  按 username 模糊查询
                │
                ▼
        userService.list(queryWrapper)
                │
                ▼
        每个 User 调用 getSafetyUser
                │
                ▼
        返回脱敏用户列表

8.4 返回结果

查询结果会逐个经过用户脱敏:

{
  "code": 0,
  "data": [
    {
      "id": 1,
      "username": "demo",
      "userAccount": "demo-user",
      "userRole": 0
    }
  ],
  "message": "ok",
  "description": ""
}

如果不传 username,当前代码会查询全部用户,再逐个脱敏。


9. 管理员删除用户流程

9.1 接口信息

POST /api/user/delete

当前方法参数是 long 类型的 id,因此请求体是 JSON 数字:

1

而不是:

{
  "id": 1
}

9.2 详细执行过程

管理员提交用户 id
        │
        ▼
UserController.deleteUser
        │
        ├─ 不是管理员?──────► NO_AUTH
        ├─ id <= 0?─────────► PARAMS_ERROR
        │
        ▼
userService.removeById(id)
        │
        ▼
MyBatis-Plus 根据逻辑删除配置处理
        │
        ▼
返回 Boolean 结果

9.3 逻辑删除

User 实体中的 isDelete 使用逻辑删除标记,配置文件中定义:

Config Value
逻辑删除字段 isDelete
已删除值 1
未删除值 0

因此删除通常表现为:

isDelete: 0 → 1

数据库记录可能仍然存在,但 MyBatis-Plus 的普通查询会自动排除已删除数据。

9.4 删除响应

成功时:

{
  "code": 0,
  "data": true,
  "message": "ok",
  "description": ""
}

data 表示底层删除操作是否成功。


10. 统一响应流程

10.1 成功响应

Controller 通常调用 ResultUtils.success(data),生成:

code = 0
data = 业务数据
message = "ok"
description = ""

返回类型是泛型 BaseResponse

10.2 错误码

ErrorCode Code Meaning
SUCCESS 0 成功
PARAMS_ERROR 40000 请求参数错误
NULL_ERROR 40001 请求数据为空
NOT_LOGIN 40100 未登录
NO_AUTH 40101 无权限
SYSTEM_ERROR 50000 系统内部异常

10.3 业务异常流程

业务代码发现问题
        │
        ▼
throw new BusinessException(...)
        │
        ▼
GlobalExceptionHandler.businessExceptionHandler
        │
        ▼
ResultUtils.error(code, message, description)
        │
        ▼
BaseResponse 错误 JSON

10.4 未知运行时异常流程

未被业务代码处理的 RuntimeException
        │
        ▼
GlobalExceptionHandler.runtimeExceptionHandler
        │
        ▼
记录日志
        │
        ▼
返回 SYSTEM_ERROR

当前实现会把部分运行时异常消息放入响应描述。生产环境应避免把 SQL、路径、主机等内部细节直接返回给客户端。


11. 数据流总览

11.1 注册数据流

注册请求
  │ 原始账号、原始密码、确认密码、星球编号
  ▼
Controller 参数接收
  ▼
Service 业务校验
  ▼
密码摘要
  ▼
User 实体
  ▼
MySQL.user
  ▼
新用户 id

11.2 登录数据流

登录请求
  │ 账号 + 原始密码
  ▼
密码摘要
  ▼
按账号和摘要密码查询
  ▼
原始 User
  ▼
getSafetyUser
  ├────────► Session[userLoginState]
  └────────► BaseResponse<User>

11.3 管理员搜索数据流

管理员请求
  ▼
Session 角色校验
  ▼
QueryWrapper 条件
  ▼
MySQL.user 查询
  ▼
用户列表逐个脱敏
  ▼
BaseResponse<List<User>>

11.4 管理员删除数据流

管理员请求 id
  ▼
角色和 id 校验
  ▼
removeById
  ▼
isDelete: 0 → 1
  ▼
Boolean 结果

12. 功能之间的关系

注册
  │ 创建账号
  ▼
登录
  │ 建立 Session 登录态
  ├────────► 当前用户查询
  ├────────► 注销
  └────────► 管理员搜索 / 管理员删除
                         │
                         ▼
                  依赖 userRole = 1

业务关系可以概括为:

  1. 注册负责创建用户数据。
  2. 登录负责验证身份并建立登录态。
  3. 当前用户接口负责读取并刷新用户信息。
  4. 注销负责移除登录态。
  5. 管理员搜索和删除依赖登录态中的管理员角色。
  6. 正常结果通过 BaseResponse 返回,异常尽量由全局异常处理器收敛。

13. 当前实现中的主要问题

以下问题都可以从当前源码直接观察到。

13.1 失败返回不统一

注册和登录存在三种失败表达:

  • 抛出 BusinessException
  • 返回 -1
  • 返回 null

建议统一为明确的业务异常或统一的错误响应,避免前端处理隐式协议。

13.2 密码安全性不足

当前使用固定盐值加 MD5。正式项目应改用专门的密码哈希算法,例如 BCrypt、SCrypt 或 Argon2,并为每个用户生成随机盐。

13.3 查重不能完全防止并发重复

当前流程是:

selectCount
    ▼
判断没有重复
    ▼
save

并发请求可能同时通过查重,因此数据库仍应为 userAccount 和 planetCode 增加唯一约束。

13.4 管理员权限判断位置较简单

当前权限判断写在 Controller 的 isAdmin 中。随着功能增多,可以抽取统一鉴权组件,避免每个接口重复判断。

13.5 Session 扩容问题

当前使用 Servlet Session。单实例运行比较直观,多实例部署时需要考虑共享 Session、会话粘滞或改用无状态认证。

13.6 生产配置安全

生产配置文件不应直接保存数据库真实密码。应使用环境变量、配置中心或密钥管理服务,并及时轮换已经暴露过的凭据。

13.7 测试覆盖不完整

目前已有 CRUD 和部分注册测试,但登录成功、Session、脱敏、权限、HTTP JSON 响应等场景还需要补充。


14. 推荐阅读顺序

  1. src/main/java/com/yupi/usercenter/controller/UserController.java
  2. src/main/java/com/yupi/usercenter/service/UserService.java
  3. src/main/java/com/yupi/usercenter/service/impl/UserServiceImpl.java
  4. src/main/java/com/yupi/usercenter/model/domain/User.java
  5. src/main/java/com/yupi/usercenter/mapper/UserMapper.java
  6. src/main/resources/mapper/UserMapper.xml
  7. src/main/java/com/yupi/usercenter/common/
  8. src/main/java/com/yupi/usercenter/exception/
  9. src/main/resources/application.yml
  10. sql/create_table.sql
  11. src/test/java/com/yupi/usercenter/service/UserServiceTest.java

15. 相关文档

用户中心后端完整业务流程
http://www.clxhxhhr.top/posts/525/
作者
clxstart
发布于
2026-09-08
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。