From c65c03b933e2021c9772bdacadd82e56a7588120 Mon Sep 17 00:00:00 2001 From: zyh Date: Sun, 24 Aug 2025 15:54:21 +0800 Subject: [PATCH] Refactor authentication logic to unify user account handling and update database schema for user accounts --- docs/game.sql | 163 +++++++----------- docs/开发文档.md | 122 +++++++++++++ .../server/controller/UserController.java | 2 +- .../controller/auth/AuthController.java | 3 +- .../mapper/account/UserAccountMapper.java | 10 ++ .../server/mapper/admin/AdminUserMapper.java | 9 - .../server/mapper/agent/AgentMapper.java | 9 - .../UserAccount.java} | 19 +- .../server/model/entity/agent/Agent.java | 32 ---- .../server/service/auth/AuthService.java | 94 ++++------ src/main/resources/mapper/UserMapper.xml | 1 - .../UserAccountMapper.xml} | 14 +- .../resources/mapper/agent/AgentMapper.xml | 22 --- .../controller/auth/AuthController$1.class | Bin 1304 -> 1439 bytes .../server/mapper/admin/AdminUserMapper.class | Bin 394 -> 0 bytes .../server/mapper/agent/AgentMapper.class | Bin 390 -> 0 bytes .../server/model/entity/admin/AdminUser.class | Bin 1965 -> 0 bytes .../server/model/entity/agent/Agent.class | Bin 2181 -> 0 bytes .../server/service/auth/AuthService.class | Bin 8164 -> 7118 bytes target/classes/mapper/UserMapper.xml | 1 - .../classes/mapper/admin/AdminUserMapper.xml | 21 --- target/classes/mapper/agent/AgentMapper.xml | 22 --- 22 files changed, 256 insertions(+), 288 deletions(-) create mode 100644 docs/开发文档.md create mode 100644 src/main/java/com/gameplatform/server/mapper/account/UserAccountMapper.java delete mode 100644 src/main/java/com/gameplatform/server/mapper/admin/AdminUserMapper.java delete mode 100644 src/main/java/com/gameplatform/server/mapper/agent/AgentMapper.java rename src/main/java/com/gameplatform/server/model/entity/{admin/AdminUser.java => account/UserAccount.java} (54%) delete mode 100644 src/main/java/com/gameplatform/server/model/entity/agent/Agent.java rename src/main/resources/mapper/{admin/AdminUserMapper.xml => account/UserAccountMapper.xml} (50%) delete mode 100644 src/main/resources/mapper/agent/AgentMapper.xml delete mode 100644 target/classes/com/gameplatform/server/mapper/admin/AdminUserMapper.class delete mode 100644 target/classes/com/gameplatform/server/mapper/agent/AgentMapper.class delete mode 100644 target/classes/com/gameplatform/server/model/entity/admin/AdminUser.class delete mode 100644 target/classes/com/gameplatform/server/model/entity/agent/Agent.class delete mode 100644 target/classes/mapper/admin/AdminUserMapper.xml delete mode 100644 target/classes/mapper/agent/AgentMapper.xml diff --git a/docs/game.sql b/docs/game.sql index 214df5f..3468c77 100644 --- a/docs/game.sql +++ b/docs/game.sql @@ -12,89 +12,84 @@ SET NAMES utf8mb4; SET sql_mode = 'STRICT_ALL_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO'; -- ----------------------------------------------------------------------------- --- 1) 管理员(平台侧) +-- 1) 统一账户表(管理员/代理商共用) +-- 用 user_type 区分:ADMIN | AGENT -- ----------------------------------------------------------------------------- -CREATE TABLE IF NOT EXISTS admin_user ( - id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, - username VARCHAR(64) NOT NULL UNIQUE, - password_hash VARBINARY(100) NOT NULL, -- 建议存储 bcrypt/argon2 等 - role ENUM('SUPER','ADMIN') NOT NULL DEFAULT 'ADMIN', +CREATE TABLE IF NOT EXISTS user_account ( + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + user_type ENUM('ADMIN','AGENT') NOT NULL, + username VARCHAR(64) NOT NULL UNIQUE, -- 登录名(两类共用) + display_name VARCHAR(100) NULL, -- 展示名(AGENT 可用) + password_hash VARCHAR(120) NOT NULL, -- 建议存储 BCrypt(或临时 PLAIN: 便于初始化) + role ENUM('SUPER','ADMIN') NULL, -- 仅 ADMIN 使用 status ENUM('ENABLED','DISABLED') NOT NULL DEFAULT 'ENABLED', - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - --- ----------------------------------------------------------------------------- --- 2) 代理商(商家) --- ----------------------------------------------------------------------------- -CREATE TABLE IF NOT EXISTS agent ( - id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(100) NOT NULL, - login_account VARCHAR(64) NOT NULL UNIQUE, - password_hash VARBINARY(100) NOT NULL, - status ENUM('ENABLED','DISABLED') NOT NULL DEFAULT 'ENABLED', - points_balance BIGINT UNSIGNED NOT NULL DEFAULT 0, -- 当前可用点数 + points_balance BIGINT UNSIGNED NOT NULL DEFAULT 0, -- 仅 AGENT 使用 created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), - CONSTRAINT chk_agent_points_nonneg CHECK (points_balance >= 0) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + CONSTRAINT chk_points_nonneg CHECK (points_balance >= 0) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- 默认管理员账号(密码:admin7uqweh12)。 +-- 生产环境请尽快替换为 BCrypt 哈希;此处为 PLAIN 方便首次初始化。 +INSERT INTO user_account(user_type, username, display_name, password_hash, role, status, points_balance) +VALUES ('ADMIN', 'admin', 'Super Admin', 'PLAIN:admin7uqweh12', 'SUPER', 'ENABLED', 0) +ON DUPLICATE KEY UPDATE username = username; -- ----------------------------------------------------------------------------- --- 3) 代理商点数流水 --- 记录加点/扣点(生成链接时扣 Times×Quantity×BatchSize) +-- 2) 代理商点数流水 -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS agent_points_tx ( - id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, - agent_id BIGINT UNSIGNED NOT NULL, - type ENUM('ADD','DEDUCT') NOT NULL, + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + account_id BIGINT UNSIGNED NOT NULL, -- 指向 user_account(AGENT) + type ENUM('ADD','DEDUCT') NOT NULL, before_points BIGINT UNSIGNED NOT NULL, - delta_points BIGINT SIGNED NOT NULL, -- 可为负/正;与 type 对应 + delta_points BIGINT SIGNED NOT NULL, -- 可为正/负;与 type 对应 after_points BIGINT UNSIGNED NOT NULL, reason ENUM('create_links','manual','refund_no_rollback','other') NOT NULL DEFAULT 'other', ref_id BIGINT UNSIGNED NULL, -- 可关联到 link_batch.id - operator_id BIGINT UNSIGNED NULL, -- 操作者(管理员) + operator_id BIGINT UNSIGNED NULL, -- 操作者(管理员,指向 user_account) created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - INDEX idx_agent_points_tx_agent_time (agent_id, created_at), - CONSTRAINT fk_apx_agent FOREIGN KEY (agent_id) REFERENCES agent(id), - CONSTRAINT fk_apx_operator FOREIGN KEY (operator_id) REFERENCES admin_user(id) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + INDEX idx_apx_account_time (account_id, created_at), + CONSTRAINT fk_apx_account FOREIGN KEY (account_id) REFERENCES user_account(id), + CONSTRAINT fk_apx_operator FOREIGN KEY (operator_id) REFERENCES user_account(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ----------------------------------------------------------------------------- --- 4) 链接批次(一次生成 N 个链接,按统一设置扣费) +-- 3) 链接批次(一次生成 N 个链接,按统一设置扣费) -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS link_batch ( - id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, - agent_id BIGINT UNSIGNED NOT NULL, - quantity INT UNSIGNED NOT NULL, -- 每次奖励数量(如 50) - times INT UNSIGNED NOT NULL, -- 重复执行次数(如 20) - batch_size INT UNSIGNED NOT NULL, -- 本批生成链接数量(如 10) - deduct_points BIGINT UNSIGNED NOT NULL, -- 扣点=quantity*times*batch_size - operator_id BIGINT UNSIGNED NULL, -- 操作者(管理员或代理自己为空) - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + agent_id BIGINT UNSIGNED NOT NULL, -- 指向 user_account(AGENT) + quantity INT UNSIGNED NOT NULL, -- 每次奖励数量(如 50) + times INT UNSIGNED NOT NULL, -- 重复执行次数(如 20) + batch_size INT UNSIGNED NOT NULL, -- 本批生成链接数量(如 10) + deduct_points BIGINT UNSIGNED NOT NULL, -- 扣点=quantity*times*batch_size + operator_id BIGINT UNSIGNED NULL, -- 操作者(管理员) + created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), INDEX idx_lb_agent_time (agent_id, created_at), CONSTRAINT chk_lb_quantity_pos CHECK (quantity > 0), CONSTRAINT chk_lb_times_pos CHECK (times > 0), CONSTRAINT chk_lb_batch_pos CHECK (batch_size > 0), - CONSTRAINT fk_lb_agent FOREIGN KEY (agent_id) REFERENCES agent(id), - CONSTRAINT fk_lb_operator FOREIGN KEY (operator_id) REFERENCES admin_user(id) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + CONSTRAINT fk_lb_agent FOREIGN KEY (agent_id) REFERENCES user_account(id), + CONSTRAINT fk_lb_operator FOREIGN KEY (operator_id) REFERENCES user_account(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ----------------------------------------------------------------------------- --- 5) 单链接任务(用户访问的“加密链接”对应的实体) +-- 4) 单链接任务(用户访问的“加密链接”对应的实体) -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS link_task ( - id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, - batch_id BIGINT UNSIGNED NOT NULL, - agent_id BIGINT UNSIGNED NOT NULL, - code_no VARCHAR(32) NOT NULL, -- 后端生成的全局唯一编号 - token_hash CHAR(64) NOT NULL, -- 加密token的SHA-256十六进制(用于失效/撤销) - expire_at DATETIME(3) NOT NULL, -- 链接有效期(默认 24h) + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + batch_id BIGINT UNSIGNED NOT NULL, + agent_id BIGINT UNSIGNED NOT NULL, -- 指向 user_account(AGENT) + code_no VARCHAR(32) NOT NULL, -- 后端生成的全局唯一编号 + token_hash CHAR(64) NOT NULL, -- 加密token的SHA-256(用于失效/撤销) + expire_at DATETIME(3) NOT NULL, -- 链接有效期(默认 24h) status ENUM('NEW','USING','LOGGED_IN','REFUNDED','EXPIRED') NOT NULL DEFAULT 'NEW', - region ENUM('Q','V') NULL, -- 选区;未选择前为 NULL - machine_id VARCHAR(64) NULL, -- 绑定的脚本端机器编号 + region ENUM('Q','V') NULL, -- 选区;未选择前为 NULL + machine_id VARCHAR(64) NULL, -- 绑定的脚本端机器编号 login_at DATETIME(3) NULL, refund_at DATETIME(3) NULL, - revoked_at DATETIME(3) NULL, -- 主动撤销(如需要立即失效) + revoked_at DATETIME(3) NULL, -- 主动撤销(如需要立即失效) created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), UNIQUE KEY uk_code_no (code_no), @@ -103,15 +98,15 @@ CREATE TABLE IF NOT EXISTS link_task ( INDEX idx_expire_at (expire_at), INDEX idx_created_at (created_at), CONSTRAINT fk_lt_batch FOREIGN KEY (batch_id) REFERENCES link_batch(id), - CONSTRAINT fk_lt_agent FOREIGN KEY (agent_id) REFERENCES agent(id) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + CONSTRAINT fk_lt_agent FOREIGN KEY (agent_id) REFERENCES user_account(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ----------------------------------------------------------------------------- --- 6) 操作日志(审计/可观测性) +-- 5) 操作日志(审计/可观测性) -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS operation_log ( - id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, - actor_type ENUM('admin','agent','system','user') NOT NULL, + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + actor_type ENUM('admin','agent','system','user') NOT NULL, actor_id BIGINT UNSIGNED NULL, -- 不强制外键,避免多态复杂度 code_no VARCHAR(32) NULL, op VARCHAR(64) NOT NULL, -- 如:create_links / refund / select_region / create_qr / poll_login / release_machine @@ -121,54 +116,18 @@ CREATE TABLE IF NOT EXISTS operation_log ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), INDEX idx_log_code_time (code_no, created_at), INDEX idx_log_time (created_at) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ----------------------------------------------------------------------------- --- 7) 公告 +-- 6) 公告 -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS announcement ( - id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, - title VARCHAR(100) NOT NULL, - content TEXT NOT NULL, -- 简单文本 + id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(100) NOT NULL, + content TEXT NOT NULL, -- 富文本 enabled TINYINT(1) NOT NULL DEFAULT 1, jump_url VARCHAR(255) NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), - INDEX idx_announce_enabled (enabled) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - --- ----------------------------------------------------------------------------- --- 8) 平台级配置(商家不可配) --- ----------------------------------------------------------------------------- -CREATE TABLE IF NOT EXISTS sys_config ( - cfg_key VARCHAR(64) PRIMARY KEY, - cfg_value TEXT NOT NULL, - description VARCHAR(255) NULL, updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; --- 预置关键配置(可按需改值) -INSERT INTO sys_config (cfg_key, cfg_value, description) VALUES - ('LINK_EXPIRE_HOURS', '24', '链接有效期(小时)'), - ('QR_EXPIRE_SECONDS', '60', '二维码过期秒数(脚本端未返回TTL时使用)'), - ('REFRESH_WAIT_SECONDS', '10', '刷新后强制等待秒数(商家不可配置)'), - ('MACHINE_COOLDOWN_MINUTES','10', '同一机器复用冷却时长(分钟)'), - ('DEFAULT_BATCH_SIZE', '10', '批量生成默认数量'), - ('SECOND_SCREEN_URL_TEMPLATE','https://你的域名/{codeNo}','二界面URL模板(包含编号占位)') - ON DUPLICATE KEY UPDATE - cfg_value = VALUES(cfg_value), - description = VALUES(description); - --- ----------------------------------------------------------------------------- --- 推荐的视图 / 物化统计可按需追加(此处略) --- ----------------------------------------------------------------------------- - --- ============================================================================= --- 说明: --- 1) 生成链接时,请在业务层完成: --- - 计算扣点 = quantity * times * batch_size --- - 扣减 agent.points_balance,并写入 agent_points_tx --- - 写入 link_batch 及其下的若干 link_task(生成 code_no / token 及 token_hash、expire_at) --- 2) 用户端所有页面均以“加密链接 token”为入口,通过 token_hash 可实现失效/撤销。 --- 3) operation_log.detail 建议仅存必要字段并脱敏;日志保留 90 天可通过定时归档或分区处理。 --- ============================================================================= diff --git a/docs/开发文档.md b/docs/开发文档.md new file mode 100644 index 0000000..c2a8c8e --- /dev/null +++ b/docs/开发文档.md @@ -0,0 +1,122 @@ +# 开发文档(Game Platform Server) + +本项目为 Spring Boot 3 + WebFlux + MyBatis + MySQL 的后端服务。文档用于: +- 统一开发风格与规范 +- 明确分层、目录结构与模块边界 +- 约定接口设计、异常与日志规范 +- 指导环境搭建、数据库迁移与交付流程 +- 便于新同学快速接手 + +## 技术栈与版本 +- Java 17、Maven 3.9+ +- Spring Boot 3.x(WebFlux、Actuator、Security) +- MyBatis(JDBC,XML 映射) +- MySQL 8+ +- JWT(JJWT 0.11.x) + +## 目录结构(分层优先 layer-first) +- `src/main/java/com/gameplatform/server` + - `config`:全局配置(WebFlux、CORS、Jackson 等) + - `security`:`SecurityConfig`、`JwtService`、认证授权 + - `exception`:全局异常处理、错误码 + - `common`:常量、工具、拦截器、审计 + - `model` + - `entity`:与数据库表对应的实体(如 `entity.account.UserAccount`) + - `dto`:请求/响应 DTO(如 `dto.auth.LoginRequest`) + - `controller`:按模块子包(`auth`, `link`, `batch`, `points`, `announcement`, ...) + - `service`:按模块子包(与 controller 对应) + - `mapper`:按模块子包(接口 + XML) +- `src/main/resources` + - `mapper/**.xml`:MyBatis XML 映射 + - `application.yml`:多环境配置(建议扩展 `application-dev.yml` 等) + - 可选:`db/migration`(建议引入 Flyway 管理 SQL) +- `docs/`:需求、数据库结构、开发文档(本文件) + +## 数据库与模型 +- 统一账户表:`user_account`(ADMIN/AGENT 共用,通过 `user_type` 区分) + - 关键字段:`username` 唯一、`password_hash`(BCrypt 或初始化阶段 `PLAIN:`)、`role`(ADMIN 用)、`points_balance`(AGENT 用) + - 初始账户:`admin / admin7uqweh12`(以 `PLAIN:` 方式插入,部署后务必改为 BCrypt) +- 相关表:`agent_points_tx`、`link_batch`、`link_task`、`operation_log`、`announcement` +- SQL 参考:`docs/game.sql` +- 建议:后续改为 Flyway 迁移(`V001__init.sql` 起步),避免手工执行 SQL + +## 安全与认证 +- 登录:`POST /api/auth/login`(`userType=admin|agent` + `username` + `password`) +- 自我信息:`GET /api/auth/me`(Authorization: Bearer ) +- JWT:HS256;配置在 `security.jwt.*`(`application.yml`) +- 密码:默认 `BCrypt`;兼容 `PLAIN:` 前缀以便初始化迁移 + +## API 设计规范 +- 路径:REST 风格、资源复数,如 `/api/links`、`/api/batches` +- 状态码:201 创建、204 删除、400/401/403/404/409/429、500 异常 +- 统一返回(推荐):`{code, message, data, traceId}` 或直接返回资源对象(二选一并全局统一) +- 分页:`page,size,sort`;返回 `{items,total,page,size}`;热点列表优先 keyset 分页 +- 幂等:写操作支持 `Idempotency-Key` +- 速率限制:对轮询/二维码代理端点限流,返回 429 + +## 异常与日志 +- 全局异常:`exception/GlobalExceptionHandler` 映射为统一 JSON +- 关联 ID:响应体与日志注入 `traceId`(落地可加 Filter/MDC) +- 日志:结构化、脱敏(token/手机号/IP 末段) + +## WebFlux + MyBatis 指南 +- MyBatis 基于 JDBC 阻塞;在 Service 层用 `Schedulers.boundedElastic()` 包裹 +- 事务在 Service;XML 使用 `resultMap` + `Base_Column_List`,避免 N+1 +- 命名:DB 下划线、Java 驼峰;启用 `map-underscore-to-camel-case` + +## 开发流程与规范 +- 分支:`main`(稳定) / `feat/*`(功能) / `fix/*`(修复) / `chore/*` +- 提交规范(建议):`type(scope): subject`,如 `feat(auth): add login api` +- 代码风格: + - Controller 瘦、Service 厚;禁止直接在 Controller 写 SQL + - DTO 与 Entity 分离;禁止直接暴露 Entity 给前端 + - 单一职责、接口优先;跨模块通过 Service 接口交互 + - 注释清晰,公共逻辑放 `common`/工具类 +- 代码评审:PR 必须通过基本 CI 与至少 1 名评审 + +## 环境与配置 +- `application.yml`:数据库连接、`security.jwt.secret`(务必替换默认 secret) +- 多环境:添加 `application-dev.yml`/`-prod.yml` 并通过 `SPRING_PROFILES_ACTIVE` 选择 +- 机密:使用环境变量/密钥管理器,不要提交到仓库 + +## 构建与运行 +- 本地:`mvn spring-boot:run` +- Jar:`mvn clean package` → `java -jar target/*.jar` +- Docker(建议后续补):多阶段构建 + 健康检查 + 环境变量注入 + +## 测试策略 +- 单元测试:Service 规则、扣点/状态机 +- 切片测试:`@WebFluxTest`(控制器)、`@MybatisTest`(Mapper) +- 集成测试:Testcontainers MySQL 覆盖关键闭环(批次→扣点→链接→二维码→登录) + +## 任务分解与进度追踪(示例) +- V1 基础闭环 + - [ ] 统一返回体与错误码 + - [x] 登录与 JWT(admin/agent 合表) + - [ ] 批次创建、扣点流水、链接生成 + - [ ] 二维码代理与状态轮询 + - [ ] 操作日志、公告 CRUD +- V2 稳态与风控 + - [ ] 幂等、限流、黑名单/刷新令牌 + - [ ] 报表与导出、对账 +- V3 优化 + - [ ] 乐观锁/并发扣点优化、观测/告警 + +## 接口清单(节选) +- `POST /api/auth/login`:登录获取 JWT +- `GET /api/auth/me`:当前用户信息 +- `GET /api/link/{codeNo}`:查询链接元数据(待实现) +- `POST /api/link/{codeNo}/select-region`:选择区服(待实现) +- `GET /api/link/{token}/qr.png`:二维码代理(待实现) + +## 上手步骤 +1) 配置数据库连接与 `security.jwt.secret` +2) 执行 `docs/game.sql` 初始化库与默认管理员 +3) `mvn spring-boot:run` 启动服务 +4) 调用 `POST /api/auth/login` 获取 token,再访问 `GET /api/auth/me` + +## 注意事项 +- 默认管理员以 `PLAIN:` 存储密码,仅用于初始化。上线前必须改为 BCrypt: + - 方案:使用项目内 `BCryptPasswordEncoder` 生成哈希,更新 `user_account.password_hash` +- 统一返回体与 RBAC 规则将在后续迭代落地 + diff --git a/src/main/java/com/gameplatform/server/controller/UserController.java b/src/main/java/com/gameplatform/server/controller/UserController.java index 741cbf0..761f519 100644 --- a/src/main/java/com/gameplatform/server/controller/UserController.java +++ b/src/main/java/com/gameplatform/server/controller/UserController.java @@ -10,7 +10,7 @@ import reactor.core.publisher.Mono; @RestController @RequestMapping("/api/users") -public class UserController { +public class UserController { private final UserService userService; public UserController(UserService userService) { diff --git a/src/main/java/com/gameplatform/server/controller/auth/AuthController.java b/src/main/java/com/gameplatform/server/controller/auth/AuthController.java index 77458f3..77bdbb0 100644 --- a/src/main/java/com/gameplatform/server/controller/auth/AuthController.java +++ b/src/main/java/com/gameplatform/server/controller/auth/AuthController.java @@ -40,9 +40,8 @@ public class AuthController { put("userType", c.get("userType")); put("userId", c.get("userId")); put("username", c.get("username")); - put("role", c.get("role")); + if ("admin".equals(c.get("userType"))) put("role", c.get("role")); put("exp", c.getExpiration()); }}; } } - diff --git a/src/main/java/com/gameplatform/server/mapper/account/UserAccountMapper.java b/src/main/java/com/gameplatform/server/mapper/account/UserAccountMapper.java new file mode 100644 index 0000000..3884cbb --- /dev/null +++ b/src/main/java/com/gameplatform/server/mapper/account/UserAccountMapper.java @@ -0,0 +1,10 @@ +package com.gameplatform.server.mapper.account; + +import com.gameplatform.server.model.entity.account.UserAccount; +import org.apache.ibatis.annotations.Param; + +public interface UserAccountMapper { + UserAccount findByUsernameAndType(@Param("username") String username, + @Param("userType") String userType); +} + diff --git a/src/main/java/com/gameplatform/server/mapper/admin/AdminUserMapper.java b/src/main/java/com/gameplatform/server/mapper/admin/AdminUserMapper.java deleted file mode 100644 index 6010c49..0000000 --- a/src/main/java/com/gameplatform/server/mapper/admin/AdminUserMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gameplatform.server.mapper.admin; - -import com.gameplatform.server.model.entity.admin.AdminUser; -import org.apache.ibatis.annotations.Param; - -public interface AdminUserMapper { - AdminUser findByUsername(@Param("username") String username); -} - diff --git a/src/main/java/com/gameplatform/server/mapper/agent/AgentMapper.java b/src/main/java/com/gameplatform/server/mapper/agent/AgentMapper.java deleted file mode 100644 index bb7ddb5..0000000 --- a/src/main/java/com/gameplatform/server/mapper/agent/AgentMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gameplatform.server.mapper.agent; - -import com.gameplatform.server.model.entity.agent.Agent; -import org.apache.ibatis.annotations.Param; - -public interface AgentMapper { - Agent findByLoginAccount(@Param("loginAccount") String loginAccount); -} - diff --git a/src/main/java/com/gameplatform/server/model/entity/admin/AdminUser.java b/src/main/java/com/gameplatform/server/model/entity/account/UserAccount.java similarity index 54% rename from src/main/java/com/gameplatform/server/model/entity/admin/AdminUser.java rename to src/main/java/com/gameplatform/server/model/entity/account/UserAccount.java index 011ad6d..d5fa3ff 100644 --- a/src/main/java/com/gameplatform/server/model/entity/admin/AdminUser.java +++ b/src/main/java/com/gameplatform/server/model/entity/account/UserAccount.java @@ -1,26 +1,35 @@ -package com.gameplatform.server.model.entity.admin; +package com.gameplatform.server.model.entity.account; import java.time.LocalDateTime; -public class AdminUser { +public class UserAccount { private Long id; - private String username; - private String passwordHash; - private String role; // SUPER / ADMIN + private String userType; // ADMIN | AGENT + private String username; // 登录名(admin/agent 共用) + private String displayName; // 显示名称(agent 可用) + private String passwordHash; // BCrypt 或 PLAIN:xxx(初始化用) + private String role; // 仅 ADMIN 使用:SUPER / ADMIN private String status; // ENABLED / DISABLED + private Long pointsBalance; // 仅 AGENT 使用 private LocalDateTime createdAt; private LocalDateTime updatedAt; public Long getId() { return id; } public void setId(Long id) { this.id = id; } + public String getUserType() { return userType; } + public void setUserType(String userType) { this.userType = userType; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } + public String getDisplayName() { return displayName; } + public void setDisplayName(String displayName) { this.displayName = displayName; } public String getPasswordHash() { return passwordHash; } public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } + public Long getPointsBalance() { return pointsBalance; } + public void setPointsBalance(Long pointsBalance) { this.pointsBalance = pointsBalance; } public LocalDateTime getCreatedAt() { return createdAt; } public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } public LocalDateTime getUpdatedAt() { return updatedAt; } diff --git a/src/main/java/com/gameplatform/server/model/entity/agent/Agent.java b/src/main/java/com/gameplatform/server/model/entity/agent/Agent.java deleted file mode 100644 index 72854cc..0000000 --- a/src/main/java/com/gameplatform/server/model/entity/agent/Agent.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gameplatform.server.model.entity.agent; - -import java.time.LocalDateTime; - -public class Agent { - private Long id; - private String name; - private String loginAccount; - private String passwordHash; - private String status; // ENABLED / DISABLED - private Long pointsBalance; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getName() { return name; } - public void setName(String name) { this.name = name; } - public String getLoginAccount() { return loginAccount; } - public void setLoginAccount(String loginAccount) { this.loginAccount = loginAccount; } - public String getPasswordHash() { return passwordHash; } - public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; } - public String getStatus() { return status; } - public void setStatus(String status) { this.status = status; } - public Long getPointsBalance() { return pointsBalance; } - public void setPointsBalance(Long pointsBalance) { this.pointsBalance = pointsBalance; } - public LocalDateTime getCreatedAt() { return createdAt; } - public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } - public LocalDateTime getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } -} - diff --git a/src/main/java/com/gameplatform/server/service/auth/AuthService.java b/src/main/java/com/gameplatform/server/service/auth/AuthService.java index 2a6ddc4..ad6fb2e 100644 --- a/src/main/java/com/gameplatform/server/service/auth/AuthService.java +++ b/src/main/java/com/gameplatform/server/service/auth/AuthService.java @@ -1,11 +1,9 @@ package com.gameplatform.server.service.auth; -import com.gameplatform.server.mapper.admin.AdminUserMapper; -import com.gameplatform.server.mapper.agent.AgentMapper; +import com.gameplatform.server.mapper.account.UserAccountMapper; import com.gameplatform.server.model.dto.auth.LoginRequest; import com.gameplatform.server.model.dto.auth.LoginResponse; -import com.gameplatform.server.model.entity.admin.AdminUser; -import com.gameplatform.server.model.entity.agent.Agent; +import com.gameplatform.server.model.entity.account.UserAccount; import com.gameplatform.server.security.JwtService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -16,78 +14,62 @@ import java.util.Map; @Service public class AuthService { - private final AdminUserMapper adminUserMapper; - private final AgentMapper agentMapper; + private final UserAccountMapper userAccountMapper; private final PasswordEncoder passwordEncoder; private final JwtService jwtService; - public AuthService(AdminUserMapper adminUserMapper, - AgentMapper agentMapper, + public AuthService(UserAccountMapper userAccountMapper, PasswordEncoder passwordEncoder, JwtService jwtService) { - this.adminUserMapper = adminUserMapper; - this.agentMapper = agentMapper; + this.userAccountMapper = userAccountMapper; this.passwordEncoder = passwordEncoder; this.jwtService = jwtService; } public Mono login(LoginRequest req) { - String userType = req.getUserType().toLowerCase(); - if ("admin".equals(userType)) { - return Mono.fromCallable(() -> adminUserMapper.findByUsername(req.getUsername())) - .subscribeOn(Schedulers.boundedElastic()) - .flatMap(admin -> validateAdminPassword(admin, req.getPassword())); - } else if ("agent".equals(userType)) { - return Mono.fromCallable(() -> agentMapper.findByLoginAccount(req.getUsername())) - .subscribeOn(Schedulers.boundedElastic()) - .flatMap(agent -> validateAgentPassword(agent, req.getPassword())); + String userType = normalizeType(req.getUserType()); + return Mono.fromCallable(() -> userAccountMapper.findByUsernameAndType(req.getUsername(), userType)) + .subscribeOn(Schedulers.boundedElastic()) + .flatMap(acc -> validatePasswordAndBuild(acc, userType, req.getPassword())); + } + + private String normalizeType(String t) { + if (t == null) return ""; + t = t.trim().toLowerCase(); + if ("admin".equals(t)) return "ADMIN"; + if ("agent".equals(t)) return "AGENT"; + throw new IllegalArgumentException("unsupported userType: " + t); + } + + private Mono validatePasswordAndBuild(UserAccount acc, String userType, String rawPwd) { + if (acc == null || acc.getPasswordHash() == null) { + return Mono.error(new IllegalArgumentException("用户名或密码错误")); + } + boolean ok; + String hash = acc.getPasswordHash(); + if (hash.startsWith("$2a$") || hash.startsWith("$2b$") || hash.startsWith("$2y$")) { + ok = passwordEncoder.matches(rawPwd, hash); + } else if (hash.startsWith("PLAIN:")) { + ok = rawPwd.equals(hash.substring(6)); } else { - return Mono.error(new IllegalArgumentException("unsupported userType: " + userType)); + ok = false; } - } - - private Mono validateAdminPassword(AdminUser admin, String rawPassword) { - if (admin == null || admin.getPasswordHash() == null) { - return Mono.error(new IllegalArgumentException("用户名或密码错误")); - } - boolean ok = passwordEncoder.matches(rawPassword, admin.getPasswordHash()); if (!ok) return Mono.error(new IllegalArgumentException("用户名或密码错误")); - if (!"ENABLED".equalsIgnoreCase(admin.getStatus())) { + if (!"ENABLED".equalsIgnoreCase(acc.getStatus())) { return Mono.error(new IllegalStateException("账户已禁用")); } + String token = jwtService.generateToken( - "admin:" + admin.getId(), - "admin", admin.getId(), admin.getUsername(), Map.of("role", admin.getRole()) + userType.toLowerCase() + ":" + acc.getId(), + userType.toLowerCase(), acc.getId(), acc.getUsername(), + userType.equals("ADMIN") ? Map.of("role", acc.getRole()) : Map.of("displayName", acc.getDisplayName()) ); LoginResponse resp = new LoginResponse(); resp.setAccessToken(token); - resp.setUserType("admin"); - resp.setUserId(admin.getId()); - resp.setUsername(admin.getUsername()); - resp.setExpiresIn(60L * 30); // align with default 30min - return Mono.just(resp); - } - - private Mono validateAgentPassword(Agent agent, String rawPassword) { - if (agent == null || agent.getPasswordHash() == null) { - return Mono.error(new IllegalArgumentException("用户名或密码错误")); - } - boolean ok = passwordEncoder.matches(rawPassword, agent.getPasswordHash()); - if (!ok) return Mono.error(new IllegalArgumentException("用户名或密码错误")); - if (!"ENABLED".equalsIgnoreCase(agent.getStatus())) { - return Mono.error(new IllegalStateException("账户已禁用")); - } - String token = jwtService.generateToken( - "agent:" + agent.getId(), - "agent", agent.getId(), agent.getLoginAccount(), Map.of("name", agent.getName()) - ); - LoginResponse resp = new LoginResponse(); - resp.setAccessToken(token); - resp.setUserType("agent"); - resp.setUserId(agent.getId()); - resp.setUsername(agent.getLoginAccount()); + resp.setUserType(userType.toLowerCase()); + resp.setUserId(acc.getId()); + resp.setUsername(acc.getUsername()); resp.setExpiresIn(60L * 30); return Mono.just(resp); } } - diff --git a/src/main/resources/mapper/UserMapper.xml b/src/main/resources/mapper/UserMapper.xml index 9a7f4a0..b7ebdf1 100644 --- a/src/main/resources/mapper/UserMapper.xml +++ b/src/main/resources/mapper/UserMapper.xml @@ -37,4 +37,3 @@ - diff --git a/src/main/resources/mapper/admin/AdminUserMapper.xml b/src/main/resources/mapper/account/UserAccountMapper.xml similarity index 50% rename from src/main/resources/mapper/admin/AdminUserMapper.xml rename to src/main/resources/mapper/account/UserAccountMapper.xml index 98f50e5..18bbde8 100644 --- a/src/main/resources/mapper/admin/AdminUserMapper.xml +++ b/src/main/resources/mapper/account/UserAccountMapper.xml @@ -1,20 +1,24 @@ - - + + + + + - + SELECT id, user_type, username, display_name, password_hash, role, status, points_balance, created_at, updated_at + FROM user_account WHERE username = #{username} + AND user_type = #{userType} LIMIT 1 diff --git a/src/main/resources/mapper/agent/AgentMapper.xml b/src/main/resources/mapper/agent/AgentMapper.xml deleted file mode 100644 index 621835b..0000000 --- a/src/main/resources/mapper/agent/AgentMapper.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/target/classes/com/gameplatform/server/controller/auth/AuthController$1.class b/target/classes/com/gameplatform/server/controller/auth/AuthController$1.class index b7a738101441a56304788a3f3d9bc67101041cc3..b0336a4c3246a67f412af17181f89ab273610d76 100644 GIT binary patch delta 399 zcmYL_NlF7z5Qe|koup$MCAI^&5ydHK6XO`;JjI>jB4WUew5AD(F-~#Z=oKU&x)QvB zAc7(uK=0rcyn$K~!Gc$R@z-DPf3L31+iLUe>-iO!WZ~X?Q5mn3+0GSBI_Na$3eZhR z5zISBPORV*v$0fZKUd5ueD2<%Q#epGh8F+3%Nu!jvos!F^J6PK`#S~KPeh@+C%e@& z&>J8|U-?6QHq@j_&gRyVv%BJK6kLN*Mf`F-UVhiEya8sJE8pqIb^T}z$n!q%pbMUr zZBO#tYO^0Othy)aH6Cfdq0zR&zg+l_Yfjr%Plb6RszlSAzL#;GLCGx`lDnxTL@Qx2 zuu0NGjtHkj8Io?37>gal#04vr7axidsaGx3x2Sak%^+dO$6!oOm4^2sNL4T>>Tw}7 bCS_*{{W|)45M$e;NRU`XJu9mUiNTXrOtYdCav7rzdZ9^ zOChNteYr@38V4Na6YqYujl~J_fsZG@%Aftgqe~j@lQ3zFn6rGbce2YI0HL!@Jlk8Jr84 zZm>sb(P{ZsZb(G|w=3hS*KOgoak)~yb1cpuii2|*gK>KHSp19h6nkz^6tEt$>L=LEU{Y(bV*5T2os3bLP(Zh|ze*JNJbTT4IB0P12Msw-OHqx!+=3 z+N14Az(IZu6US&hDSTzkUIdGSj@Bjw6*hDIr4(U?u+WD8oa1-{d8eRlpF9nh(oXInX@U+ zp7yyLMW2JP{>F_?ImL`kMK&bEpeb1~XH$t4=_q#NcEqX$n-=MspiC{4ZY=A&cxS0| zPwe^9sRlK-`3japoD|p%o0e2W`@DV`k=(;81!b#VOTKUWCo(*APnrsuiuj3XO2 zqf_I;m7Luw!wFgYPBVkKyFQ#fi&XmqO@Ib*S2Y%VKB6CjEZ91`40H#oC1?b+mwOxf zy?$R7F4+v7LHiLn;JnynH25+qr#|X8kb?(~V+TPytjX6Nb58c=b&cngmN7O7d@~kS z%szGr9#thVh)QDal*G6xi5XK8lLMJ0^oi0~mvM3+1!QXFCy8&$?Iu<>LrJ%=+THza zx`UITyL1oh)!^@BeT6YW{oqMsaG5Ib;^0k4eo1;|^#@t{)mar??08V;YR2L>3tB;3 z3WEV5XqE08cqVk7DI?DVS~KxX=seRyc)0dGo~+_=j6BmiPtM5m(9AQf^W=x{@C@{L z@`~q?k*A>Z6pTEN%{&F2XJ!Zw&ti|KsCb?jd1iHZknHYou@Q}hri7p&!XaaVdS~4^UNE0HqAWOb)JPGJp7O7@hmBx JEvyOJ{s+UWApHOU diff --git a/target/classes/com/gameplatform/server/model/entity/agent/Agent.class b/target/classes/com/gameplatform/server/model/entity/agent/Agent.class deleted file mode 100644 index 35a8571ec88ca15e91d56f559184badec6e61fe2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2181 zcmb7_TTc@~6oAi^UTj_Ll|rG2h?f?WMZ5!|1!FX+NCNUcY{#;s-EDTK82^>$@IaOIWyopw5 z?|YKMrz|KdLyw2yXFq7Y<>3h#W-O?XAsI?8`yr|3ET|IyWY2d!8SZeb*b!v8ZovZF zV32kK!KG+55V@m;K+LAL37Vz1vl7QrDu z?x>&^eY%}oZ0wvg}eMhmri5Jw5)BhJ-fWd>g=kWzoVXf*{#j+q0$ zA2{NTOWn?>nO~(@heaH&7`{1L@$KL)M&qs|4z!Xuze?h`Dv2|yBu=D~IA9z~Iw_MYS`yk8ufx2jW>(Md&<}I#1rnvjz`LJd-+4VGIxT zV8oMEJnKfDyv|cJ@;o&2O^JTmf>be<_A&to%BN#`k#;h~uv@st(M z6C=;G&NFS~d1~gF)_G>e@X*^B@l+JgGvhqZ>O2)A&vWxU&+0t0V|eI|j(DnyX9G4- zrO)%c&NFA^*}{Ki!~2@od8%W0=(l3Tv!HmkjXaAw&%BZ6rI}|@=ea(Hhkmn0JWGn_ J6|OO8`~$=4JX`<( diff --git a/target/classes/com/gameplatform/server/service/auth/AuthService.class b/target/classes/com/gameplatform/server/service/auth/AuthService.class index cbca48fbc246629c3fc01bca713c101dc58c82c6..4e2944a8ea1c6ebd6ac27acee965ee0c3d134856 100644 GIT binary patch literal 7118 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 literal 8164 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 - diff --git a/target/classes/mapper/admin/AdminUserMapper.xml b/target/classes/mapper/admin/AdminUserMapper.xml deleted file mode 100644 index 98f50e5..0000000 --- a/target/classes/mapper/admin/AdminUserMapper.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/target/classes/mapper/agent/AgentMapper.xml b/target/classes/mapper/agent/AgentMapper.xml deleted file mode 100644 index 621835b..0000000 --- a/target/classes/mapper/agent/AgentMapper.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - -