Refactor user account management by replacing User entity with UserAccount, updating UserController and UserService for CRUD operations, and modifying MyBatis mappers accordingly.
This commit is contained in:
5
.cursor/rules/zh.mdc
Normal file
5
.cursor/rules/zh.mdc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs: 中文回答我
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
262
docs/game.sql
262
docs/game.sql
@@ -1,133 +1,149 @@
|
|||||||
-- =============================================================================
|
/*
|
||||||
-- 上号系统 - 数据库结构 (MySQL 8+)
|
Navicat Premium Dump SQL
|
||||||
-- =============================================================================
|
|
||||||
|
|
||||||
-- 可选:创建并使用独立库
|
Source Server : localhost
|
||||||
CREATE DATABASE IF NOT EXISTS login_task_db
|
Source Server Type : MySQL
|
||||||
DEFAULT CHARACTER SET utf8mb4
|
Source Server Version : 80043 (8.0.43)
|
||||||
DEFAULT COLLATE utf8mb4_0900_ai_ci;
|
Source Host : localhost:3306
|
||||||
USE login_task_db;
|
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 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) 统一账户表(管理员/代理商共用)
|
-- Table structure for agent_points_tx
|
||||||
-- 用 user_type 区分:ADMIN | AGENT
|
-- ----------------------------
|
||||||
-- -----------------------------------------------------------------------------
|
DROP TABLE IF EXISTS `agent_points_tx`;
|
||||||
CREATE TABLE IF NOT EXISTS user_account (
|
CREATE TABLE `agent_points_tx` (
|
||||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
user_type ENUM('ADMIN','AGENT') NOT NULL,
|
`account_id` bigint UNSIGNED NOT NULL,
|
||||||
username VARCHAR(64) NOT NULL UNIQUE, -- 登录名(两类共用)
|
`type` enum('ADD','DEDUCT') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
display_name VARCHAR(100) NULL, -- 展示名(AGENT 可用)
|
`before_points` bigint UNSIGNED NOT NULL,
|
||||||
password_hash VARCHAR(120) NOT NULL, -- 建议存储 BCrypt(或临时 PLAIN:<pwd> 便于初始化)
|
`delta_points` bigint NOT NULL,
|
||||||
role ENUM('SUPER','ADMIN') NULL, -- 仅 ADMIN 使用
|
`after_points` bigint UNSIGNED NOT NULL,
|
||||||
status ENUM('ENABLED','DISABLED') NOT NULL DEFAULT 'ENABLED',
|
`reason` enum('create_links','manual','refund_no_rollback','other') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'other',
|
||||||
points_balance BIGINT UNSIGNED NOT NULL DEFAULT 0, -- 仅 AGENT 使用
|
`ref_id` bigint UNSIGNED NULL DEFAULT NULL,
|
||||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
`operator_id` bigint UNSIGNED NULL DEFAULT NULL,
|
||||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
CONSTRAINT chk_points_nonneg CHECK (points_balance >= 0)
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
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 方便首次初始化。
|
-- Table structure for announcement
|
||||||
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)
|
DROP TABLE IF EXISTS `announcement`;
|
||||||
ON DUPLICATE KEY UPDATE username = username;
|
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) 代理商点数流水
|
-- Table structure for link_batch
|
||||||
-- -----------------------------------------------------------------------------
|
-- ----------------------------
|
||||||
CREATE TABLE IF NOT EXISTS agent_points_tx (
|
DROP TABLE IF EXISTS `link_batch`;
|
||||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
CREATE TABLE `link_batch` (
|
||||||
account_id BIGINT UNSIGNED NOT NULL, -- 指向 user_account(AGENT)
|
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
type ENUM('ADD','DEDUCT') NOT NULL,
|
`agent_id` bigint UNSIGNED NOT NULL,
|
||||||
before_points BIGINT UNSIGNED NOT NULL,
|
`quantity` int UNSIGNED NOT NULL,
|
||||||
delta_points BIGINT SIGNED NOT NULL, -- 可为正/负;与 type 对应
|
`times` int UNSIGNED NOT NULL,
|
||||||
after_points BIGINT UNSIGNED NOT NULL,
|
`batch_size` int UNSIGNED NOT NULL,
|
||||||
reason ENUM('create_links','manual','refund_no_rollback','other') NOT NULL DEFAULT 'other',
|
`deduct_points` bigint UNSIGNED NOT NULL,
|
||||||
ref_id BIGINT UNSIGNED NULL, -- 可关联到 link_batch.id
|
`operator_id` bigint UNSIGNED NULL DEFAULT NULL,
|
||||||
operator_id BIGINT UNSIGNED NULL, -- 操作者(管理员,指向 user_account)
|
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
INDEX idx_apx_account_time (account_id, created_at),
|
INDEX `idx_lb_agent_time`(`agent_id` ASC, `created_at` ASC) USING BTREE,
|
||||||
CONSTRAINT fk_apx_account FOREIGN KEY (account_id) REFERENCES user_account(id),
|
INDEX `fk_lb_operator`(`operator_id` ASC) USING BTREE,
|
||||||
CONSTRAINT fk_apx_operator FOREIGN KEY (operator_id) REFERENCES user_account(id)
|
CONSTRAINT `fk_lb_agent` FOREIGN KEY (`agent_id`) REFERENCES `user_account` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
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 个链接,按统一设置扣费)
|
-- Table structure for link_task
|
||||||
-- -----------------------------------------------------------------------------
|
-- ----------------------------
|
||||||
CREATE TABLE IF NOT EXISTS link_batch (
|
DROP TABLE IF EXISTS `link_task`;
|
||||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
CREATE TABLE `link_task` (
|
||||||
agent_id BIGINT UNSIGNED NOT NULL, -- 指向 user_account(AGENT)
|
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
quantity INT UNSIGNED NOT NULL, -- 每次奖励数量(如 50)
|
`batch_id` bigint UNSIGNED NOT NULL,
|
||||||
times INT UNSIGNED NOT NULL, -- 重复执行次数(如 20)
|
`agent_id` bigint UNSIGNED NOT NULL,
|
||||||
batch_size INT UNSIGNED NOT NULL, -- 本批生成链接数量(如 10)
|
`code_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
deduct_points BIGINT UNSIGNED NOT NULL, -- 扣点=quantity*times*batch_size
|
`token_hash` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
operator_id BIGINT UNSIGNED NULL, -- 操作者(管理员)
|
`expire_at` datetime(3) NOT NULL,
|
||||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
`status` enum('NEW','USING','LOGGED_IN','REFUNDED','EXPIRED') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'NEW',
|
||||||
INDEX idx_lb_agent_time (agent_id, created_at),
|
`region` enum('Q','V') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
CONSTRAINT chk_lb_quantity_pos CHECK (quantity > 0),
|
`machine_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
CONSTRAINT chk_lb_times_pos CHECK (times > 0),
|
`login_at` datetime(3) NULL DEFAULT NULL,
|
||||||
CONSTRAINT chk_lb_batch_pos CHECK (batch_size > 0),
|
`refund_at` datetime(3) NULL DEFAULT NULL,
|
||||||
CONSTRAINT fk_lb_agent FOREIGN KEY (agent_id) REFERENCES user_account(id),
|
`revoked_at` datetime(3) NULL DEFAULT NULL,
|
||||||
CONSTRAINT fk_lb_operator FOREIGN KEY (operator_id) REFERENCES user_account(id)
|
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
`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) 单链接任务(用户访问的“加密链接”对应的实体)
|
-- Table structure for operation_log
|
||||||
-- -----------------------------------------------------------------------------
|
-- ----------------------------
|
||||||
CREATE TABLE IF NOT EXISTS link_task (
|
DROP TABLE IF EXISTS `operation_log`;
|
||||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
CREATE TABLE `operation_log` (
|
||||||
batch_id BIGINT UNSIGNED NOT NULL,
|
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
agent_id BIGINT UNSIGNED NOT NULL, -- 指向 user_account(AGENT)
|
`actor_type` enum('admin','agent','system','user') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
code_no VARCHAR(32) NOT NULL, -- 后端生成的全局唯一编号
|
`actor_id` bigint UNSIGNED NULL DEFAULT NULL,
|
||||||
token_hash CHAR(64) NOT NULL, -- 加密token的SHA-256(用于失效/撤销)
|
`code_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
expire_at DATETIME(3) NOT NULL, -- 链接有效期(默认 24h)
|
`op` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
status ENUM('NEW','USING','LOGGED_IN','REFUNDED','EXPIRED') NOT NULL DEFAULT 'NEW',
|
`detail` json NULL,
|
||||||
region ENUM('Q','V') NULL, -- 选区;未选择前为 NULL
|
`client_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
machine_id VARCHAR(64) NULL, -- 绑定的脚本端机器编号
|
`user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
login_at DATETIME(3) NULL,
|
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
refund_at DATETIME(3) NULL,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
revoked_at DATETIME(3) NULL, -- 主动撤销(如需要立即失效)
|
INDEX `idx_log_code_time`(`code_no` ASC, `created_at` ASC) USING BTREE,
|
||||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
INDEX `idx_log_time`(`created_at` ASC) USING BTREE
|
||||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||||
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;
|
|
||||||
|
|
||||||
-- -----------------------------------------------------------------------------
|
-- ----------------------------
|
||||||
-- 5) 操作日志(审计/可观测性)
|
-- Table structure for user_account
|
||||||
-- -----------------------------------------------------------------------------
|
-- ----------------------------
|
||||||
CREATE TABLE IF NOT EXISTS operation_log (
|
DROP TABLE IF EXISTS `user_account`;
|
||||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
CREATE TABLE `user_account` (
|
||||||
actor_type ENUM('admin','agent','system','user') NOT NULL,
|
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
actor_id BIGINT UNSIGNED NULL, -- 不强制外键,避免多态复杂度
|
`user_type` enum('ADMIN','AGENT') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
code_no VARCHAR(32) NULL,
|
`username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
op VARCHAR(64) NOT NULL, -- 如:create_links / refund / select_region / create_qr / poll_login / release_machine
|
`display_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
detail JSON NULL, -- 具体参数/返回(注意脱敏)
|
`password_hash` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
client_ip VARCHAR(45) NULL,
|
`role` enum('SUPER','ADMIN') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
user_agent VARCHAR(255) NULL,
|
`status` enum('ENABLED','DISABLED') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ENABLED',
|
||||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
`points_balance` bigint UNSIGNED NOT NULL DEFAULT 0,
|
||||||
INDEX idx_log_code_time (code_no, created_at),
|
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
INDEX idx_log_time (created_at)
|
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
UNIQUE INDEX `username`(`username` ASC) USING BTREE,
|
||||||
-- -----------------------------------------------------------------------------
|
CONSTRAINT `chk_points_nonneg` CHECK (`points_balance` >= 0)
|
||||||
-- 6) 公告
|
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||||
-- -----------------------------------------------------------------------------
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|||||||
@@ -1,48 +1,83 @@
|
|||||||
package com.gameplatform.server.controller;
|
package com.gameplatform.server.controller;
|
||||||
|
|
||||||
import com.gameplatform.server.model.User;
|
import com.gameplatform.server.model.dto.account.AccountCreateRequest;
|
||||||
import com.gameplatform.server.service.UserService;
|
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 jakarta.validation.Valid;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户接口控制器 - 基于UserAccount实体
|
||||||
|
* 提供用户账户的基本CRUD操作
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/users")
|
@RequestMapping("/api/users")
|
||||||
public class UserController {
|
public class UserController {
|
||||||
private final UserService userService;
|
private final AccountService accountService;
|
||||||
|
|
||||||
public UserController(UserService userService) {
|
public UserController(AccountService accountService) {
|
||||||
this.userService = userService;
|
this.accountService = accountService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取用户账户信息
|
||||||
|
*/
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Mono<User> getById(@PathVariable Long id) {
|
public Mono<AccountResponse> getById(@PathVariable Long id) {
|
||||||
return userService.getById(id);
|
return accountService.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询用户列表
|
||||||
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Flux<User> listAll() {
|
public Mono<PageResult<AccountResponse>> list(
|
||||||
return userService.listAll();
|
@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
|
@PostMapping
|
||||||
@ResponseStatus(HttpStatus.CREATED)
|
@ResponseStatus(HttpStatus.CREATED)
|
||||||
public Mono<User> create(@Valid @RequestBody User user) {
|
public Mono<AccountResponse> create(@Valid @RequestBody AccountCreateRequest request) {
|
||||||
return userService.create(user);
|
return accountService.create(request);
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
|
||||||
public Mono<Void> delete(@PathVariable Long id) {
|
|
||||||
return userService.deleteById(id)
|
|
||||||
.filter(Boolean::booleanValue)
|
|
||||||
.then();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户账户信息
|
||||||
|
*/
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
public Mono<User> update(@PathVariable Long id, @Valid @RequestBody User user) {
|
public Mono<AccountResponse> update(@PathVariable Long id, @Valid @RequestBody AccountUpdateRequest request) {
|
||||||
return userService.update(id, user);
|
return accountService.update(id, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用用户账户
|
||||||
|
*/
|
||||||
|
@PostMapping("/{id}/enable")
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
public Mono<Void> enable(@PathVariable Long id) {
|
||||||
|
return accountService.setStatus(id, "ENABLED").then();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用用户账户
|
||||||
|
*/
|
||||||
|
@PostMapping("/{id}/disable")
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
public Mono<Void> disable(@PathVariable Long id) {
|
||||||
|
return accountService.setStatus(id, "DISABLED").then();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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<User> findAll();
|
|
||||||
|
|
||||||
int insert(User user);
|
|
||||||
|
|
||||||
int deleteById(@Param("id") Long id);
|
|
||||||
|
|
||||||
int update(User user);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ package com.gameplatform.server.security;
|
|||||||
|
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
import io.jsonwebtoken.io.Decoders;
|
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.gameplatform.server.security;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpMethod;
|
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.annotation.web.reactive.EnableWebFluxSecurity;
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|||||||
@@ -1,54 +1,92 @@
|
|||||||
package com.gameplatform.server.service;
|
package com.gameplatform.server.service;
|
||||||
|
|
||||||
import com.gameplatform.server.mapper.UserMapper;
|
import com.gameplatform.server.mapper.account.UserAccountMapper;
|
||||||
import com.gameplatform.server.model.User;
|
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 org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务类 - 基于UserAccount实体
|
||||||
|
* 提供用户账户相关的业务逻辑
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class UserService {
|
public class UserService {
|
||||||
private final UserMapper userMapper;
|
private final UserAccountMapper userAccountMapper;
|
||||||
|
|
||||||
public UserService(UserMapper userMapper) {
|
public UserService(UserAccountMapper userAccountMapper) {
|
||||||
this.userMapper = userMapper;
|
this.userAccountMapper = userAccountMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<User> getById(Long id) {
|
/**
|
||||||
return Mono.fromCallable(() -> userMapper.findById(id))
|
* 根据ID获取用户账户
|
||||||
|
*/
|
||||||
|
public Mono<AccountResponse> getById(Long id) {
|
||||||
|
return Mono.fromCallable(() -> userAccountMapper.findById(id))
|
||||||
|
.subscribeOn(Schedulers.boundedElastic())
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(this::toAccountResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户名获取用户账户
|
||||||
|
*/
|
||||||
|
public Mono<UserAccount> getByUsername(String username) {
|
||||||
|
return Mono.fromCallable(() -> userAccountMapper.findByUsername(username))
|
||||||
.subscribeOn(Schedulers.boundedElastic())
|
.subscribeOn(Schedulers.boundedElastic())
|
||||||
.filter(Objects::nonNull);
|
.filter(Objects::nonNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Flux<User> listAll() {
|
/**
|
||||||
return Mono.fromCallable(userMapper::findAll)
|
* 分页查询用户列表
|
||||||
.subscribeOn(Schedulers.boundedElastic())
|
*/
|
||||||
.flatMapMany(Flux::fromIterable);
|
public Mono<PageResult<AccountResponse>> 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;
|
||||||
|
|
||||||
public Mono<User> create(User user) {
|
|
||||||
return Mono.fromCallable(() -> {
|
return Mono.fromCallable(() -> {
|
||||||
userMapper.insert(user);
|
long total = userAccountMapper.countByFilter(userType, status, role, keyword);
|
||||||
return user;
|
List<UserAccount> list = userAccountMapper.listByFilter(userType, status, role, keyword, s, offset);
|
||||||
|
List<AccountResponse> items = list.stream()
|
||||||
|
.map(this::toAccountResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return new PageResult<>(items, total, p, s);
|
||||||
})
|
})
|
||||||
.subscribeOn(Schedulers.boundedElastic());
|
.subscribeOn(Schedulers.boundedElastic());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<Boolean> deleteById(Long id) {
|
/**
|
||||||
return Mono.fromCallable(() -> userMapper.deleteById(id) > 0)
|
* 检查用户名是否存在
|
||||||
|
*/
|
||||||
|
public Mono<Boolean> existsByUsername(String username) {
|
||||||
|
return Mono.fromCallable(() -> userAccountMapper.findByUsername(username) != null)
|
||||||
.subscribeOn(Schedulers.boundedElastic());
|
.subscribeOn(Schedulers.boundedElastic());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<User> update(Long id, User user) {
|
/**
|
||||||
return Mono.fromCallable(() -> {
|
* 将UserAccount实体转换为AccountResponse DTO
|
||||||
user.setId(id);
|
*/
|
||||||
int n = userMapper.update(user);
|
private AccountResponse toAccountResponse(UserAccount account) {
|
||||||
return n;
|
if (account == null) return null;
|
||||||
})
|
|
||||||
.subscribeOn(Schedulers.boundedElastic())
|
AccountResponse response = new AccountResponse();
|
||||||
.flatMap(n -> n > 0 ? getById(id) : Mono.empty());
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE mapper
|
|
||||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
||||||
<mapper namespace="com.gameplatform.server.mapper.UserMapper">
|
|
||||||
|
|
||||||
<resultMap id="UserResultMap" type="com.gameplatform.server.model.User">
|
|
||||||
<id property="id" column="id" />
|
|
||||||
<result property="username" column="username" />
|
|
||||||
<result property="email" column="email" />
|
|
||||||
<result property="createdAt" column="created_at" />
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
|
||||||
id, username, email, created_at
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<select id="findById" parameterType="long" resultMap="UserResultMap">
|
|
||||||
SELECT <include refid="Base_Column_List"/>
|
|
||||||
FROM users
|
|
||||||
WHERE id = #{id}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="findAll" resultMap="UserResultMap">
|
|
||||||
SELECT <include refid="Base_Column_List"/>
|
|
||||||
FROM users
|
|
||||||
ORDER BY id DESC
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<insert id="insert" parameterType="com.gameplatform.server.model.User" useGeneratedKeys="true" keyProperty="id">
|
|
||||||
INSERT INTO users (username, email, created_at)
|
|
||||||
VALUES (#{username}, #{email}, NOW())
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<delete id="deleteById" parameterType="long">
|
|
||||||
DELETE FROM users WHERE id = #{id}
|
|
||||||
</delete>
|
|
||||||
|
|
||||||
<update id="update" parameterType="com.gameplatform.server.model.User">
|
|
||||||
UPDATE users
|
|
||||||
SET username = #{username},
|
|
||||||
email = #{email}
|
|
||||||
WHERE id = #{id}
|
|
||||||
</update>
|
|
||||||
|
|
||||||
</mapper>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE mapper
|
|
||||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
||||||
<mapper namespace="com.gameplatform.server.mapper.UserMapper">
|
|
||||||
|
|
||||||
<resultMap id="UserResultMap" type="com.gameplatform.server.model.User">
|
|
||||||
<id property="id" column="id" />
|
|
||||||
<result property="username" column="username" />
|
|
||||||
<result property="email" column="email" />
|
|
||||||
<result property="createdAt" column="created_at" />
|
|
||||||
</resultMap>
|
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
|
||||||
id, username, email, created_at
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<select id="findById" parameterType="long" resultMap="UserResultMap">
|
|
||||||
SELECT <include refid="Base_Column_List"/>
|
|
||||||
FROM users
|
|
||||||
WHERE id = #{id}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="findAll" resultMap="UserResultMap">
|
|
||||||
SELECT <include refid="Base_Column_List"/>
|
|
||||||
FROM users
|
|
||||||
ORDER BY id DESC
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<insert id="insert" parameterType="com.gameplatform.server.model.User" useGeneratedKeys="true" keyProperty="id">
|
|
||||||
INSERT INTO users (username, email, created_at)
|
|
||||||
VALUES (#{username}, #{email}, NOW())
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<delete id="deleteById" parameterType="long">
|
|
||||||
DELETE FROM users WHERE id = #{id}
|
|
||||||
</delete>
|
|
||||||
|
|
||||||
</mapper>
|
|
||||||
@@ -28,4 +28,61 @@
|
|||||||
WHERE username = #{username}
|
WHERE username = #{username}
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="findById" parameterType="long" resultMap="UserAccountMap">
|
||||||
|
SELECT id, user_type, username, display_name, password_hash, role, status, points_balance, created_at, updated_at
|
||||||
|
FROM user_account
|
||||||
|
WHERE id = #{id}
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insert" parameterType="com.gameplatform.server.model.entity.account.UserAccount" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO user_account (user_type, username, display_name, password_hash, role, status, points_balance)
|
||||||
|
VALUES (#{userType}, #{username}, #{displayName}, #{passwordHash}, #{role}, #{status}, #{pointsBalance})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="update" parameterType="com.gameplatform.server.model.entity.account.UserAccount">
|
||||||
|
UPDATE user_account
|
||||||
|
<set>
|
||||||
|
<if test="displayName != null">display_name = #{displayName},</if>
|
||||||
|
<if test="role != null">role = #{role},</if>
|
||||||
|
<if test="status != null">status = #{status},</if>
|
||||||
|
</set>
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="setStatus">
|
||||||
|
UPDATE user_account SET status = #{status} WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="updatePassword">
|
||||||
|
UPDATE user_account SET password_hash = #{passwordHash} WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<select id="countByFilter" resultType="long">
|
||||||
|
SELECT COUNT(1) FROM user_account
|
||||||
|
<where>
|
||||||
|
<if test="userType != null and userType != ''">AND user_type = #{userType}</if>
|
||||||
|
<if test="status != null and status != ''">AND status = #{status}</if>
|
||||||
|
<if test="role != null and role != ''">AND role = #{role}</if>
|
||||||
|
<if test="keyword != null and keyword != ''">
|
||||||
|
AND (username LIKE CONCAT('%', #{keyword}, '%') OR display_name LIKE CONCAT('%', #{keyword}, '%'))
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="listByFilter" resultMap="UserAccountMap">
|
||||||
|
SELECT id, user_type, username, display_name, password_hash, role, status, points_balance, created_at, updated_at
|
||||||
|
FROM user_account
|
||||||
|
<where>
|
||||||
|
<if test="userType != null and userType != ''">AND user_type = #{userType}</if>
|
||||||
|
<if test="status != null and status != ''">AND status = #{status}</if>
|
||||||
|
<if test="role != null and role != ''">AND role = #{role}</if>
|
||||||
|
<if test="keyword != null and keyword != ''">
|
||||||
|
AND (username LIKE CONCAT('%', #{keyword}, '%') OR display_name LIKE CONCAT('%', #{keyword}, '%'))
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY id DESC
|
||||||
|
LIMIT #{size} OFFSET #{offset}
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user