diff --git a/.cursor/rules/zh.mdc b/.cursor/rules/zh.mdc new file mode 100644 index 0000000..dc88b4a --- /dev/null +++ b/.cursor/rules/zh.mdc @@ -0,0 +1,5 @@ +--- +description: +globs: 中文回答我 +alwaysApply: true +--- diff --git a/docs/game.sql b/docs/game.sql index 3468c77..44ace88 100644 --- a/docs/game.sql +++ b/docs/game.sql @@ -1,133 +1,149 @@ --- ============================================================================= --- 上号系统 - 数据库结构 (MySQL 8+) --- ============================================================================= +/* + Navicat Premium Dump SQL --- 可选:创建并使用独立库 -CREATE DATABASE IF NOT EXISTS login_task_db - DEFAULT CHARACTER SET utf8mb4 - DEFAULT COLLATE utf8mb4_0900_ai_ci; -USE login_task_db; + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 80043 (8.0.43) + Source Host : localhost:3306 + Source Schema : login_task_db + + Target Server Type : MySQL + Target Server Version : 80043 (8.0.43) + File Encoding : 65001 + + Date: 24/08/2025 17:24:44 +*/ SET NAMES utf8mb4; -SET sql_mode = 'STRICT_ALL_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO'; +SET FOREIGN_KEY_CHECKS = 0; --- ----------------------------------------------------------------------------- --- 1) 统一账户表(管理员/代理商共用) --- 用 user_type 区分:ADMIN | AGENT --- ----------------------------------------------------------------------------- -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', - 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_points_nonneg CHECK (points_balance >= 0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- ---------------------------- +-- Table structure for agent_points_tx +-- ---------------------------- +DROP TABLE IF EXISTS `agent_points_tx`; +CREATE TABLE `agent_points_tx` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `account_id` bigint UNSIGNED NOT NULL, + `type` enum('ADD','DEDUCT') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `before_points` bigint UNSIGNED NOT NULL, + `delta_points` bigint NOT NULL, + `after_points` bigint UNSIGNED NOT NULL, + `reason` enum('create_links','manual','refund_no_rollback','other') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'other', + `ref_id` bigint UNSIGNED NULL DEFAULT NULL, + `operator_id` bigint UNSIGNED NULL DEFAULT NULL, + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_apx_account_time`(`account_id` ASC, `created_at` ASC) USING BTREE, + INDEX `fk_apx_operator`(`operator_id` ASC) USING BTREE, + CONSTRAINT `fk_apx_account` FOREIGN KEY (`account_id`) REFERENCES `user_account` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `fk_apx_operator` FOREIGN KEY (`operator_id`) REFERENCES `user_account` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; --- 默认管理员账号(密码: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; +-- ---------------------------- +-- Table structure for announcement +-- ---------------------------- +DROP TABLE IF EXISTS `announcement`; +CREATE TABLE `announcement` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT 1, + `jump_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 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), + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; --- ----------------------------------------------------------------------------- --- 2) 代理商点数流水 --- ----------------------------------------------------------------------------- -CREATE TABLE IF NOT EXISTS agent_points_tx ( - 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 对应 - 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, -- 操作者(管理员,指向 user_account) - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - 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; +-- ---------------------------- +-- Table structure for link_batch +-- ---------------------------- +DROP TABLE IF EXISTS `link_batch`; +CREATE TABLE `link_batch` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `agent_id` bigint UNSIGNED NOT NULL, + `quantity` int UNSIGNED NOT NULL, + `times` int UNSIGNED NOT NULL, + `batch_size` int UNSIGNED NOT NULL, + `deduct_points` bigint UNSIGNED NOT NULL, + `operator_id` bigint UNSIGNED NULL DEFAULT NULL, + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_lb_agent_time`(`agent_id` ASC, `created_at` ASC) USING BTREE, + INDEX `fk_lb_operator`(`operator_id` ASC) USING BTREE, + CONSTRAINT `fk_lb_agent` FOREIGN KEY (`agent_id`) REFERENCES `user_account` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `fk_lb_operator` FOREIGN KEY (`operator_id`) REFERENCES `user_account` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `chk_lb_batch_pos` CHECK (`batch_size` > 0), + CONSTRAINT `chk_lb_quantity_pos` CHECK (`quantity` > 0), + CONSTRAINT `chk_lb_times_pos` CHECK (`times` > 0) +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; --- ----------------------------------------------------------------------------- --- 3) 链接批次(一次生成 N 个链接,按统一设置扣费) --- ----------------------------------------------------------------------------- -CREATE TABLE IF NOT EXISTS link_batch ( - 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 user_account(id), - CONSTRAINT fk_lb_operator FOREIGN KEY (operator_id) REFERENCES user_account(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- ---------------------------- +-- Table structure for link_task +-- ---------------------------- +DROP TABLE IF EXISTS `link_task`; +CREATE TABLE `link_task` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `batch_id` bigint UNSIGNED NOT NULL, + `agent_id` bigint UNSIGNED NOT NULL, + `code_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `token_hash` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `expire_at` datetime(3) NOT NULL, + `status` enum('NEW','USING','LOGGED_IN','REFUNDED','EXPIRED') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'NEW', + `region` enum('Q','V') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `machine_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `login_at` datetime(3) NULL DEFAULT NULL, + `refund_at` datetime(3) NULL DEFAULT NULL, + `revoked_at` datetime(3) NULL DEFAULT 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), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_code_no`(`code_no` ASC) USING BTREE, + UNIQUE INDEX `uk_token_hash`(`token_hash` ASC) USING BTREE, + INDEX `idx_agent_status`(`agent_id` ASC, `status` ASC) USING BTREE, + INDEX `idx_expire_at`(`expire_at` ASC) USING BTREE, + INDEX `idx_created_at`(`created_at` ASC) USING BTREE, + INDEX `fk_lt_batch`(`batch_id` ASC) USING BTREE, + CONSTRAINT `fk_lt_agent` FOREIGN KEY (`agent_id`) REFERENCES `user_account` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `fk_lt_batch` FOREIGN KEY (`batch_id`) REFERENCES `link_batch` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; --- ----------------------------------------------------------------------------- --- 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, -- 指向 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, -- 绑定的脚本端机器编号 - login_at DATETIME(3) NULL, - refund_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), - UNIQUE KEY uk_token_hash (token_hash), - INDEX idx_agent_status (agent_id, status), - 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 user_account(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- ---------------------------- +-- Table structure for operation_log +-- ---------------------------- +DROP TABLE IF EXISTS `operation_log`; +CREATE TABLE `operation_log` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `actor_type` enum('admin','agent','system','user') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `actor_id` bigint UNSIGNED NULL DEFAULT NULL, + `code_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `op` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `detail` json NULL, + `client_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_log_code_time`(`code_no` ASC, `created_at` ASC) USING BTREE, + INDEX `idx_log_time`(`created_at` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; --- ----------------------------------------------------------------------------- --- 5) 操作日志(审计/可观测性) --- ----------------------------------------------------------------------------- -CREATE TABLE IF NOT EXISTS operation_log ( - 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 - detail JSON NULL, -- 具体参数/返回(注意脱敏) - client_ip VARCHAR(45) NULL, - user_agent VARCHAR(255) NULL, - 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; - --- ----------------------------------------------------------------------------- --- 6) 公告 --- ----------------------------------------------------------------------------- -CREATE TABLE IF NOT EXISTS announcement ( - 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) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- ---------------------------- +-- Table structure for user_account +-- ---------------------------- +DROP TABLE IF EXISTS `user_account`; +CREATE TABLE `user_account` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `user_type` enum('ADMIN','AGENT') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `display_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `password_hash` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `role` enum('SUPER','ADMIN') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `status` enum('ENABLED','DISABLED') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ENABLED', + `points_balance` bigint UNSIGNED NOT NULL DEFAULT 0, + `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), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `username`(`username` ASC) USING BTREE, + CONSTRAINT `chk_points_nonneg` CHECK (`points_balance` >= 0) +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; +SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/java/com/gameplatform/server/controller/UserController.java b/src/main/java/com/gameplatform/server/controller/UserController.java index 5230b5d..b34100f 100644 --- a/src/main/java/com/gameplatform/server/controller/UserController.java +++ b/src/main/java/com/gameplatform/server/controller/UserController.java @@ -1,48 +1,83 @@ package com.gameplatform.server.controller; -import com.gameplatform.server.model.User; -import com.gameplatform.server.service.UserService; +import com.gameplatform.server.model.dto.account.AccountCreateRequest; +import com.gameplatform.server.model.dto.account.AccountResponse; +import com.gameplatform.server.model.dto.account.AccountUpdateRequest; +import com.gameplatform.server.model.dto.common.PageResult; +import com.gameplatform.server.service.account.AccountService; import jakarta.validation.Valid; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +/** + * 用户接口控制器 - 基于UserAccount实体 + * 提供用户账户的基本CRUD操作 + */ @RestController @RequestMapping("/api/users") -public class UserController { - private final UserService userService; +public class UserController { + private final AccountService accountService; - public UserController(UserService userService) { - this.userService = userService; + public UserController(AccountService accountService) { + this.accountService = accountService; } + /** + * 根据ID获取用户账户信息 + */ @GetMapping("/{id}") - public Mono getById(@PathVariable Long id) { - return userService.getById(id); + public Mono getById(@PathVariable Long id) { + return accountService.get(id); } + /** + * 分页查询用户列表 + */ @GetMapping - public Flux listAll() { - return userService.listAll(); + public Mono> list( + @RequestParam(value = "userType", required = false) String userType, + @RequestParam(value = "status", required = false) String status, + @RequestParam(value = "role", required = false) String role, + @RequestParam(value = "keyword", required = false) String keyword, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "size", defaultValue = "20") Integer size + ) { + return accountService.list(userType, status, role, keyword, page, size); } + /** + * 创建新用户账户 + */ @PostMapping @ResponseStatus(HttpStatus.CREATED) - public Mono create(@Valid @RequestBody User user) { - return userService.create(user); - } - - @DeleteMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public Mono delete(@PathVariable Long id) { - return userService.deleteById(id) - .filter(Boolean::booleanValue) - .then(); + public Mono create(@Valid @RequestBody AccountCreateRequest request) { + return accountService.create(request); } + /** + * 更新用户账户信息 + */ @PutMapping("/{id}") - public Mono update(@PathVariable Long id, @Valid @RequestBody User user) { - return userService.update(id, user); + public Mono update(@PathVariable Long id, @Valid @RequestBody AccountUpdateRequest request) { + return accountService.update(id, request); } -} + + /** + * 启用用户账户 + */ + @PostMapping("/{id}/enable") + @ResponseStatus(HttpStatus.NO_CONTENT) + public Mono enable(@PathVariable Long id) { + return accountService.setStatus(id, "ENABLED").then(); + } + + /** + * 禁用用户账户 + */ + @PostMapping("/{id}/disable") + @ResponseStatus(HttpStatus.NO_CONTENT) + public Mono disable(@PathVariable Long id) { + return accountService.setStatus(id, "DISABLED").then(); + } +} \ No newline at end of file diff --git a/src/main/java/com/gameplatform/server/mapper/UserMapper.java b/src/main/java/com/gameplatform/server/mapper/UserMapper.java deleted file mode 100644 index a11afed..0000000 --- a/src/main/java/com/gameplatform/server/mapper/UserMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.gameplatform.server.mapper; - -import com.gameplatform.server.model.User; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -public interface UserMapper { - User findById(@Param("id") Long id); - - List findAll(); - - int insert(User user); - - int deleteById(@Param("id") Long id); - - int update(User user); -} diff --git a/src/main/java/com/gameplatform/server/model/User.java b/src/main/java/com/gameplatform/server/model/User.java deleted file mode 100644 index fbe2c0a..0000000 --- a/src/main/java/com/gameplatform/server/model/User.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gameplatform.server.model; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; - -import java.time.LocalDateTime; - -public class User { - private Long id; - - @NotBlank - private String username; - - @Email - private String email; - - private LocalDateTime createdAt; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } -} - diff --git a/src/main/java/com/gameplatform/server/security/JwtService.java b/src/main/java/com/gameplatform/server/security/JwtService.java index 1c90e75..c84e6a3 100644 --- a/src/main/java/com/gameplatform/server/security/JwtService.java +++ b/src/main/java/com/gameplatform/server/security/JwtService.java @@ -2,7 +2,7 @@ package com.gameplatform.server.security; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.io.Decoders; + import io.jsonwebtoken.security.Keys; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/gameplatform/server/security/SecurityConfig.java b/src/main/java/com/gameplatform/server/security/SecurityConfig.java index ad9807e..1800fc2 100644 --- a/src/main/java/com/gameplatform/server/security/SecurityConfig.java +++ b/src/main/java/com/gameplatform/server/security/SecurityConfig.java @@ -3,7 +3,7 @@ package com.gameplatform.server.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; + import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; diff --git a/src/main/java/com/gameplatform/server/service/UserService.java b/src/main/java/com/gameplatform/server/service/UserService.java index beb4f85..72e4177 100644 --- a/src/main/java/com/gameplatform/server/service/UserService.java +++ b/src/main/java/com/gameplatform/server/service/UserService.java @@ -1,54 +1,92 @@ package com.gameplatform.server.service; -import com.gameplatform.server.mapper.UserMapper; -import com.gameplatform.server.model.User; +import com.gameplatform.server.mapper.account.UserAccountMapper; +import com.gameplatform.server.model.dto.account.AccountResponse; +import com.gameplatform.server.model.dto.common.PageResult; +import com.gameplatform.server.model.entity.account.UserAccount; import org.springframework.stereotype.Service; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import java.util.Objects; +import java.util.List; +import java.util.stream.Collectors; +/** + * 用户服务类 - 基于UserAccount实体 + * 提供用户账户相关的业务逻辑 + */ @Service public class UserService { - private final UserMapper userMapper; + private final UserAccountMapper userAccountMapper; - public UserService(UserMapper userMapper) { - this.userMapper = userMapper; + public UserService(UserAccountMapper userAccountMapper) { + this.userAccountMapper = userAccountMapper; } - public Mono getById(Long id) { - return Mono.fromCallable(() -> userMapper.findById(id)) + /** + * 根据ID获取用户账户 + */ + public Mono getById(Long id) { + return Mono.fromCallable(() -> userAccountMapper.findById(id)) + .subscribeOn(Schedulers.boundedElastic()) + .filter(Objects::nonNull) + .map(this::toAccountResponse); + } + + /** + * 根据用户名获取用户账户 + */ + public Mono getByUsername(String username) { + return Mono.fromCallable(() -> userAccountMapper.findByUsername(username)) .subscribeOn(Schedulers.boundedElastic()) .filter(Objects::nonNull); } - public Flux listAll() { - return Mono.fromCallable(userMapper::findAll) - .subscribeOn(Schedulers.boundedElastic()) - .flatMapMany(Flux::fromIterable); - } - - public Mono create(User user) { + /** + * 分页查询用户列表 + */ + public Mono> list(String userType, String status, String role, String keyword, + Integer page, Integer size) { + int p = (page == null || page < 1) ? 1 : page; + int s = (size == null || size < 1 || size > 200) ? 20 : size; + int offset = (p - 1) * s; + return Mono.fromCallable(() -> { - userMapper.insert(user); - return user; + long total = userAccountMapper.countByFilter(userType, status, role, keyword); + List list = userAccountMapper.listByFilter(userType, status, role, keyword, s, offset); + List items = list.stream() + .map(this::toAccountResponse) + .collect(Collectors.toList()); + return new PageResult<>(items, total, p, s); }) .subscribeOn(Schedulers.boundedElastic()); } - public Mono deleteById(Long id) { - return Mono.fromCallable(() -> userMapper.deleteById(id) > 0) + /** + * 检查用户名是否存在 + */ + public Mono existsByUsername(String username) { + return Mono.fromCallable(() -> userAccountMapper.findByUsername(username) != null) .subscribeOn(Schedulers.boundedElastic()); } - public Mono update(Long id, User user) { - return Mono.fromCallable(() -> { - user.setId(id); - int n = userMapper.update(user); - return n; - }) - .subscribeOn(Schedulers.boundedElastic()) - .flatMap(n -> n > 0 ? getById(id) : Mono.empty()); + /** + * 将UserAccount实体转换为AccountResponse DTO + */ + private AccountResponse toAccountResponse(UserAccount account) { + if (account == null) return null; + + AccountResponse response = new AccountResponse(); + response.setId(account.getId()); + response.setUserType(account.getUserType()); + response.setUsername(account.getUsername()); + response.setDisplayName(account.getDisplayName()); + response.setRole(account.getRole()); + response.setStatus(account.getStatus()); + response.setPointsBalance(account.getPointsBalance()); + response.setCreatedAt(account.getCreatedAt()); + response.setUpdatedAt(account.getUpdatedAt()); + return response; } } diff --git a/src/main/resources/mapper/UserMapper.xml b/src/main/resources/mapper/UserMapper.xml deleted file mode 100644 index beaa840..0000000 --- a/src/main/resources/mapper/UserMapper.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - id, username, email, created_at - - - - - - - - INSERT INTO users (username, email, created_at) - VALUES (#{username}, #{email}, NOW()) - - - - DELETE FROM users WHERE id = #{id} - - - - UPDATE users - SET username = #{username}, - email = #{email} - WHERE id = #{id} - - - diff --git a/target/classes/com/gameplatform/server/GamePlatformServerApplication.class b/target/classes/com/gameplatform/server/GamePlatformServerApplication.class index 91b7441..48d8854 100644 Binary files a/target/classes/com/gameplatform/server/GamePlatformServerApplication.class and b/target/classes/com/gameplatform/server/GamePlatformServerApplication.class differ diff --git a/target/classes/com/gameplatform/server/config/CorsConfig.class b/target/classes/com/gameplatform/server/config/CorsConfig.class index 9b5d160..6faec7c 100644 Binary files a/target/classes/com/gameplatform/server/config/CorsConfig.class and b/target/classes/com/gameplatform/server/config/CorsConfig.class differ diff --git a/target/classes/com/gameplatform/server/controller/UserController.class b/target/classes/com/gameplatform/server/controller/UserController.class index 1b5da30..d7d9d2e 100644 Binary files a/target/classes/com/gameplatform/server/controller/UserController.class and b/target/classes/com/gameplatform/server/controller/UserController.class differ diff --git a/target/classes/com/gameplatform/server/controller/admin/AccountController.class b/target/classes/com/gameplatform/server/controller/admin/AccountController.class new file mode 100644 index 0000000..ce66e17 Binary files /dev/null and b/target/classes/com/gameplatform/server/controller/admin/AccountController.class differ 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 542d1f9..f1dcb43 100644 Binary files a/target/classes/com/gameplatform/server/controller/auth/AuthController$1.class and b/target/classes/com/gameplatform/server/controller/auth/AuthController$1.class differ diff --git a/target/classes/com/gameplatform/server/controller/auth/AuthController.class b/target/classes/com/gameplatform/server/controller/auth/AuthController.class index 5a46935..bde12b3 100644 Binary files a/target/classes/com/gameplatform/server/controller/auth/AuthController.class and b/target/classes/com/gameplatform/server/controller/auth/AuthController.class differ diff --git a/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$1.class b/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$1.class index 315d986..35b664e 100644 Binary files a/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$1.class and b/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$1.class differ diff --git a/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$2.class b/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$2.class index 88214a0..3d9a1c2 100644 Binary files a/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$2.class and b/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler$2.class differ diff --git a/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler.class b/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler.class index 216ec5d..3b797d3 100644 Binary files a/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler.class and b/target/classes/com/gameplatform/server/exception/GlobalExceptionHandler.class differ diff --git a/target/classes/com/gameplatform/server/mapper/UserMapper.class b/target/classes/com/gameplatform/server/mapper/UserMapper.class deleted file mode 100644 index e344c5c..0000000 Binary files a/target/classes/com/gameplatform/server/mapper/UserMapper.class and /dev/null differ diff --git a/target/classes/com/gameplatform/server/mapper/account/UserAccountMapper.class b/target/classes/com/gameplatform/server/mapper/account/UserAccountMapper.class index c186692..f1dcec5 100644 Binary files a/target/classes/com/gameplatform/server/mapper/account/UserAccountMapper.class and b/target/classes/com/gameplatform/server/mapper/account/UserAccountMapper.class differ diff --git a/target/classes/com/gameplatform/server/model/User.class b/target/classes/com/gameplatform/server/model/User.class deleted file mode 100644 index 9b4483a..0000000 Binary files a/target/classes/com/gameplatform/server/model/User.class and /dev/null differ diff --git a/target/classes/com/gameplatform/server/model/dto/account/AccountCreateRequest.class b/target/classes/com/gameplatform/server/model/dto/account/AccountCreateRequest.class new file mode 100644 index 0000000..0ab009d Binary files /dev/null and b/target/classes/com/gameplatform/server/model/dto/account/AccountCreateRequest.class differ diff --git a/target/classes/com/gameplatform/server/model/dto/account/AccountResponse.class b/target/classes/com/gameplatform/server/model/dto/account/AccountResponse.class new file mode 100644 index 0000000..ff99eb5 Binary files /dev/null and b/target/classes/com/gameplatform/server/model/dto/account/AccountResponse.class differ diff --git a/target/classes/com/gameplatform/server/model/dto/account/AccountUpdateRequest.class b/target/classes/com/gameplatform/server/model/dto/account/AccountUpdateRequest.class new file mode 100644 index 0000000..e5f613d Binary files /dev/null and b/target/classes/com/gameplatform/server/model/dto/account/AccountUpdateRequest.class differ diff --git a/target/classes/com/gameplatform/server/model/dto/account/ResetPasswordRequest.class b/target/classes/com/gameplatform/server/model/dto/account/ResetPasswordRequest.class new file mode 100644 index 0000000..695cdef Binary files /dev/null and b/target/classes/com/gameplatform/server/model/dto/account/ResetPasswordRequest.class differ diff --git a/target/classes/com/gameplatform/server/model/dto/auth/LoginRequest.class b/target/classes/com/gameplatform/server/model/dto/auth/LoginRequest.class index bf762f2..416d507 100644 Binary files a/target/classes/com/gameplatform/server/model/dto/auth/LoginRequest.class and b/target/classes/com/gameplatform/server/model/dto/auth/LoginRequest.class differ diff --git a/target/classes/com/gameplatform/server/model/dto/auth/LoginResponse.class b/target/classes/com/gameplatform/server/model/dto/auth/LoginResponse.class index 2557f1e..2cb34ad 100644 Binary files a/target/classes/com/gameplatform/server/model/dto/auth/LoginResponse.class and b/target/classes/com/gameplatform/server/model/dto/auth/LoginResponse.class differ diff --git a/target/classes/com/gameplatform/server/model/dto/common/PageResult.class b/target/classes/com/gameplatform/server/model/dto/common/PageResult.class new file mode 100644 index 0000000..5a381ba Binary files /dev/null and b/target/classes/com/gameplatform/server/model/dto/common/PageResult.class differ diff --git a/target/classes/com/gameplatform/server/model/entity/account/UserAccount.class b/target/classes/com/gameplatform/server/model/entity/account/UserAccount.class index e20bf45..0ecf4eb 100644 Binary files a/target/classes/com/gameplatform/server/model/entity/account/UserAccount.class and b/target/classes/com/gameplatform/server/model/entity/account/UserAccount.class differ diff --git a/target/classes/com/gameplatform/server/security/JwtService.class b/target/classes/com/gameplatform/server/security/JwtService.class index e741cfc..b6a4e1c 100644 Binary files a/target/classes/com/gameplatform/server/security/JwtService.class and b/target/classes/com/gameplatform/server/security/JwtService.class differ diff --git a/target/classes/com/gameplatform/server/security/SecurityConfig.class b/target/classes/com/gameplatform/server/security/SecurityConfig.class index c97bdc4..b167e2b 100644 Binary files a/target/classes/com/gameplatform/server/security/SecurityConfig.class and b/target/classes/com/gameplatform/server/security/SecurityConfig.class differ diff --git a/target/classes/com/gameplatform/server/service/UserService.class b/target/classes/com/gameplatform/server/service/UserService.class index 8706941..9daad0a 100644 Binary files a/target/classes/com/gameplatform/server/service/UserService.class and b/target/classes/com/gameplatform/server/service/UserService.class differ diff --git a/target/classes/com/gameplatform/server/service/account/AccountService.class b/target/classes/com/gameplatform/server/service/account/AccountService.class new file mode 100644 index 0000000..2a0b690 Binary files /dev/null and b/target/classes/com/gameplatform/server/service/account/AccountService.class differ diff --git a/target/classes/com/gameplatform/server/service/auth/AuthService.class b/target/classes/com/gameplatform/server/service/auth/AuthService.class index 225381c..fddf891 100644 Binary files a/target/classes/com/gameplatform/server/service/auth/AuthService.class and b/target/classes/com/gameplatform/server/service/auth/AuthService.class differ diff --git a/target/classes/mapper/UserMapper.xml b/target/classes/mapper/UserMapper.xml deleted file mode 100644 index b7ebdf1..0000000 --- a/target/classes/mapper/UserMapper.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - id, username, email, created_at - - - - - - - - INSERT INTO users (username, email, created_at) - VALUES (#{username}, #{email}, NOW()) - - - - DELETE FROM users WHERE id = #{id} - - - diff --git a/target/classes/mapper/account/UserAccountMapper.xml b/target/classes/mapper/account/UserAccountMapper.xml index 3736788..578d906 100644 --- a/target/classes/mapper/account/UserAccountMapper.xml +++ b/target/classes/mapper/account/UserAccountMapper.xml @@ -28,4 +28,61 @@ WHERE username = #{username} LIMIT 1 + + + + + INSERT INTO user_account (user_type, username, display_name, password_hash, role, status, points_balance) + VALUES (#{userType}, #{username}, #{displayName}, #{passwordHash}, #{role}, #{status}, #{pointsBalance}) + + + + UPDATE user_account + + display_name = #{displayName}, + role = #{role}, + status = #{status}, + + WHERE id = #{id} + + + + UPDATE user_account SET status = #{status} WHERE id = #{id} + + + + UPDATE user_account SET password_hash = #{passwordHash} WHERE id = #{id} + + + + +