Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4201cba5b | |||
| 5f414dceae | |||
| 6ce84f6f9f | |||
| 3f46bb02cf | |||
| 4496a31334 | |||
| 290f8bacd2 | |||
| b99834bdc0 | |||
| ce5873793a |
@@ -1,7 +1,14 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(mvn clean *)"
|
||||
"Bash(mvn clean *)",
|
||||
"mcp__codegraph__codegraph_context",
|
||||
"mcp__codegraph__codegraph_files",
|
||||
"mcp__codegraph__codegraph_search",
|
||||
"mcp__codegraph__codegraph_explore",
|
||||
"mcp__codegraph__codegraph_node",
|
||||
"Bash(python -)",
|
||||
"mcp__codegraph__codegraph_status"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
7
.codegraph/.gitignore
vendored
7
.codegraph/.gitignore
vendored
@@ -14,3 +14,10 @@ cache/
|
||||
|
||||
# Hook markers
|
||||
.dirty
|
||||
|
||||
|
||||
|
||||
|
||||
# Compiled class files
|
||||
target/
|
||||
*.class
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -36,3 +36,4 @@ application-test.yml
|
||||
|
||||
# Compiled class files
|
||||
*.class
|
||||
/.codegraph/daemon.pid
|
||||
|
||||
1124
.understand-anything/knowledge-graph.json
Normal file
1124
.understand-anything/knowledge-graph.json
Normal file
File diff suppressed because it is too large
Load Diff
6
.understand-anything/meta.json
Normal file
6
.understand-anything/meta.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"lastAnalyzedAt": "2026-05-28T18:03:22.405243Z",
|
||||
"gitCommitHash": "8cb9c14b64ed71d8e46a304143ae43200dc0acc3",
|
||||
"version": "1.0.0",
|
||||
"analyzedFiles": 56
|
||||
}
|
||||
@@ -103,7 +103,8 @@ SysUser (entity) + UserCreateDTO (dto) + UserVO (vo)
|
||||
|
||||
1. **Single-table queries**: Use MyBatis-Plus native methods (`selectById`, `selectList`, `selectPage`, `LambdaQueryWrapper`)
|
||||
2. **Join queries**: Use MyBatis XML mapper (`@MapperScan("com.leiyuwei.mhxy.**.mapper")`)
|
||||
3. **Logical deletion**: All entities have `deleted` field (0=active, 1=deleted)
|
||||
3. **No logical deletion**: Project does not use `deleted` field for filtering - query all records
|
||||
4. **No wildcard select**: Do not use `SELECT *` or table wildcards like `r.*` / `p.*` in SQL. Always list only the fields actually needed by the mapper result.
|
||||
|
||||
### Security & Authentication
|
||||
|
||||
@@ -125,7 +126,7 @@ SysUser (entity) + UserCreateDTO (dto) + UserVO (vo)
|
||||
|
||||
### File Upload (MinIO + Deduplication)
|
||||
|
||||
**Location**: `com.leiyuwei.mhxy.business.service.impl.FileServiceImpl`
|
||||
**Location**: `com.leiyuwei.mhxy.game.service.impl.FileServiceImpl`
|
||||
|
||||
**Deduplication flow**:
|
||||
1. Calculate SHA-256 hash of uploaded file
|
||||
@@ -140,7 +141,7 @@ SysUser (entity) + UserCreateDTO (dto) + UserVO (vo)
|
||||
|
||||
### Invitation Code System
|
||||
|
||||
**Location**: `com.leiyuwei.mhxy.business.service.InvitationCodeService`
|
||||
**Location**: `com.leiyuwei.mhxy.game.service.InvitationCodeService`
|
||||
|
||||
Three formats supported (not yet implemented in code):
|
||||
- RANDOM: Random string (X7K9M2P4)
|
||||
@@ -211,7 +212,7 @@ Created by `V1.0.1__init_admin_data.sql`:
|
||||
- `spring.datasource.*` - MySQL connection
|
||||
- `minio.*` - MinIO endpoint, access key, secret key, bucket name
|
||||
- `jwt.*` - Secret key, token expiration times
|
||||
- `mybatis-plus.*` - ID type (auto), logical delete field (`deleted`)
|
||||
- `mybatis-plus.*` - ID type (auto)
|
||||
|
||||
**Druid monitoring**: http://localhost:8080/druid/index.html (user: admin, password: admin)
|
||||
|
||||
|
||||
@@ -22,6 +22,27 @@ This file indexes all memory entries stored in the project's memory system.
|
||||
|
||||
Current session ID: 2026-05-27
|
||||
|
||||
### Completed Tasks
|
||||
1. **Package Name Migration** - Fixed imports for RoleVO and FileUploadVO in controllers/services
|
||||
2. **Created Missing Classes**:
|
||||
- PermissionTreeVO - Permission tree structure VO
|
||||
- SysPermissionService - Permission service interface
|
||||
- SysPermissionServiceImpl - Permission service implementation with tree building logic
|
||||
3. **Fixed PageVO.from() API** - Changed from incorrect MyBatis-Plus methods to correct ones
|
||||
4. **MinioClient Refactoring**:
|
||||
- Created MinioConfig with @Bean for MinioClient
|
||||
- Updated FileServiceImpl to use dependency injection instead of creating client
|
||||
- Added getMinioClient() method to FileService interface for Controller use
|
||||
5. **Added Mapper Methods**:
|
||||
- SysPermissionMapper.selectPermissionsByRoleId() - Query role permissions
|
||||
- Created corresponding XML mapper
|
||||
|
||||
### Issues Resolved
|
||||
- Fixed SysPermissionController imports (PermissionTreeVO and SysPermissionService)
|
||||
- Fixed PageVO.from() to use correct IPage API methods
|
||||
- Fixed FileServiceImpl to use injected MinioClient instead of creating instance
|
||||
- Fixed FileServiceImpl.getPresignedUrl() to use minioConfig.getBucketName()
|
||||
|
||||
---
|
||||
|
||||
## Long-term Memory (Typed)
|
||||
@@ -38,6 +59,12 @@ Current session ID: 2026-05-27
|
||||
- Location: `AuthServiceImpl.java`
|
||||
- BCrypt encoder configured in `SecurityConfig.passwordEncoder()`
|
||||
|
||||
- **MinIO Integration**:
|
||||
- Created MinioConfig with @Bean for MinioClient
|
||||
- FileServiceImpl uses constructor injection
|
||||
- MinioClient injected via Bean, not created dynamically
|
||||
- getMinioClient() exposed in FileService interface for Controller use
|
||||
|
||||
- **Missing TODOs**:
|
||||
- `AuthServiceImpl.register()` - Invitation code validation
|
||||
- `SysPermissionController.getMenus()` - User-specific menu tree
|
||||
@@ -52,6 +79,6 @@ Current session ID: 2026-05-27
|
||||
|
||||
## Memory Stats
|
||||
|
||||
- Total entries: 3
|
||||
- Memory size: ~3 KB
|
||||
- Total entries: 4
|
||||
- Memory size: ~4 KB
|
||||
- Last updated: 2026-05-27
|
||||
|
||||
40
pom.xml
40
pom.xml
@@ -27,6 +27,7 @@
|
||||
<jwt.version>0.12.3</jwt.version>
|
||||
<flyway.version>10.4.1</flyway.version>
|
||||
<hutool.version>5.8.24</hutool.version>
|
||||
<lombok.version>1.18.38</lombok.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -49,7 +50,7 @@
|
||||
<!-- MyBatis Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -94,14 +95,14 @@
|
||||
</dependency>
|
||||
|
||||
<!-- Flyway 数据库迁移 -->
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.flywaydb</groupId>-->
|
||||
<!-- <artifactId>flyway-core</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.flywaydb</groupId>-->
|
||||
<!-- <artifactId>flyway-mysql</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- Hutool 工具类 -->
|
||||
<dependency>
|
||||
@@ -110,11 +111,16 @@
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Caffeine 高性能缓存 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security -->
|
||||
@@ -140,6 +146,20 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<release>${java.version}</release>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
|
||||
21
sql/logback.xml
Normal file
21
sql/logback.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- MyBatis SQL 日志 -->
|
||||
<logger name="com.leiyuwei.mhxy.mapper" level="DEBUG"/>
|
||||
<logger name="com.baomidou.mybatisplus" level="DEBUG"/>
|
||||
<logger name="org.apache.ibatis" level="DEBUG"/>
|
||||
|
||||
<!-- 打印 SQL 参数(更详细) -->
|
||||
<logger name="com.leiyuwei.mhxy.mapper" level="TRACE"/>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -3,11 +3,13 @@ package com.leiyuwei.mhxy;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
|
||||
/**
|
||||
* 梦幻西游管理后台 - 主启动类
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableCaching
|
||||
@MapperScan("com.leiyuwei.mhxy.**.mapper")
|
||||
public class MhxyToolsApplication {
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.leiyuwei.mhxy.common.config;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Caffeine 缓存配置类
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class CacheConfig {
|
||||
|
||||
/**
|
||||
* Caffeine 配置参数(可移到配置文件)
|
||||
*/
|
||||
@Value("${caffeine.initial-capacity:100}")
|
||||
private int initialCapacity;
|
||||
|
||||
@Value("${caffeine.maximum-size:10000}")
|
||||
private long maximumSize;
|
||||
|
||||
@Value("${caffeine.expire-minutes:30}")
|
||||
private int expireMinutes;
|
||||
|
||||
/**
|
||||
* 配置 Caffeine 缓存
|
||||
*/
|
||||
// @Bean(name = "caffeineCache")
|
||||
// public Cache<String, Object> genericCache() {
|
||||
// Cache<String, Object> cache = Caffeine.newBuilder()
|
||||
// .initialCapacity(100)
|
||||
// .maximumSize(10000)
|
||||
// .expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
// .recordStats()
|
||||
// .build();
|
||||
//
|
||||
// log.info("通用 Caffeine 缓存初始化完成 (initial=100, max=10000, TTL=15min)");
|
||||
// return cache;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 配置 Caffeine 缓存管理器
|
||||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
CaffeineCacheManager cacheManager = new CaffeineCacheManager(
|
||||
"userPermissions",
|
||||
"roles",
|
||||
"permissions",
|
||||
"default"
|
||||
);
|
||||
|
||||
cacheManager.setCaffeine(Caffeine.newBuilder()
|
||||
.initialCapacity(initialCapacity)
|
||||
.maximumSize(maximumSize)
|
||||
.expireAfterWrite(expireMinutes, TimeUnit.MINUTES)
|
||||
.recordStats());
|
||||
|
||||
cacheManager.setAllowNullValues(false);
|
||||
|
||||
log.info("Caffeine 缓存管理器初始化完成,已注册缓存: userPermissions, roles, permissions, default");
|
||||
return cacheManager;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.leiyuwei.mhxy.common.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -14,7 +12,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@MapperScan("com.leiyuwei.mhxy.mapper")
|
||||
//@MapperScan("com.leiyuwei.mhxy.**.mapper")
|
||||
public class MyBatisPlusConfig {
|
||||
|
||||
/**
|
||||
@@ -31,8 +29,8 @@ public class MyBatisPlusConfig {
|
||||
/**
|
||||
* MyBatis-Plus 元数据填充处理器
|
||||
*/
|
||||
@Bean
|
||||
public MetaObjectHandler metaObjectHandler() {
|
||||
return new CustomMetaObjectHandler();
|
||||
}
|
||||
// @Bean
|
||||
// public MetaObjectHandler metaObjectHandler() {
|
||||
// return new CustomMetaObjectHandler();
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.leiyuwei.mhxy.common.config;
|
||||
|
||||
import com.leiyuwei.mhxy.common.security.JwtAuthenticationFilter;
|
||||
import com.leiyuwei.mhxy.system.service.impl.UserDetailsServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@@ -13,6 +17,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
/**
|
||||
* SpringSecurity 配置类
|
||||
@@ -20,10 +25,12 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
@EnableCaching
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
private final UserDetailsServiceImpl userDetailsService;
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
@@ -33,33 +40,50 @@ public class SecurityConfig {
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// 禁用 CSRF
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
// 禁用 CSRF
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
|
||||
// 设置 Session 管理为无状态
|
||||
.sessionManagement(session -> session
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
)
|
||||
// 设置 Session 管理为无状态
|
||||
.sessionManagement(session -> session
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
)
|
||||
|
||||
// 配置权限规则
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
// 认证相关接口开放
|
||||
.requestMatchers("/api/auth/**").permitAll()
|
||||
// Knife4j 文档接口开放
|
||||
.requestMatchers("/doc.html", "/webjars/**", "/swagger-ui/**", "/swagger-resources/**").permitAll()
|
||||
// 邀请码验证接口开放
|
||||
.requestMatchers("/api/business/invitation-codes/validate").permitAll()
|
||||
// Druid 监控接口开放
|
||||
.requestMatchers("/druid/**").permitAll()
|
||||
// 健康检查接口
|
||||
.requestMatchers("/actuator/**").permitAll()
|
||||
// 其他所有请求需要认证
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
// 配置权限规则
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
|
||||
// 添加 JWT 过滤器
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 认证相关接口开放
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/api/auth/**")).permitAll()
|
||||
// Knife4j 文档接口开放
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/webjars/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/doc.html")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/swagger-ui/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/v3/api-docs/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/swagger-resources/**")).permitAll()
|
||||
|
||||
// 邀请码验证接口开放
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/api/business/invitation-codes/validate")).permitAll()
|
||||
// Druid 监控接口开放
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/druid/**")).permitAll()
|
||||
// 健康检查接口
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/actuator/**")).permitAll()
|
||||
// 其他所有请求需要认证
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
|
||||
// 添加 JWT 过滤器
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
|
||||
// 配置 AuthenticationManager
|
||||
.authenticationProvider(authenticationProvider());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationProvider authenticationProvider() {
|
||||
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||
authProvider.setUserDetailsService(userDetailsService);
|
||||
authProvider.setPasswordEncoder(passwordEncoder());
|
||||
return authProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.leiyuwei.mhxy.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -14,7 +17,7 @@ public class BaseEntity {
|
||||
/**
|
||||
* 主键 ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
@@ -32,11 +35,12 @@ public class BaseEntity {
|
||||
/**
|
||||
* 逻辑删除字段
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
// @TableField
|
||||
// private Integer deleted;
|
||||
|
||||
/**
|
||||
* 乐观锁版本号
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Integer version;
|
||||
}
|
||||
|
||||
@@ -28,17 +28,17 @@ public class Result<T> {
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
//private Long timestamp;
|
||||
|
||||
private Result() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
//this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private Result(Integer code, String message, T data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
//this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.leiyuwei.mhxy.common.security;
|
||||
|
||||
import com.leiyuwei.mhxy.common.util.JwtUtil;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -8,22 +9,29 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JWT 认证过滤器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
private final JwtUtil jwtUtil;
|
||||
|
||||
@Override
|
||||
@@ -33,21 +41,20 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
try {
|
||||
String token = getTokenFromRequest(request);
|
||||
|
||||
if (StringUtils.hasText(token) && jwtUtil.validateToken(token)) {
|
||||
if (StringUtils.hasText(token)
|
||||
&& SecurityContextHolder.getContext().getAuthentication() == null
|
||||
&& jwtUtil.validateToken(token)) {
|
||||
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||
String username = jwtUtil.getUsernameFromToken(token);
|
||||
Claims claims = jwtUtil.getClaimsFromToken(token);
|
||||
Collection<? extends GrantedAuthority> authorities = buildAuthorities(claims);
|
||||
|
||||
// 设置 Spring Security 认证信息
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(
|
||||
username,
|
||||
null,
|
||||
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
|
||||
);
|
||||
new UsernamePasswordAuthenticationToken(username, null, authorities);
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
log.debug("设置用户认证信息: userId={}, username={}", userId, username);
|
||||
log.debug("设置用户认证信息: userId={}, username={}, authorities={}", userId, username, authorities);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("无法设置用户认证: {}", e.getMessage());
|
||||
@@ -56,14 +63,50 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private Collection<? extends GrantedAuthority> buildAuthorities(Claims claims) {
|
||||
LinkedHashSet<String> authorityCodes = new LinkedHashSet<>();
|
||||
authorityCodes.addAll(extractStringList(claims.get("roleCodes")));
|
||||
authorityCodes.addAll(extractStringList(claims.get("permissionCodes")));
|
||||
|
||||
return authorityCodes.stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<String> extractStringList(Object claimValue) {
|
||||
if (!(claimValue instanceof List<?> values)) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return values.stream()
|
||||
.filter(String.class::isInstance)
|
||||
.map(String.class::cast)
|
||||
.filter(StringUtils::hasText)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头获取 Token
|
||||
*/
|
||||
private String getTokenFromRequest(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
String authorization = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(authorization)) {
|
||||
if (authorization.regionMatches(true, 0, BEARER_PREFIX, 0, BEARER_PREFIX.length())) {
|
||||
return authorization.substring(BEARER_PREFIX.length()).trim();
|
||||
}
|
||||
return authorization.trim();
|
||||
}
|
||||
|
||||
String token = request.getHeader("token");
|
||||
if (StringUtils.hasText(token)) {
|
||||
return token.trim();
|
||||
}
|
||||
|
||||
String accessToken = request.getParameter("accessToken");
|
||||
if (StringUtils.hasText(accessToken)) {
|
||||
return accessToken.trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class JwtUtil {
|
||||
/**
|
||||
* 从 Token 中获取 Claims
|
||||
*/
|
||||
private Claims getClaimsFromToken(String token) {
|
||||
public Claims getClaimsFromToken(String token) {
|
||||
return Jwts.parser()
|
||||
.verifyWith(getSignKey())
|
||||
.build()
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.leiyuwei.mhxy.game.controller;
|
||||
|
||||
import com.leiyuwei.mhxy.common.result.Result;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.AccountVO;
|
||||
import com.leiyuwei.mhxy.game.service.AccountService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/game/accounts")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "游戏账号管理", description = "游戏账号增删改查")
|
||||
public class AccountController {
|
||||
|
||||
private final AccountService accountService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页查询账号")
|
||||
@PreAuthorize("hasAuthority('game:account:list')")
|
||||
public Result<PageVO<AccountVO>> getAccountList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String accountName,
|
||||
@RequestParam(required = false) Long userId) {
|
||||
AccountQueryDTO dto = new AccountQueryDTO();
|
||||
dto.setPageNum(page);
|
||||
dto.setPageSize(size);
|
||||
dto.setAccountName(accountName);
|
||||
dto.setUserId(userId);
|
||||
return Result.success(accountService.getAccountList(dto));
|
||||
}
|
||||
|
||||
@GetMapping("/detail/{id}")
|
||||
@Operation(summary = "查询账号详情")
|
||||
@PreAuthorize("hasAuthority('game:account:view')")
|
||||
public Result<AccountVO> getAccountById(@PathVariable Long id) {
|
||||
return Result.success(accountService.getAccountById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增账号")
|
||||
@PreAuthorize("hasAuthority('game:account:add')")
|
||||
public Result<AccountVO> createAccount(@Valid @RequestBody AccountCreateDTO dto) {
|
||||
return Result.success(accountService.createAccount(dto));
|
||||
}
|
||||
|
||||
@PutMapping("/edit/{id}")
|
||||
@Operation(summary = "编辑账号")
|
||||
@PreAuthorize("hasAuthority('game:account:edit')")
|
||||
public Result<Void> updateAccount(@PathVariable Long id, @Valid @RequestBody AccountUpdateDTO dto) {
|
||||
accountService.updateAccount(id, dto);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
@Operation(summary = "删除账号")
|
||||
@PreAuthorize("hasAuthority('game:account:delete')")
|
||||
public Result<Void> deleteAccount(@PathVariable Long id) {
|
||||
accountService.deleteAccount(id);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.leiyuwei.mhxy.game.controller;
|
||||
|
||||
import com.leiyuwei.mhxy.common.result.Result;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.CharacterImageVO;
|
||||
import com.leiyuwei.mhxy.game.service.CharacterImageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/game/character-images")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "角色图片管理", description = "角色图片增删改查")
|
||||
public class CharacterImageController {
|
||||
|
||||
private final CharacterImageService characterImageService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页查询角色图片")
|
||||
@PreAuthorize("hasAuthority('game:character:list')")
|
||||
public Result<PageVO<CharacterImageVO>> getCharacterImageList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) Long characterId,
|
||||
@RequestParam(required = false) String fileHash) {
|
||||
CharacterImageQueryDTO dto = new CharacterImageQueryDTO();
|
||||
dto.setPageNum(page);
|
||||
dto.setPageSize(size);
|
||||
dto.setCharacterId(characterId);
|
||||
dto.setFileHash(fileHash);
|
||||
return Result.success(characterImageService.getCharacterImageList(dto));
|
||||
}
|
||||
|
||||
@GetMapping("/detail/{id}")
|
||||
@Operation(summary = "查询角色图片详情")
|
||||
@PreAuthorize("hasAuthority('game:character:image:view')")
|
||||
public Result<CharacterImageVO> getCharacterImageById(@PathVariable Long id) {
|
||||
return Result.success(characterImageService.getCharacterImageById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增角色图片")
|
||||
@PreAuthorize("hasAuthority('game:character:image:add')")
|
||||
public Result<CharacterImageVO> createCharacterImage(@Valid @RequestBody CharacterImageCreateDTO dto) {
|
||||
return Result.success(characterImageService.createCharacterImage(dto));
|
||||
}
|
||||
|
||||
@PutMapping("/edit/{id}")
|
||||
@Operation(summary = "编辑角色图片")
|
||||
@PreAuthorize("hasAuthority('game:character:image:add')")
|
||||
public Result<Void> updateCharacterImage(@PathVariable Long id, @Valid @RequestBody CharacterImageUpdateDTO dto) {
|
||||
characterImageService.updateCharacterImage(id, dto);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
@Operation(summary = "删除角色图片")
|
||||
@PreAuthorize("hasAuthority('game:character:image:delete')")
|
||||
public Result<Void> deleteCharacterImage(@PathVariable Long id) {
|
||||
characterImageService.deleteCharacterImage(id);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.leiyuwei.mhxy.game.controller;
|
||||
|
||||
import com.leiyuwei.mhxy.common.result.Result;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordCreateDTO;
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordUpdateDTO;
|
||||
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.vo.GameCharacterKeywordVO;
|
||||
import com.leiyuwei.mhxy.game.service.GameCharacterKeywordService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/game/character-keywords")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "角色关键字管理", description = "角色关键字增删改查")
|
||||
public class CharacterKeywordController {
|
||||
|
||||
private final GameCharacterKeywordService characterKeywordService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页查询关键字")
|
||||
@PreAuthorize("hasAuthority('game:characterKeyword:list')")
|
||||
public Result<PageVO<GameCharacterKeywordVO>> getCharacterKeywordList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) Long characterId,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
GameCharacterKeywordQueryDTO dto = new GameCharacterKeywordQueryDTO();
|
||||
dto.setPageNum(page);
|
||||
dto.setPageSize(size);
|
||||
dto.setCharacterId(characterId);
|
||||
dto.setKeyword(keyword);
|
||||
return Result.success(characterKeywordService.getCharacterKeywordList(dto));
|
||||
}
|
||||
|
||||
@GetMapping("/detail/{id}")
|
||||
@Operation(summary = "查询关键字详情")
|
||||
@PreAuthorize("hasAuthority('game:characterKeyword:view')")
|
||||
public Result<GameCharacterKeywordVO> getCharacterKeywordById(@PathVariable Long id) {
|
||||
return Result.success(characterKeywordService.getCharacterKeywordById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增关键字")
|
||||
@PreAuthorize("hasAuthority('game:characterKeyword:add')")
|
||||
public Result<GameCharacterKeywordVO> createCharacterKeyword(@Valid @RequestBody GameCharacterKeywordCreateDTO dto) {
|
||||
return Result.success(characterKeywordService.createCharacterKeyword(dto));
|
||||
}
|
||||
|
||||
@PutMapping("/edit/{id}")
|
||||
@Operation(summary = "编辑关键字")
|
||||
@PreAuthorize("hasAuthority('game:characterKeyword:edit')")
|
||||
public Result<Void> updateCharacterKeyword(@PathVariable Long id, @Valid @RequestBody GameCharacterKeywordUpdateDTO dto) {
|
||||
characterKeywordService.updateCharacterKeyword(id, dto);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
@Operation(summary = "删除关键字")
|
||||
@PreAuthorize("hasAuthority('game:characterKeyword:delete')")
|
||||
public Result<Void> deleteCharacterKeyword(@PathVariable Long id) {
|
||||
characterKeywordService.deleteCharacterKeyword(id);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package com.leiyuwei.mhxy.business.controller;
|
||||
package com.leiyuwei.mhxy.game.controller;
|
||||
|
||||
import com.leiyuwei.mhxy.business.model.vo.FileUploadVO;
|
||||
import com.leiyuwei.mhxy.business.service.FileService;
|
||||
import com.leiyuwei.mhxy.common.result.Result;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.FileUploadVO;
|
||||
import com.leiyuwei.mhxy.game.service.FileService;
|
||||
import io.minio.GetObjectArgs;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -16,26 +18,22 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 文件管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/business/files")
|
||||
@RequestMapping({"/api/business/files", "/api/game/files"})
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "文件管理", description = "文件上传、下载、删除")
|
||||
public class FileController {
|
||||
|
||||
private final FileService fileService;
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "上传文件", description = "上传文件到 MinIO,自动去重")
|
||||
@PreAuthorize("hasAuthority('game:file:add')")
|
||||
public Result<Map<String, Object>> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||
FileUploadVO uploadVO = fileService.uploadFile(file);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("id", uploadVO.getId());
|
||||
result.put("fileHash", uploadVO.getFileHash());
|
||||
result.put("fileName", uploadVO.getObjectName());
|
||||
result.put("fileSize", uploadVO.getFileSize());
|
||||
@@ -45,33 +43,25 @@ public class FileController {
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量上传文件
|
||||
*/
|
||||
@PostMapping("/upload/batch")
|
||||
@Operation(summary = "批量上传文件", description = "批量上传文件到 MinIO")
|
||||
@PreAuthorize("hasAuthority('game:file:add')")
|
||||
public Result<List<FileUploadVO>> uploadFiles(@RequestParam("files") List<MultipartFile> files) {
|
||||
List<FileUploadVO> result = fileService.uploadFiles(files);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
@GetMapping("/download/{fileHash}")
|
||||
@Operation(summary = "下载文件", description = "下载指定文件")
|
||||
@PreAuthorize("hasAuthority('game:file:view')")
|
||||
public void downloadFile(@PathVariable String fileHash,
|
||||
HttpServletResponse response) {
|
||||
HttpServletResponse response) {
|
||||
try {
|
||||
FileUploadVO fileVO = fileService.getFileByHash(fileHash);
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType(fileVO.getContentType());
|
||||
response.setContentLength(fileVO.getFileSize().intValue());
|
||||
response.setHeader("Content-Disposition",
|
||||
"attachment; filename=\"" + fileVO.getObjectName() + "\"");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileVO.getObjectName() + "\"");
|
||||
|
||||
// 从 MinIO 下载文件
|
||||
InputStream inputStream = fileService.getMinioClient().getObject(
|
||||
GetObjectArgs.builder()
|
||||
.bucket("mhxy-files")
|
||||
@@ -79,40 +69,49 @@ public class FileController {
|
||||
.build()
|
||||
);
|
||||
|
||||
// 写入响应流
|
||||
inputStream.transferTo(response.getOutputStream());
|
||||
response.getOutputStream().flush();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("文件下载失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
@DeleteMapping("/{fileHash}")
|
||||
@Operation(summary = "删除文件", description = "删除文件(引用计数-1,为0时才真正删除)")
|
||||
@PreAuthorize("hasAuthority('game:file:delete')")
|
||||
public Result<Void> deleteFile(@PathVariable String fileHash) {
|
||||
fileService.deleteFile(fileHash);
|
||||
return Result.success("文件删除成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
*/
|
||||
@GetMapping("/{fileHash}")
|
||||
@Operation(summary = "获取文件信息", description = "根据 fileHash 获取文件详细信息")
|
||||
@PreAuthorize("hasAuthority('game:file:view')")
|
||||
public Result<FileUploadVO> getFileByHash(@PathVariable String fileHash) {
|
||||
FileUploadVO fileVO = fileService.getFileByHash(fileHash);
|
||||
return Result.success(fileVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成预签名 URL
|
||||
*/
|
||||
@GetMapping("/detail/{id}")
|
||||
@Operation(summary = "按ID获取文件信息")
|
||||
@PreAuthorize("hasAuthority('game:file:view')")
|
||||
public Result<FileUploadVO> getFileById(@PathVariable Long id) {
|
||||
return Result.success(fileService.getFileById(id));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页查询文件")
|
||||
@PreAuthorize("hasAuthority('game:file:list')")
|
||||
public Result<PageVO<FileUploadVO>> getFileList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String fileHash) {
|
||||
return Result.success(fileService.getFileList(page, size, fileHash));
|
||||
}
|
||||
|
||||
@GetMapping("/preview/{fileHash}")
|
||||
@Operation(summary = "生成预签名 URL", description = "生成文件预签名的临时访问 URL")
|
||||
@PreAuthorize("hasAuthority('game:file:view')")
|
||||
public Result<String> generatePresignedUrl(
|
||||
@PathVariable String fileHash,
|
||||
@RequestParam(defaultValue = "30") Integer expirationMinutes) {
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.leiyuwei.mhxy.game.controller;
|
||||
|
||||
import com.leiyuwei.mhxy.common.result.Result;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.CharacterVO;
|
||||
|
||||
import com.leiyuwei.mhxy.game.service.GameCharacterService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/game/characters")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "游戏角色管理", description = "游戏角色增删改查")
|
||||
public class GameCharacterController {
|
||||
|
||||
private final GameCharacterService characterService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页查询角色")
|
||||
@PreAuthorize("hasAuthority('game:character:list')")
|
||||
public Result<PageVO<CharacterVO>> getCharacterList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String characterName,
|
||||
@RequestParam(required = false) Long accountId,
|
||||
@RequestParam(required = false) Long serverId) {
|
||||
CharacterQueryDTO dto = new CharacterQueryDTO();
|
||||
dto.setPageNum(page);
|
||||
dto.setPageSize(size);
|
||||
dto.setCharacterName(characterName);
|
||||
dto.setAccountId(accountId);
|
||||
dto.setServerId(serverId);
|
||||
return Result.success(characterService.getCharacterList(dto));
|
||||
}
|
||||
|
||||
@GetMapping("/detail/{id}")
|
||||
@Operation(summary = "查询角色详情")
|
||||
@PreAuthorize("hasAuthority('game:character:view')")
|
||||
public Result<CharacterVO> getCharacterById(@PathVariable Long id) {
|
||||
return Result.success(characterService.getCharacterById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增角色")
|
||||
@PreAuthorize("hasAuthority('game:character:add')")
|
||||
public Result<CharacterVO> createCharacter(@Valid @RequestBody CharacterCreateDTO dto) {
|
||||
return Result.success(characterService.createCharacter(dto));
|
||||
}
|
||||
|
||||
@PutMapping("/edit/{id}")
|
||||
@Operation(summary = "编辑角色")
|
||||
@PreAuthorize("hasAuthority('game:character:edit')")
|
||||
public Result<Void> updateCharacter(@PathVariable Long id, @Valid @RequestBody CharacterUpdateDTO dto) {
|
||||
characterService.updateCharacter(id, dto);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
@Operation(summary = "删除角色")
|
||||
@PreAuthorize("hasAuthority('game:character:delete')")
|
||||
public Result<Void> deleteCharacter(@PathVariable Long id) {
|
||||
characterService.deleteCharacter(id);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.leiyuwei.mhxy.game.controller;
|
||||
|
||||
import com.leiyuwei.mhxy.common.result.Result;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.ServerVO;
|
||||
import com.leiyuwei.mhxy.game.service.ServerService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 游戏服务器管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/game/servers")
|
||||
@RequiredArgsConstructor
|
||||
public class ServerController {
|
||||
|
||||
private final ServerService serverService;
|
||||
|
||||
/**
|
||||
* 分页查询服务器列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@PreAuthorize("hasAuthority('game:server:list')")
|
||||
public Result<PageVO<ServerVO>> getServerList(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword){
|
||||
ServerQueryDTO dto = new ServerQueryDTO();
|
||||
dto.setPageNum(page);
|
||||
dto.setPageSize(size);
|
||||
dto.setKeyword(keyword);
|
||||
PageVO<ServerVO> serverPage = serverService.getServerList(dto);
|
||||
return Result.success(serverPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询服务器详情
|
||||
*/
|
||||
@GetMapping("/detail/{id}")
|
||||
@PreAuthorize("hasAuthority('game:server:view')")
|
||||
public Result<ServerVO> getServerById(@PathVariable Long id) {
|
||||
ServerVO vo = serverService.getServerById(id);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新服务器
|
||||
*/
|
||||
@PostMapping("/add")
|
||||
@PreAuthorize("hasAuthority('game:server:add')")
|
||||
public Result<ServerVO> createServer(@RequestBody ServerCreateDTO dto) {
|
||||
ServerVO vo = serverService.createServer(dto);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新服务器信息
|
||||
*/
|
||||
@PutMapping("/edit/{id}")
|
||||
@PreAuthorize("hasAuthority('game:server:edit')")
|
||||
public Result<Void> updateServer(@PathVariable Long id, @RequestBody ServerUpdateDTO dto) {
|
||||
serverService.updateServer(id, dto);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除服务器
|
||||
*/
|
||||
@DeleteMapping("/remove/{id}")
|
||||
@PreAuthorize("hasAuthority('game:server:delete')")
|
||||
public Result<Void> deleteServer(@PathVariable Long id) {
|
||||
serverService.deleteServer(id);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.leiyuwei.mhxy.game.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.game.model.entity.Account;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface AccountMapper extends BaseMapper<Account> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.leiyuwei.mhxy.game.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacterKeyword;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface CharacterKeywordMapper extends BaseMapper<GameCharacterKeyword> {
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.leiyuwei.mhxy.business.mapper;
|
||||
package com.leiyuwei.mhxy.game.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.business.model.entity.FileHashReference;
|
||||
import com.leiyuwei.mhxy.game.model.entity.FileHashReference;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* 文件 Hash 引用 Mapper
|
||||
@@ -15,6 +14,5 @@ public interface FileHashReferenceMapper extends BaseMapper<FileHashReference> {
|
||||
/**
|
||||
* 按对象名称查询文件
|
||||
*/
|
||||
@Select("SELECT * FROM file_hash_reference WHERE object_name = #{objectName}")
|
||||
FileHashReference selectByObjectName(@Param("objectName") String objectName);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.leiyuwei.mhxy.game.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacterImage;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface GameCharacterImageMapper extends BaseMapper<GameCharacterImage> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.leiyuwei.mhxy.game.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacter;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface GameCharacterMapper extends BaseMapper<GameCharacter> {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.leiyuwei.mhxy.game.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.game.model.entity.InvitationCode;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 邀请码 Mapper(最小实现)
|
||||
*/
|
||||
@Mapper
|
||||
public interface InvitationCodeMapper extends BaseMapper<InvitationCode> {
|
||||
|
||||
/**
|
||||
* 根据邀请码查询
|
||||
*/
|
||||
InvitationCode selectByCode(@Param("code") String code);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.leiyuwei.mhxy.game.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.game.model.entity.Server;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ServerMapper extends BaseMapper<Server> {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AccountCreateDTO {
|
||||
@NotBlank(message = "账号名称不能为空")
|
||||
@Size(max = 50, message = "账号名称长度不能超过 50")
|
||||
private String accountName;
|
||||
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import com.leiyuwei.mhxy.common.dto.PageDTO;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AccountQueryDTO extends PageDTO {
|
||||
@Size(max = 50, message = "账号名称长度不能超过 50")
|
||||
private String accountName;
|
||||
|
||||
private Long userId;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AccountUpdateDTO {
|
||||
@NotBlank(message = "账号名称不能为空")
|
||||
@Size(max = 50, message = "账号名称长度不能超过 50")
|
||||
private String accountName;
|
||||
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CharacterCreateDTO {
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
@Size(max = 50, message = "角色名称长度不能超过 50")
|
||||
private String characterName;
|
||||
|
||||
@NotNull(message = "账号ID不能为空")
|
||||
private Long accountId;
|
||||
|
||||
@NotNull(message = "服务器ID不能为空")
|
||||
private Long serverId;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CharacterImageCreateDTO {
|
||||
@NotNull(message = "角色ID不能为空")
|
||||
private Long characterId;
|
||||
|
||||
@Size(max = 64, message = "文件哈希长度不能超过 64")
|
||||
private String fileHash;
|
||||
|
||||
private Long fileId;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import com.leiyuwei.mhxy.common.dto.PageDTO;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CharacterImageQueryDTO extends PageDTO {
|
||||
private Long characterId;
|
||||
|
||||
@Size(max = 64, message = "文件哈希长度不能超过 64")
|
||||
private String fileHash;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CharacterImageUpdateDTO {
|
||||
@NotNull(message = "角色ID不能为空")
|
||||
private Long characterId;
|
||||
|
||||
@Size(max = 64, message = "文件哈希长度不能超过 64")
|
||||
private String fileHash;
|
||||
|
||||
private Long fileId;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import com.leiyuwei.mhxy.common.dto.PageDTO;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CharacterQueryDTO extends PageDTO {
|
||||
@Size(max = 50, message = "角色名称长度不能超过 50")
|
||||
private String characterName;
|
||||
|
||||
private Long accountId;
|
||||
private Long serverId;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CharacterUpdateDTO {
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
@Size(max = 50, message = "角色名称长度不能超过 50")
|
||||
private String characterName;
|
||||
|
||||
@NotNull(message = "账号ID不能为空")
|
||||
private Long accountId;
|
||||
|
||||
@NotNull(message = "服务器ID不能为空")
|
||||
private Long serverId;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GameCharacterKeywordCreateDTO {
|
||||
@NotBlank(message = "关键字不能为空")
|
||||
@Size(max = 100, message = "关键字长度不能超过 100")
|
||||
private String keyword;
|
||||
|
||||
@NotNull(message = "角色ID不能为空")
|
||||
private Long characterId;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import com.leiyuwei.mhxy.common.dto.PageDTO;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GameCharacterKeywordQueryDTO extends PageDTO {
|
||||
private Long characterId;
|
||||
|
||||
@Size(max = 100, message = "关键字长度不能超过 100")
|
||||
private String keyword;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GameCharacterKeywordUpdateDTO {
|
||||
@NotBlank(message = "关键字不能为空")
|
||||
@Size(max = 100, message = "关键字长度不能超过 100")
|
||||
private String keyword;
|
||||
|
||||
@NotNull(message = "角色ID不能为空")
|
||||
private Long characterId;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ServerCreateDTO {
|
||||
@NotBlank(message = "区服名称不能为空")
|
||||
@Size(max = 50, message = "区服名称长度不能超过 50")
|
||||
private String serverName;
|
||||
|
||||
@Size(max = 200, message = "描述长度不能超过 200")
|
||||
private String description;
|
||||
|
||||
private Boolean enabled = true;
|
||||
private Integer sort = 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import com.leiyuwei.mhxy.common.dto.PageDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ServerQueryDTO extends PageDTO {
|
||||
@Schema(title = "服务器名称")
|
||||
@Size(max = 50, message = "区服名称长度不能超过 50")
|
||||
private String serverName;
|
||||
private Boolean enabled;
|
||||
private String keyword;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.leiyuwei.mhxy.game.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ServerUpdateDTO {
|
||||
@NotBlank(message = "区服名称不能为空")
|
||||
@Size(max = 50, message = "区服名称长度不能超过 50")
|
||||
private String serverName;
|
||||
|
||||
@Size(max = 200, message = "描述长度不能超过 200")
|
||||
private String description;
|
||||
|
||||
private Boolean enabled;
|
||||
private Integer sort;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.leiyuwei.mhxy.game.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.leiyuwei.mhxy.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 游戏账号实体,对应 `account` 表。
|
||||
*/
|
||||
@Data
|
||||
@TableName("account")
|
||||
public class Account extends BaseEntity {
|
||||
private String accountName;
|
||||
private Long userId; // 关联 sys_user.id,可为空
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.leiyuwei.mhxy.business.model.entity;
|
||||
package com.leiyuwei.mhxy.game.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.leiyuwei.mhxy.common.entity.BaseEntity;
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.leiyuwei.mhxy.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 游戏角色实体,对应 `character` 表。
|
||||
*/
|
||||
@Data
|
||||
@TableName("game_character")
|
||||
public class GameCharacter extends BaseEntity {
|
||||
private String characterName;
|
||||
private Long accountId;
|
||||
private Long serverId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.leiyuwei.mhxy.game.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.leiyuwei.mhxy.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 角色图片关联实体,对应 `game_character_image` 表。
|
||||
*/
|
||||
@Data
|
||||
@TableName("game_character_image")
|
||||
public class GameCharacterImage extends BaseEntity {
|
||||
private String fileHash;
|
||||
private Long gameCharacterId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.leiyuwei.mhxy.game.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.leiyuwei.mhxy.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 角色关键字实体,对应 `game_character_keyword` 表。
|
||||
*/
|
||||
@Data
|
||||
@TableName("game_character_keyword")
|
||||
public class GameCharacterKeyword extends BaseEntity {
|
||||
private String keyword;
|
||||
private Long gameCharacterId;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.leiyuwei.mhxy.game.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.leiyuwei.mhxy.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 邀请码实体(最小实现)
|
||||
*/
|
||||
@Data
|
||||
@TableName("invitation_code")
|
||||
public class InvitationCode extends BaseEntity {
|
||||
/**
|
||||
* 邀请码唯一字符串
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 最大使用次数
|
||||
*/
|
||||
private Integer maxUsage;
|
||||
|
||||
/**
|
||||
* 已使用次数
|
||||
*/
|
||||
private Integer usedCount;
|
||||
|
||||
/**
|
||||
* 失效时间戳(毫秒),为空表示永不过期
|
||||
*/
|
||||
private Long expireTime;
|
||||
|
||||
/**
|
||||
* 是否启用(1 启用,0 禁用)
|
||||
*/
|
||||
private Integer enabled;
|
||||
|
||||
/**
|
||||
* 是否已删除(0 未删,1 已删)
|
||||
*/
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.leiyuwei.mhxy.game.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.leiyuwei.mhxy.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class Server extends BaseEntity {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private String serverName;
|
||||
private String description;
|
||||
// private Boolean enabled = true;
|
||||
// private Integer sort = 0;
|
||||
// private Integer deleted = 0; // 逻辑删除字段
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
14
src/main/java/com/leiyuwei/mhxy/game/model/vo/AccountVO.java
Normal file
14
src/main/java/com/leiyuwei/mhxy/game/model/vo/AccountVO.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.leiyuwei.mhxy.game.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class AccountVO {
|
||||
private Long id;
|
||||
private String accountName;
|
||||
private Long userId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.leiyuwei.mhxy.game.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class CharacterImageVO {
|
||||
private Long id;
|
||||
private String fileHash;
|
||||
private Long characterId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.leiyuwei.mhxy.game.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class CharacterVO {
|
||||
private Long id;
|
||||
private String characterName;
|
||||
private Long accountId;
|
||||
private Long serverId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.leiyuwei.mhxy.business.model.vo;
|
||||
package com.leiyuwei.mhxy.game.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.leiyuwei.mhxy.game.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class GameCharacterKeywordVO {
|
||||
private Long id;
|
||||
private String keyword;
|
||||
private Long characterId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
16
src/main/java/com/leiyuwei/mhxy/game/model/vo/ServerVO.java
Normal file
16
src/main/java/com/leiyuwei/mhxy/game/model/vo/ServerVO.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.leiyuwei.mhxy.game.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class ServerVO {
|
||||
private Long id;
|
||||
private String serverName;
|
||||
private String description;
|
||||
private Boolean enabled;
|
||||
private Integer sort;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.leiyuwei.mhxy.game.service;
|
||||
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.AccountVO;
|
||||
|
||||
public interface AccountService {
|
||||
PageVO<AccountVO> getAccountList(AccountQueryDTO query);
|
||||
|
||||
AccountVO getAccountById(Long id);
|
||||
|
||||
AccountVO createAccount(AccountCreateDTO dto);
|
||||
|
||||
AccountVO updateAccount(Long id, AccountUpdateDTO dto);
|
||||
|
||||
void deleteAccount(Long id);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.leiyuwei.mhxy.game.service;
|
||||
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.CharacterImageVO;
|
||||
|
||||
public interface CharacterImageService {
|
||||
PageVO<CharacterImageVO> getCharacterImageList(CharacterImageQueryDTO query);
|
||||
|
||||
CharacterImageVO getCharacterImageById(Long id);
|
||||
|
||||
CharacterImageVO createCharacterImage(CharacterImageCreateDTO dto);
|
||||
|
||||
CharacterImageVO updateCharacterImage(Long id, CharacterImageUpdateDTO dto);
|
||||
|
||||
void deleteCharacterImage(Long id);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.leiyuwei.mhxy.business.service;
|
||||
package com.leiyuwei.mhxy.game.service;
|
||||
|
||||
import com.leiyuwei.mhxy.business.model.vo.FileUploadVO;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.FileUploadVO;
|
||||
import io.minio.MinioClient;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -36,6 +37,26 @@ public interface FileService {
|
||||
*/
|
||||
FileUploadVO getFileByHash(String fileHash);
|
||||
|
||||
/**
|
||||
* 根据ID获取文件信息
|
||||
*/
|
||||
FileUploadVO getFileById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询文件
|
||||
*/
|
||||
PageVO<FileUploadVO> getFileList(Integer pageNum, Integer pageSize, String fileHash);
|
||||
|
||||
/**
|
||||
* 文件引用计数 +1
|
||||
*/
|
||||
void increaseReferenceByHash(String fileHash);
|
||||
|
||||
/**
|
||||
* 文件引用计数 -1
|
||||
*/
|
||||
void decreaseReferenceByHash(String fileHash);
|
||||
|
||||
/**
|
||||
* 生成临时访问 URL
|
||||
*/
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.leiyuwei.mhxy.game.service;
|
||||
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.GameCharacterKeywordVO;
|
||||
|
||||
public interface GameCharacterKeywordService {
|
||||
PageVO<GameCharacterKeywordVO> getCharacterKeywordList(GameCharacterKeywordQueryDTO query);
|
||||
|
||||
GameCharacterKeywordVO getCharacterKeywordById(Long id);
|
||||
|
||||
GameCharacterKeywordVO createCharacterKeyword(GameCharacterKeywordCreateDTO dto);
|
||||
|
||||
GameCharacterKeywordVO updateCharacterKeyword(Long id, GameCharacterKeywordUpdateDTO dto);
|
||||
|
||||
void deleteCharacterKeyword(Long id);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.leiyuwei.mhxy.game.service;
|
||||
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.CharacterVO;
|
||||
|
||||
public interface GameCharacterService {
|
||||
PageVO<CharacterVO> getCharacterList(CharacterQueryDTO query);
|
||||
|
||||
CharacterVO getCharacterById(Long id);
|
||||
|
||||
CharacterVO createCharacter(CharacterCreateDTO dto);
|
||||
|
||||
CharacterVO updateCharacter(Long id, CharacterUpdateDTO dto);
|
||||
|
||||
void deleteCharacter(Long id);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.leiyuwei.mhxy.game.service;
|
||||
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.ServerVO;
|
||||
|
||||
public interface ServerService {
|
||||
/**
|
||||
* 分页查询服务器列表
|
||||
*/
|
||||
PageVO<ServerVO> getServerList(ServerQueryDTO query);
|
||||
|
||||
/**
|
||||
* 创建服务器
|
||||
*/
|
||||
ServerVO createServer(ServerCreateDTO dto);
|
||||
|
||||
/**
|
||||
* 根据ID更新服务器
|
||||
*/
|
||||
ServerVO updateServer(Long id, ServerUpdateDTO dto);
|
||||
|
||||
/**
|
||||
* 根据ID删除服务器
|
||||
*/
|
||||
void deleteServer(Long id);
|
||||
|
||||
/**
|
||||
* 根据ID查询详情
|
||||
*/
|
||||
ServerVO getServerById(Long id);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.leiyuwei.mhxy.game.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.mapper.AccountMapper;
|
||||
|
||||
import com.leiyuwei.mhxy.game.mapper.GameCharacterMapper;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.AccountUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.entity.Account;
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacter;
|
||||
import com.leiyuwei.mhxy.game.model.vo.AccountVO;
|
||||
import com.leiyuwei.mhxy.game.service.AccountService;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserMapper;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AccountServiceImpl implements AccountService {
|
||||
|
||||
private final AccountMapper accountMapper;
|
||||
private final GameCharacterMapper characterMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
@Override
|
||||
public PageVO<AccountVO> getAccountList(AccountQueryDTO query) {
|
||||
LambdaQueryWrapper<Account> wrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.hasText(query.getAccountName())) {
|
||||
wrapper.like(Account::getAccountName, query.getAccountName());
|
||||
}
|
||||
if (query.getUserId() != null) {
|
||||
wrapper.eq(Account::getUserId, query.getUserId());
|
||||
}
|
||||
wrapper.orderByDesc(Account::getId);
|
||||
|
||||
IPage<Account> page = accountMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper);
|
||||
IPage<AccountVO> voPage = page.convert(this::toVO);
|
||||
return PageVO.from(voPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountVO getAccountById(Long id) {
|
||||
Account account = accountMapper.selectById(id);
|
||||
if (account == null) {
|
||||
throw new BusinessException("游戏账号不存在");
|
||||
}
|
||||
return toVO(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AccountVO createAccount(AccountCreateDTO dto) {
|
||||
checkUserExists(dto.getUserId());
|
||||
checkNameDuplicate(null, dto.getAccountName());
|
||||
|
||||
Account account = new Account();
|
||||
account.setAccountName(dto.getAccountName());
|
||||
account.setUserId(dto.getUserId());
|
||||
accountMapper.insert(account);
|
||||
return toVO(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AccountVO updateAccount(Long id, AccountUpdateDTO dto) {
|
||||
Account account = accountMapper.selectById(id);
|
||||
if (account == null) {
|
||||
throw new BusinessException("游戏账号不存在");
|
||||
}
|
||||
|
||||
checkUserExists(dto.getUserId());
|
||||
checkNameDuplicate(id, dto.getAccountName());
|
||||
|
||||
account.setAccountName(dto.getAccountName());
|
||||
account.setUserId(dto.getUserId());
|
||||
accountMapper.updateById(account);
|
||||
return toVO(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteAccount(Long id) {
|
||||
Account account = accountMapper.selectById(id);
|
||||
if (account == null) {
|
||||
throw new BusinessException("游戏账号不存在");
|
||||
}
|
||||
|
||||
Long linkedCharacterCount = characterMapper.selectCount(
|
||||
new LambdaQueryWrapper<GameCharacter>().eq(GameCharacter::getAccountId, id)
|
||||
);
|
||||
if (linkedCharacterCount != null && linkedCharacterCount > 0) {
|
||||
throw new BusinessException("该账号下存在角色,无法删除");
|
||||
}
|
||||
|
||||
accountMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void checkUserExists(Long userId) {
|
||||
SysUser user = sysUserMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
throw new BusinessException("关联用户不存在");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNameDuplicate(Long selfId, String accountName) {
|
||||
LambdaQueryWrapper<Account> wrapper = new LambdaQueryWrapper<Account>()
|
||||
.eq(Account::getAccountName, accountName);
|
||||
if (selfId != null) {
|
||||
wrapper.ne(Account::getId, selfId);
|
||||
}
|
||||
Long count = accountMapper.selectCount(wrapper);
|
||||
if (count != null && count > 0) {
|
||||
throw new BusinessException("账号名称已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private AccountVO toVO(Account entity) {
|
||||
AccountVO vo = new AccountVO();
|
||||
BeanUtils.copyProperties(entity, vo);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.leiyuwei.mhxy.game.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
|
||||
import com.leiyuwei.mhxy.game.mapper.GameCharacterImageMapper;
|
||||
import com.leiyuwei.mhxy.game.mapper.GameCharacterMapper;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterImageUpdateDTO;
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacter;
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacterImage;
|
||||
import com.leiyuwei.mhxy.game.model.vo.CharacterImageVO;
|
||||
import com.leiyuwei.mhxy.game.model.vo.FileUploadVO;
|
||||
import com.leiyuwei.mhxy.game.service.CharacterImageService;
|
||||
import com.leiyuwei.mhxy.game.service.FileService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CharacterImageServiceImpl implements CharacterImageService {
|
||||
|
||||
private final GameCharacterImageMapper characterImageMapper;
|
||||
private final GameCharacterMapper characterMapper;
|
||||
private final FileService fileService;
|
||||
|
||||
@Override
|
||||
public PageVO<CharacterImageVO> getCharacterImageList(CharacterImageQueryDTO query) {
|
||||
LambdaQueryWrapper<GameCharacterImage> wrapper = new LambdaQueryWrapper<>();
|
||||
if (query.getCharacterId() != null) {
|
||||
wrapper.eq(GameCharacterImage::getGameCharacterId, query.getCharacterId());
|
||||
}
|
||||
if (StringUtils.hasText(query.getFileHash())) {
|
||||
wrapper.eq(GameCharacterImage::getFileHash, query.getFileHash());
|
||||
}
|
||||
wrapper.orderByDesc(GameCharacterImage::getId);
|
||||
IPage<GameCharacterImage> page = characterImageMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper);
|
||||
IPage<CharacterImageVO> voPage = page.convert(this::toVO);
|
||||
return PageVO.from(voPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharacterImageVO getCharacterImageById(Long id) {
|
||||
GameCharacterImage image = characterImageMapper.selectById(id);
|
||||
if (image == null) {
|
||||
throw new BusinessException("图片不存在");
|
||||
}
|
||||
return toVO(image);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CharacterImageVO createCharacterImage(CharacterImageCreateDTO dto) {
|
||||
checkCharacterExists(dto.getCharacterId());
|
||||
String fileHash = resolveFileHash(dto.getFileHash(), dto.getFileId());
|
||||
|
||||
GameCharacterImage image = new GameCharacterImage();
|
||||
image.setGameCharacterId(dto.getCharacterId());
|
||||
image.setFileHash(fileHash);
|
||||
characterImageMapper.insert(image);
|
||||
|
||||
fileService.increaseReferenceByHash(fileHash);
|
||||
return toVO(image);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CharacterImageVO updateCharacterImage(Long id, CharacterImageUpdateDTO dto) {
|
||||
GameCharacterImage image = characterImageMapper.selectById(id);
|
||||
if (image == null) {
|
||||
throw new BusinessException("图片不存在");
|
||||
}
|
||||
|
||||
checkCharacterExists(dto.getCharacterId());
|
||||
String newFileHash = resolveFileHash(dto.getFileHash(), dto.getFileId());
|
||||
|
||||
if (!newFileHash.equals(image.getFileHash())) {
|
||||
fileService.decreaseReferenceByHash(image.getFileHash());
|
||||
fileService.increaseReferenceByHash(newFileHash);
|
||||
image.setFileHash(newFileHash);
|
||||
}
|
||||
image.setGameCharacterId(dto.getCharacterId());
|
||||
|
||||
characterImageMapper.updateById(image);
|
||||
return toVO(image);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteCharacterImage(Long id) {
|
||||
GameCharacterImage image = characterImageMapper.selectById(id);
|
||||
if (image == null) {
|
||||
throw new BusinessException("图片不存在");
|
||||
}
|
||||
|
||||
fileService.decreaseReferenceByHash(image.getFileHash());
|
||||
characterImageMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private String resolveFileHash(String fileHash, Long fileId) {
|
||||
if (StringUtils.hasText(fileHash)) {
|
||||
fileService.getFileByHash(fileHash);
|
||||
return fileHash;
|
||||
}
|
||||
if (fileId != null) {
|
||||
FileUploadVO fileUploadVO = fileService.getFileById(fileId);
|
||||
return fileUploadVO.getFileHash();
|
||||
}
|
||||
throw new BusinessException("文件哈希或文件ID必须提供一个");
|
||||
}
|
||||
|
||||
private void checkCharacterExists(Long characterId) {
|
||||
GameCharacter gameCharacter = characterMapper.selectById(characterId);
|
||||
if (gameCharacter == null) {
|
||||
throw new BusinessException("关联角色不存在");
|
||||
}
|
||||
}
|
||||
|
||||
private CharacterImageVO toVO(GameCharacterImage entity) {
|
||||
CharacterImageVO vo = new CharacterImageVO();
|
||||
BeanUtils.copyProperties(entity, vo);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
package com.leiyuwei.mhxy.business.service.impl;
|
||||
package com.leiyuwei.mhxy.game.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.leiyuwei.mhxy.business.mapper.FileHashReferenceMapper;
|
||||
import com.leiyuwei.mhxy.business.model.entity.FileHashReference;
|
||||
import com.leiyuwei.mhxy.business.model.vo.FileUploadVO;
|
||||
import com.leiyuwei.mhxy.business.service.FileService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.config.MinioConfig;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.mapper.FileHashReferenceMapper;
|
||||
import com.leiyuwei.mhxy.game.model.entity.FileHashReference;
|
||||
import com.leiyuwei.mhxy.game.model.vo.FileUploadVO;
|
||||
import com.leiyuwei.mhxy.game.service.FileService;
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
@@ -156,6 +160,48 @@ public class FileServiceImpl implements FileService {
|
||||
return convertToVO(fileRef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileUploadVO getFileById(Long id) {
|
||||
FileHashReference fileRef = fileHashReferenceMapper.selectById(id);
|
||||
if (fileRef == null) {
|
||||
throw new RuntimeException("文件不存在");
|
||||
}
|
||||
return convertToVO(fileRef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageVO<FileUploadVO> getFileList(Integer pageNum, Integer pageSize, String fileHash) {
|
||||
LambdaQueryWrapper<FileHashReference> wrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.hasText(fileHash)) {
|
||||
wrapper.like(FileHashReference::getFileHash, fileHash);
|
||||
}
|
||||
wrapper.orderByDesc(FileHashReference::getId);
|
||||
|
||||
IPage<FileHashReference> page = fileHashReferenceMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);
|
||||
IPage<FileUploadVO> voPage = page.convert(this::convertToVO);
|
||||
return PageVO.from(voPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void increaseReferenceByHash(String fileHash) {
|
||||
FileHashReference fileRef = fileHashReferenceMapper.selectOne(
|
||||
new LambdaQueryWrapper<FileHashReference>()
|
||||
.eq(FileHashReference::getFileHash, fileHash)
|
||||
);
|
||||
if (fileRef == null) {
|
||||
throw new RuntimeException("文件不存在");
|
||||
}
|
||||
fileRef.setReferenceCount(fileRef.getReferenceCount() + 1);
|
||||
fileHashReferenceMapper.updateById(fileRef);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void decreaseReferenceByHash(String fileHash) {
|
||||
deleteFile(fileHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generatePresignedUrl(String fileHash, Integer expirationMinutes) {
|
||||
try {
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.leiyuwei.mhxy.game.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.mapper.CharacterKeywordMapper;
|
||||
|
||||
import com.leiyuwei.mhxy.game.mapper.GameCharacterMapper;
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordUpdateDTO;
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.dto.GameCharacterKeywordQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacter;
|
||||
import com.leiyuwei.mhxy.game.model.entity.GameCharacterKeyword;
|
||||
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.vo.GameCharacterKeywordVO;
|
||||
import com.leiyuwei.mhxy.game.service.GameCharacterKeywordService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GameCharacterKeywordServiceImpl implements GameCharacterKeywordService {
|
||||
|
||||
private final CharacterKeywordMapper characterKeywordMapper;
|
||||
private final GameCharacterMapper characterMapper;
|
||||
|
||||
@Override
|
||||
public PageVO<GameCharacterKeywordVO> getCharacterKeywordList(GameCharacterKeywordQueryDTO query) {
|
||||
LambdaQueryWrapper<GameCharacterKeyword> wrapper = new LambdaQueryWrapper<>();
|
||||
if (query.getCharacterId() != null) {
|
||||
wrapper.eq(GameCharacterKeyword::getGameCharacterId, query.getCharacterId());
|
||||
}
|
||||
if (StringUtils.hasText(query.getKeyword())) {
|
||||
wrapper.like(GameCharacterKeyword::getKeyword, query.getKeyword());
|
||||
}
|
||||
wrapper.orderByDesc(GameCharacterKeyword::getId);
|
||||
|
||||
IPage<GameCharacterKeyword> page = characterKeywordMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper);
|
||||
IPage<GameCharacterKeywordVO> voPage = page.convert(this::toVO);
|
||||
return PageVO.from(voPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameCharacterKeywordVO getCharacterKeywordById(Long id) {
|
||||
GameCharacterKeyword keyword = characterKeywordMapper.selectById(id);
|
||||
if (keyword == null) {
|
||||
throw new BusinessException("关键字不存在");
|
||||
}
|
||||
return toVO(keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public GameCharacterKeywordVO createCharacterKeyword(GameCharacterKeywordCreateDTO dto) {
|
||||
checkCharacterExists(dto.getCharacterId());
|
||||
checkKeywordDuplicate(null, dto.getCharacterId(), dto.getKeyword());
|
||||
|
||||
GameCharacterKeyword keyword = new GameCharacterKeyword();
|
||||
keyword.setGameCharacterId(dto.getCharacterId());
|
||||
keyword.setKeyword(dto.getKeyword());
|
||||
characterKeywordMapper.insert(keyword);
|
||||
return toVO(keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public GameCharacterKeywordVO updateCharacterKeyword(Long id, GameCharacterKeywordUpdateDTO dto) {
|
||||
GameCharacterKeyword keyword = characterKeywordMapper.selectById(id);
|
||||
if (keyword == null) {
|
||||
throw new BusinessException("关键字不存在");
|
||||
}
|
||||
|
||||
checkCharacterExists(dto.getCharacterId());
|
||||
checkKeywordDuplicate(id, dto.getCharacterId(), dto.getKeyword());
|
||||
|
||||
keyword.setGameCharacterId(dto.getCharacterId());
|
||||
keyword.setKeyword(dto.getKeyword());
|
||||
characterKeywordMapper.updateById(keyword);
|
||||
return toVO(keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteCharacterKeyword(Long id) {
|
||||
GameCharacterKeyword keyword = characterKeywordMapper.selectById(id);
|
||||
if (keyword == null) {
|
||||
throw new BusinessException("关键字不存在");
|
||||
}
|
||||
characterKeywordMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void checkCharacterExists(Long characterId) {
|
||||
GameCharacter character = characterMapper.selectById(characterId);
|
||||
if (character == null) {
|
||||
throw new BusinessException("关联角色不存在");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkKeywordDuplicate(Long selfId, Long characterId, String keyword) {
|
||||
LambdaQueryWrapper<GameCharacterKeyword> wrapper = new LambdaQueryWrapper<GameCharacterKeyword>()
|
||||
.eq(GameCharacterKeyword::getGameCharacterId, characterId)
|
||||
.eq(GameCharacterKeyword::getKeyword, keyword);
|
||||
if (selfId != null) {
|
||||
wrapper.ne(GameCharacterKeyword::getId, selfId);
|
||||
}
|
||||
Long count = characterKeywordMapper.selectCount(wrapper);
|
||||
if (count != null && count > 0) {
|
||||
throw new BusinessException("关键字已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private GameCharacterKeywordVO toVO(GameCharacterKeyword entity) {
|
||||
GameCharacterKeywordVO vo = new GameCharacterKeywordVO();
|
||||
BeanUtils.copyProperties(entity, vo);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.leiyuwei.mhxy.game.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.mapper.*;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.CharacterUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.entity.*;
|
||||
|
||||
import com.leiyuwei.mhxy.game.model.vo.CharacterVO;
|
||||
|
||||
import com.leiyuwei.mhxy.game.service.GameCharacterService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GameCharacterServiceImpl implements GameCharacterService {
|
||||
|
||||
private final GameCharacterMapper characterMapper;
|
||||
private final AccountMapper accountMapper;
|
||||
private final ServerMapper serverMapper;
|
||||
private final GameCharacterImageMapper characterImageMapper;
|
||||
private final CharacterKeywordMapper characterKeywordMapper;
|
||||
|
||||
@Override
|
||||
public PageVO<CharacterVO> getCharacterList(CharacterQueryDTO query) {
|
||||
LambdaQueryWrapper<GameCharacter> wrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.hasText(query.getCharacterName())) {
|
||||
wrapper.like(GameCharacter::getCharacterName, query.getCharacterName());
|
||||
}
|
||||
if (query.getAccountId() != null) {
|
||||
wrapper.eq(GameCharacter::getAccountId, query.getAccountId());
|
||||
}
|
||||
if (query.getServerId() != null) {
|
||||
wrapper.eq(GameCharacter::getServerId, query.getServerId());
|
||||
}
|
||||
wrapper.orderByDesc(GameCharacter::getId);
|
||||
|
||||
IPage<GameCharacter> page = characterMapper.selectPage(
|
||||
new Page<>(query.getPageNum(), query.getPageSize()), wrapper
|
||||
);
|
||||
IPage<CharacterVO> voPage = page.convert(this::toVO);
|
||||
return PageVO.from(voPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharacterVO getCharacterById(Long id) {
|
||||
GameCharacter character = characterMapper.selectById(id);
|
||||
if (character == null) {
|
||||
throw new BusinessException("角色不存在");
|
||||
}
|
||||
return toVO(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CharacterVO createCharacter(CharacterCreateDTO dto) {
|
||||
checkRelatedExists(dto.getAccountId(), dto.getServerId());
|
||||
|
||||
GameCharacter character = new GameCharacter();
|
||||
character.setCharacterName(dto.getCharacterName());
|
||||
character.setAccountId(dto.getAccountId());
|
||||
character.setServerId(dto.getServerId());
|
||||
characterMapper.insert(character);
|
||||
return toVO(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CharacterVO updateCharacter(Long id, CharacterUpdateDTO dto) {
|
||||
GameCharacter character = characterMapper.selectById(id);
|
||||
if (character == null) {
|
||||
throw new BusinessException("角色不存在");
|
||||
}
|
||||
|
||||
checkRelatedExists(dto.getAccountId(), dto.getServerId());
|
||||
|
||||
character.setCharacterName(dto.getCharacterName());
|
||||
character.setAccountId(dto.getAccountId());
|
||||
character.setServerId(dto.getServerId());
|
||||
characterMapper.updateById(character);
|
||||
return toVO(character);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteCharacter(Long id) {
|
||||
GameCharacter character = characterMapper.selectById(id);
|
||||
if (character == null) {
|
||||
throw new BusinessException("角色不存在");
|
||||
}
|
||||
|
||||
Long imageCount = characterImageMapper.selectCount(
|
||||
new LambdaQueryWrapper<GameCharacterImage>().eq(GameCharacterImage::getGameCharacterId, id)
|
||||
);
|
||||
if (imageCount != null && imageCount > 0) {
|
||||
throw new BusinessException("角色下存在图片,无法删除");
|
||||
}
|
||||
|
||||
Long keywordCount = characterKeywordMapper.selectCount(
|
||||
new LambdaQueryWrapper<GameCharacterKeyword>().eq(GameCharacterKeyword::getGameCharacterId, id)
|
||||
);
|
||||
if (keywordCount != null && keywordCount > 0) {
|
||||
throw new BusinessException("角色下存在关键字,无法删除");
|
||||
}
|
||||
|
||||
characterMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void checkRelatedExists(Long accountId, Long serverId) {
|
||||
Account account = accountMapper.selectById(accountId);
|
||||
if (account == null) {
|
||||
throw new BusinessException("关联账号不存在");
|
||||
}
|
||||
|
||||
Server server = serverMapper.selectById(serverId);
|
||||
if (server == null) {
|
||||
throw new BusinessException("关联服务器不存在");
|
||||
}
|
||||
}
|
||||
|
||||
private CharacterVO toVO(GameCharacter entity) {
|
||||
CharacterVO vo = new CharacterVO();
|
||||
BeanUtils.copyProperties(entity, vo);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.leiyuwei.mhxy.game.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.game.mapper.ServerMapper;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerCreateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerQueryDTO;
|
||||
import com.leiyuwei.mhxy.game.model.dto.ServerUpdateDTO;
|
||||
import com.leiyuwei.mhxy.game.model.entity.Server;
|
||||
import com.leiyuwei.mhxy.game.model.vo.ServerVO;
|
||||
import com.leiyuwei.mhxy.game.service.ServerService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ServerServiceImpl implements ServerService {
|
||||
|
||||
private final ServerMapper serverMapper;
|
||||
|
||||
@Override
|
||||
public PageVO<ServerVO> getServerList(ServerQueryDTO query) {
|
||||
LambdaQueryWrapper<Server> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (StringUtils.hasText(query.getServerName())) {
|
||||
wrapper.like(Server::getServerName, query.getServerName());
|
||||
}
|
||||
|
||||
// if (query.getEnabled() != null) {
|
||||
// wrapper.eq(Server::getEnabled, query.getEnabled());
|
||||
// }
|
||||
|
||||
wrapper.orderByDesc(Server::getId);
|
||||
|
||||
Page<Server> page = new Page<>(query.getPageNum(), query.getPageSize());
|
||||
IPage<Server> serverPage = serverMapper.selectPage(page, wrapper);
|
||||
IPage<ServerVO> voPage = serverPage.convert(this::convertToVO);
|
||||
return PageVO.from(voPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ServerVO createServer(ServerCreateDTO dto) {
|
||||
// 检查服务器名称是否重复
|
||||
if (isServerNameExists(dto.getServerName())) {
|
||||
throw new BusinessException("服务器名称已存在");
|
||||
}
|
||||
|
||||
// 创建实体
|
||||
Server server = new Server();
|
||||
server.setServerName(dto.getServerName());
|
||||
server.setDescription(dto.getDescription());
|
||||
//server.setEnabled(dto.getEnabled());
|
||||
// server.setSort(dto.getSort());
|
||||
|
||||
// 插入数据库
|
||||
serverMapper.insert(server);
|
||||
|
||||
return convertToVO(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ServerVO updateServer(Long id, ServerUpdateDTO dto) {
|
||||
// 检查服务器是否存在
|
||||
Server existingServer = serverMapper.selectById(id);
|
||||
if (existingServer == null) {
|
||||
throw new BusinessException("服务器不存在");
|
||||
}
|
||||
|
||||
// 检查服务器名称是否重复(排除自身)
|
||||
if (!existingServer.getServerName().equals(dto.getServerName())
|
||||
&& isServerNameExists(dto.getServerName())) {
|
||||
throw new BusinessException("服务器名称已存在");
|
||||
}
|
||||
|
||||
// 更新实体
|
||||
existingServer.setServerName(dto.getServerName());
|
||||
existingServer.setDescription(dto.getDescription());
|
||||
// existingServer.setEnabled(dto.getEnabled());
|
||||
// existingServer.setSort(dto.getSort());
|
||||
existingServer.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
serverMapper.updateById(existingServer);
|
||||
|
||||
return convertToVO(existingServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteServer(Long id) {
|
||||
Server server = serverMapper.selectById(id);
|
||||
if (server == null) {
|
||||
throw new BusinessException("服务器不存在");
|
||||
}
|
||||
|
||||
// 删除服务器
|
||||
serverMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerVO getServerById(Long id) {
|
||||
Server server = serverMapper.selectById(id);
|
||||
if (server == null) {
|
||||
throw new BusinessException("服务器不存在");
|
||||
}
|
||||
|
||||
return convertToVO(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查服务器名称是否已存在
|
||||
*/
|
||||
private boolean isServerNameExists(String serverName) {
|
||||
LambdaQueryWrapper<Server> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Server::getServerName, serverName);
|
||||
return serverMapper.selectCount(wrapper) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为VO对象
|
||||
*/
|
||||
private ServerVO convertToVO(Server server) {
|
||||
ServerVO vo = new ServerVO();
|
||||
vo.setId(server.getId());
|
||||
vo.setServerName(server.getServerName());
|
||||
vo.setDescription(server.getDescription());
|
||||
//vo.setEnabled(server.getEnabled());
|
||||
// vo.setSort(server.getSort());
|
||||
vo.setCreatedAt(server.getCreatedAt());
|
||||
vo.setUpdatedAt(server.getUpdatedAt());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public class AuthController {
|
||||
* 用户登录
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "用户登录", description = "用户名密码登录,返回 Access Token 和 Refresh Token")
|
||||
@Operation(summary = "用户登录", description = "用户名密码登录,返回 Access Token 和 Refresh Token。后续访问受保护接口时,请在 Authorization 请求头中传 Bearer {accessToken}")
|
||||
public Result<LoginVO> login(@Valid @RequestBody UserLoginDTO dto) {
|
||||
LoginVO loginVO = authService.login(dto);
|
||||
return Result.success(loginVO);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class SysPermissionController {
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "获取权限列表", description = "分页查询权限列表")
|
||||
@PreAuthorize("hasAuthority('permission:list')")
|
||||
@PreAuthorize("hasAuthority('system:permission:list')")
|
||||
public Result<PageVO<SysPermission>> getPermissionList(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
@@ -50,7 +50,7 @@ public class SysPermissionController {
|
||||
*/
|
||||
@GetMapping("/tree")
|
||||
@Operation(summary = "获取权限树", description = "获取权限树形结构")
|
||||
@PreAuthorize("hasAuthority('permission:view')")
|
||||
@PreAuthorize("hasAuthority('system:permission:list')")
|
||||
public Result<List<PermissionTreeVO>> getPermissionTree() {
|
||||
List<PermissionTreeVO> tree = sysPermissionService.getPermissionTree();
|
||||
return Result.success(tree);
|
||||
@@ -61,10 +61,9 @@ public class SysPermissionController {
|
||||
*/
|
||||
@GetMapping("/menus")
|
||||
@Operation(summary = "获取当前用户菜单树", description = "获取当前用户有权限访问的菜单树")
|
||||
@PreAuthorize("hasAuthority('permission:view')")
|
||||
@PreAuthorize("hasAuthority('system:permission:list')")
|
||||
public Result<List<PermissionTreeVO>> getMenus() {
|
||||
// TODO: 实现从 SecurityContext 获取用户权限
|
||||
List<PermissionTreeVO> menus = sysPermissionService.getPermissionTree();
|
||||
List<PermissionTreeVO> menus = sysPermissionService.getMenus();
|
||||
return Result.success(menus);
|
||||
}
|
||||
|
||||
@@ -73,7 +72,7 @@ public class SysPermissionController {
|
||||
*/
|
||||
@PostMapping
|
||||
@Operation(summary = "创建权限", description = "创建新权限")
|
||||
@PreAuthorize("hasAuthority('permission:create')")
|
||||
@PreAuthorize("hasAuthority('system:permission:add')")
|
||||
public Result<SysPermission> createPermission(@Valid @RequestBody SysPermission permission) {
|
||||
sysPermissionService.createPermission(permission);
|
||||
return Result.success("权限创建成功", permission);
|
||||
@@ -84,7 +83,7 @@ public class SysPermissionController {
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新权限", description = "更新权限信息")
|
||||
@PreAuthorize("hasAuthority('permission:update')")
|
||||
@PreAuthorize("hasAuthority('system:permission:edit')")
|
||||
public Result<Void> updatePermission(@PathVariable Long id, @RequestBody SysPermission permission) {
|
||||
permission.setId(id);
|
||||
sysPermissionService.updatePermission(permission);
|
||||
@@ -96,7 +95,7 @@ public class SysPermissionController {
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除权限", description = "删除指定权限(会同时删除子权限)")
|
||||
@PreAuthorize("hasAuthority('permission:delete')")
|
||||
@PreAuthorize("hasAuthority('system:permission:delete')")
|
||||
public Result<Void> deletePermission(@PathVariable Long id) {
|
||||
sysPermissionService.deletePermission(id);
|
||||
return Result.success("权限删除成功", null);
|
||||
|
||||
@@ -31,7 +31,7 @@ public class SysRoleController {
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "获取角色列表", description = "分页查询角色列表")
|
||||
@PreAuthorize("hasAuthority('role:list')")
|
||||
@PreAuthorize("hasAuthority('system:role:list')")
|
||||
public Result<PageVO<RoleVO>> getRoleList(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
@@ -43,7 +43,7 @@ public class SysRoleController {
|
||||
* 获取角色详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAuthority('role:view')")
|
||||
@PreAuthorize("hasAuthority('system:role:list')")
|
||||
public Result<RoleVO> getRoleById(@PathVariable Long id) {
|
||||
RoleVO roleVO = sysRoleService.getRoleById(id);
|
||||
return Result.success(roleVO);
|
||||
@@ -54,7 +54,7 @@ public class SysRoleController {
|
||||
*/
|
||||
@PostMapping
|
||||
@Operation(summary = "创建角色", description = "创建新角色")
|
||||
@PreAuthorize("hasAuthority('role:create')")
|
||||
@PreAuthorize("hasAuthority('system:role:add')")
|
||||
public Result<RoleVO> createRole(@Valid @RequestBody SysRole role) {
|
||||
sysRoleService.createRole(role);
|
||||
return Result.success("角色创建成功", null);
|
||||
@@ -65,7 +65,7 @@ public class SysRoleController {
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新角色", description = "更新角色信息")
|
||||
@PreAuthorize("hasAuthority('role:update')")
|
||||
@PreAuthorize("hasAuthority('system:role:edit')")
|
||||
public Result<Void> updateRole(@PathVariable Long id, @RequestBody SysRole role) {
|
||||
role.setId(id);
|
||||
sysRoleService.updateRole(role);
|
||||
@@ -77,7 +77,7 @@ public class SysRoleController {
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除角色", description = "删除指定角色(逻辑删除)")
|
||||
@PreAuthorize("hasAuthority('role:delete')")
|
||||
@PreAuthorize("hasAuthority('system:role:delete')")
|
||||
public Result<Void> deleteRole(@PathVariable Long id) {
|
||||
sysRoleService.deleteRole(id);
|
||||
return Result.success("角色删除成功", null);
|
||||
@@ -88,7 +88,7 @@ public class SysRoleController {
|
||||
*/
|
||||
@PutMapping("/{id}/permissions")
|
||||
@Operation(summary = "分配权限", description = "为角色分配权限")
|
||||
@PreAuthorize("hasAuthority('role:assign-permissions')")
|
||||
@PreAuthorize("hasAuthority('system:role:assign')")
|
||||
public Result<Void> assignPermissions(@PathVariable Long id, @RequestBody List<Long> permissionIds) {
|
||||
sysRoleService.assignPermissions(id, permissionIds);
|
||||
return Result.success("权限分配成功", null);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.leiyuwei.mhxy.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.common.result.Result;
|
||||
import com.leiyuwei.mhxy.common.vo.PageVO;
|
||||
import com.leiyuwei.mhxy.system.model.dto.UserRegisterDTO;
|
||||
@@ -12,6 +13,8 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
@@ -34,9 +37,18 @@ public class SysUserController {
|
||||
@GetMapping("/current")
|
||||
@Operation(summary = "获取当前用户信息", description = "获取当前登录用户的详细信息")
|
||||
public Result<UserVO> getCurrentUser() {
|
||||
// TODO: 从 SecurityContext 获取当前用户 ID
|
||||
Long userId = 1L;
|
||||
UserVO userVO = sysUserService.getUserById(userId);
|
||||
// 从 SecurityContext 获取当前用户名
|
||||
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
String username;
|
||||
if (principal instanceof UserDetails) {
|
||||
username = ((UserDetails) principal).getUsername();
|
||||
} else {
|
||||
username = principal.toString();
|
||||
}
|
||||
UserVO userVO = sysUserService.getUserByUsername(username);
|
||||
if (userVO == null) {
|
||||
throw new BusinessException("当前用户不存在");
|
||||
}
|
||||
return Result.success(userVO);
|
||||
}
|
||||
|
||||
@@ -45,7 +57,7 @@ public class SysUserController {
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "获取用户列表", description = "分页查询用户列表")
|
||||
@PreAuthorize("hasAuthority('user:list')")
|
||||
@PreAuthorize("hasAuthority('system:user:list')")
|
||||
public Result<PageVO<UserVO>> getUserList(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
@@ -58,7 +70,7 @@ public class SysUserController {
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取用户详情", description = "根据用户 ID 查询用户详细信息")
|
||||
@PreAuthorize("hasAuthority('user:view')")
|
||||
@PreAuthorize("hasAuthority('system:user:list')")
|
||||
public Result<UserVO> getUserById(@PathVariable Long id) {
|
||||
UserVO userVO = sysUserService.getUserById(id);
|
||||
return Result.success(userVO);
|
||||
@@ -69,10 +81,10 @@ public class SysUserController {
|
||||
*/
|
||||
@PostMapping
|
||||
@Operation(summary = "创建用户", description = "创建新用户")
|
||||
@PreAuthorize("hasAuthority('user:create')")
|
||||
@PreAuthorize("hasAuthority('system:user:add')")
|
||||
public Result<UserVO> createUser(@Valid @RequestBody UserRegisterDTO dto) {
|
||||
// TODO: 实现创建用户逻辑
|
||||
return Result.success("用户创建成功", null);
|
||||
UserVO userVO = sysUserService.createUser(dto);
|
||||
return Result.success("用户创建成功", userVO);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,9 +92,9 @@ public class SysUserController {
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新用户", description = "更新用户信息")
|
||||
@PreAuthorize("hasAuthority('user:update')")
|
||||
@PreAuthorize("hasAuthority('system:user:edit')")
|
||||
public Result<Void> updateUser(@PathVariable Long id, @RequestBody UserRegisterDTO dto) {
|
||||
// TODO: 实现更新用户逻辑
|
||||
sysUserService.updateUser(id, dto);
|
||||
return Result.success("用户更新成功", null);
|
||||
}
|
||||
|
||||
@@ -91,9 +103,9 @@ public class SysUserController {
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除用户", description = "删除指定用户(逻辑删除)")
|
||||
@PreAuthorize("hasAuthority('user:delete')")
|
||||
@PreAuthorize("hasAuthority('system:user:delete')")
|
||||
public Result<Void> deleteUser(@PathVariable Long id) {
|
||||
// TODO: 实现删除用户逻辑
|
||||
sysUserService.deleteUser(id);
|
||||
return Result.success("用户删除成功", null);
|
||||
}
|
||||
|
||||
@@ -102,9 +114,9 @@ public class SysUserController {
|
||||
*/
|
||||
@PutMapping("/{id}/reset-password")
|
||||
@Operation(summary = "重置密码", description = "重置用户密码")
|
||||
@PreAuthorize("hasAuthority('user:reset-password')")
|
||||
@PreAuthorize("hasAuthority('system:auth:password')")
|
||||
public Result<Void> resetPassword(@PathVariable Long id, @RequestParam String newPassword) {
|
||||
// TODO: 实现重置密码逻辑
|
||||
sysUserService.resetPassword(id, newPassword);
|
||||
return Result.success("密码重置成功", null);
|
||||
}
|
||||
|
||||
@@ -113,9 +125,9 @@ public class SysUserController {
|
||||
*/
|
||||
@PutMapping("/{id}/roles")
|
||||
@Operation(summary = "分配角色", description = "为用户分配角色")
|
||||
@PreAuthorize("hasAuthority('user:assign-roles')")
|
||||
@PreAuthorize("hasAuthority('system:user:assign')")
|
||||
public Result<Void> assignRoles(@PathVariable Long id, @RequestBody List<Long> roleIds) {
|
||||
// TODO: 实现分配角色逻辑
|
||||
sysUserService.assignRoles(id, roleIds);
|
||||
return Result.success("角色分配成功", null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,9 @@ public interface SysPermissionMapper extends BaseMapper<SysPermission> {
|
||||
* 查询角色的权限列表
|
||||
*/
|
||||
List<SysPermission> selectPermissionsByRoleId(@Param("roleId") Long roleId);
|
||||
|
||||
/**
|
||||
* 批量查询角色的权限列表
|
||||
*/
|
||||
List<SysPermission> selectPermissionsByRoleIds(@Param("roleIds") List<Long> roleIds);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.leiyuwei.mhxy.system.model.entity.SysPermission;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysRole;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,8 +17,10 @@ public interface SysRoleMapper extends BaseMapper<SysRole> {
|
||||
/**
|
||||
* 查询角色的权限列表
|
||||
*/
|
||||
@Select("SELECT p.* FROM sys_permission p " +
|
||||
"INNER JOIN sys_role_permission rp ON p.id = rp.permission_id " +
|
||||
"WHERE rp.role_id = #{roleId} AND p.deleted = 0")
|
||||
List<SysPermission> selectPermissionsByRoleId(@Param("roleId") Long roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色编码查询角色
|
||||
*/
|
||||
SysRole selectByCode(@Param("code") String code);
|
||||
}
|
||||
@@ -3,9 +3,9 @@ package com.leiyuwei.mhxy.system.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysRole;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUser;
|
||||
import com.leiyuwei.mhxy.system.model.vo.UserPermissionsVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,14 +18,15 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*/
|
||||
@Select("SELECT * FROM sys_user WHERE username = #{username} AND deleted = 0")
|
||||
SysUser selectByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 查询用户的角色列表
|
||||
*/
|
||||
@Select("SELECT r.* FROM sys_role r " +
|
||||
"INNER JOIN sys_user_role ur ON r.id = ur.role_id " +
|
||||
"WHERE ur.user_id = #{userId} AND r.deleted = 0 AND r.enabled = 1")
|
||||
List<SysRole> selectRolesByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户的权限信息(包含角色和权限编码)
|
||||
*/
|
||||
List<UserPermissionsVO> selectUserPermissionsById(@Param("userId") Long userId);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public class SysPermission extends BaseEntity {
|
||||
/**
|
||||
* 父权限 ID
|
||||
*/
|
||||
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,9 @@ import lombok.Data;
|
||||
@TableName("sys_role")
|
||||
public class SysRole extends BaseEntity {
|
||||
|
||||
// @TableId(type = IdType.ASSIGN_ID)
|
||||
// private Long id;
|
||||
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.leiyuwei.mhxy.system.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -9,17 +8,20 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_role_permission")
|
||||
public class SysRolePermission {
|
||||
public class SysRolePermission {
|
||||
|
||||
// @TableId(type = IdType.ASSIGN_ID)
|
||||
// private Long id;
|
||||
|
||||
/**
|
||||
* 角色 ID
|
||||
*/
|
||||
@TableId
|
||||
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* 权限 ID
|
||||
*/
|
||||
@TableId
|
||||
|
||||
private Long permissionId;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import lombok.Data;
|
||||
@TableName("sys_user")
|
||||
public class SysUser extends BaseEntity {
|
||||
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.leiyuwei.mhxy.system.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -9,17 +8,19 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_user_role")
|
||||
public class SysUserRole {
|
||||
public class SysUserRole {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 用户 ID
|
||||
*/
|
||||
@TableId
|
||||
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 角色 ID
|
||||
*/
|
||||
@TableId
|
||||
|
||||
private Long roleId;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.leiyuwei.mhxy.system.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 登录响应 VO
|
||||
*/
|
||||
@@ -23,6 +25,26 @@ public class LoginVO {
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像 URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 角色编码列表
|
||||
*/
|
||||
private List<String> roleCodes;
|
||||
|
||||
/**
|
||||
* 权限编码列表
|
||||
*/
|
||||
private List<String> permissionCodes;
|
||||
|
||||
/**
|
||||
* 是否为超级管理员
|
||||
*/
|
||||
private Boolean isAdmin;
|
||||
|
||||
/**
|
||||
* Access Token
|
||||
*/
|
||||
@@ -32,4 +54,4 @@ public class LoginVO {
|
||||
* Refresh Token
|
||||
*/
|
||||
private String refreshToken;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.leiyuwei.mhxy.system.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户权限信息VO
|
||||
*/
|
||||
@Data
|
||||
public class UserPermissionsVO {
|
||||
|
||||
/**
|
||||
* 用户 ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
private String roleCode;
|
||||
|
||||
/**
|
||||
* 权限编码
|
||||
*/
|
||||
private String permissionCode;
|
||||
|
||||
/**
|
||||
* 是否为超级管理员
|
||||
*/
|
||||
private Boolean isAdmin;
|
||||
}
|
||||
@@ -11,6 +11,11 @@ import java.util.List;
|
||||
*/
|
||||
public interface SysPermissionService {
|
||||
|
||||
/**
|
||||
* 获取角色下的权限列表
|
||||
*/
|
||||
List<SysPermission> getPermissionListByRoleId(Long roleId);
|
||||
|
||||
/**
|
||||
* 获取权限列表
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.leiyuwei.mhxy.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.leiyuwei.mhxy.system.model.dto.UserRegisterDTO;
|
||||
import com.leiyuwei.mhxy.system.model.vo.UserVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统用户服务接口
|
||||
*/
|
||||
@@ -17,4 +20,34 @@ public interface SysUserService {
|
||||
* 获取用户列表
|
||||
*/
|
||||
IPage<UserVO> getUserList(Integer pageNum, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
*/
|
||||
UserVO createUser(UserRegisterDTO dto);
|
||||
|
||||
/**
|
||||
* 更新用户
|
||||
*/
|
||||
void updateUser(Long id, UserRegisterDTO dto);
|
||||
|
||||
/**
|
||||
* 删除用户(逻辑删除)
|
||||
*/
|
||||
void deleteUser(Long id);
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
void resetPassword(Long id, String newPassword);
|
||||
|
||||
/**
|
||||
* 分配角色
|
||||
*/
|
||||
void assignRoles(Long userId, List<Long> roleIds);
|
||||
|
||||
/**
|
||||
* 根据用户名获取用户信息
|
||||
*/
|
||||
UserVO getUserByUsername(String username);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
package com.leiyuwei.mhxy.system.service.impl;
|
||||
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.common.util.JwtUtil;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysRoleMapper;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserMapper;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserRoleMapper;
|
||||
import com.leiyuwei.mhxy.system.model.dto.UserLoginDTO;
|
||||
import com.leiyuwei.mhxy.system.model.dto.UserRegisterDTO;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysRole;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUser;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUserRole;
|
||||
import com.leiyuwei.mhxy.system.model.vo.LoginVO;
|
||||
import com.leiyuwei.mhxy.system.service.AuthService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -26,7 +31,15 @@ import java.util.Map;
|
||||
public class AuthServiceImpl implements AuthService {
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final SysUserRoleMapper sysUserRoleMapper;
|
||||
private final SysRoleMapper sysRoleMapper;
|
||||
|
||||
private final JwtUtil jwtUtil;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final UserPermissionServiceImpl userPermissionService;
|
||||
|
||||
private static final String ROLE_ADMIN = "ROLE_ADMIN";
|
||||
private static final String DEFAULT_ROLE_CODE = "ROLE_GAME";
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -34,13 +47,13 @@ public class AuthServiceImpl implements AuthService {
|
||||
// 查询用户
|
||||
SysUser user = sysUserMapper.selectByUsername(dto.getUsername());
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户名或密码错误");
|
||||
throw new BusinessException("用户不存在或密码错误");
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
String encryptedPassword = DigestUtil.md5Hex(dto.getPassword());
|
||||
if (!encryptedPassword.equals(user.getPassword())) {
|
||||
throw new BusinessException("用户名或密码错误");
|
||||
boolean passwordMatch = passwordEncoder.matches(dto.getPassword(), user.getPassword());
|
||||
if (!passwordMatch) {
|
||||
throw new BusinessException("用户不存在或密码错误");
|
||||
}
|
||||
|
||||
// 检查用户是否启用
|
||||
@@ -48,8 +61,25 @@ public class AuthServiceImpl implements AuthService {
|
||||
throw new BusinessException("用户已被禁用");
|
||||
}
|
||||
|
||||
// 生成 Token
|
||||
// 查询用户权限信息(包含角色和权限编码)
|
||||
Map<String, Object> permissionInfo = userPermissionService.getUserPermissionInfo(user.getId());
|
||||
|
||||
if (permissionInfo.isEmpty()) {
|
||||
throw new BusinessException("用户未分配任何权限");
|
||||
}
|
||||
|
||||
// 从缓存信息中提取角色和权限编码
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> roleCodes = (List<String>) permissionInfo.get("roleCodes");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> permissionCodes = (List<String>) permissionInfo.get("permissionCodes");
|
||||
boolean isAdmin = Boolean.TRUE.equals(permissionInfo.get("isAdmin"));
|
||||
|
||||
// 生成 Token,携带角色和权限信息
|
||||
Map<String, Object> extraClaims = new HashMap<>();
|
||||
extraClaims.put("roleCodes", roleCodes);
|
||||
extraClaims.put("permissionCodes", permissionCodes);
|
||||
extraClaims.put("isAdmin", isAdmin);
|
||||
String accessToken = jwtUtil.generateAccessToken(user.getId(), user.getUsername(), extraClaims);
|
||||
String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());
|
||||
|
||||
@@ -58,10 +88,14 @@ public class AuthServiceImpl implements AuthService {
|
||||
vo.setUserId(user.getId());
|
||||
vo.setUsername(user.getUsername());
|
||||
vo.setNickname(user.getNickname());
|
||||
vo.setAvatar(user.getAvatar());
|
||||
vo.setRoleCodes(roleCodes);
|
||||
vo.setPermissionCodes(permissionCodes);
|
||||
vo.setIsAdmin(isAdmin);
|
||||
vo.setAccessToken(accessToken);
|
||||
vo.setRefreshToken(refreshToken);
|
||||
|
||||
log.info("用户登录成功: userId={}, username={}", user.getId(), user.getUsername());
|
||||
log.info("用户登录成功: userId={}, username={}, roles={}", user.getId(), user.getUsername(), roleCodes);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@@ -79,23 +113,47 @@ public class AuthServiceImpl implements AuthService {
|
||||
throw new BusinessException("两次输入的密码不一致");
|
||||
}
|
||||
|
||||
// TODO: 验证邀请码(需要在 InvitationCodeService 中实现)
|
||||
|
||||
// 加密密码
|
||||
String encryptedPassword = DigestUtil.md5Hex(dto.getPassword());
|
||||
String encryptedPassword = passwordEncoder.encode(dto.getPassword());
|
||||
|
||||
// 创建用户
|
||||
SysUser user = new SysUser();
|
||||
user.setUsername(dto.getUsername());
|
||||
user.setPassword(encryptedPassword);
|
||||
user.setNickname(dto.getUsername()); // 默认昵称为用户名
|
||||
//user.setNickname(dto.getNickname() != null ? dto.getNickname() : dto.getUsername());
|
||||
user.setNickname(dto.getUsername());
|
||||
// user.setEmail(dto.getEmail());
|
||||
// user.setPhone(dto.getPhone());
|
||||
user.setEnabled(true);
|
||||
|
||||
sysUserMapper.insert(user);
|
||||
|
||||
// 分配默认角色 game_role
|
||||
assignDefaultRole(user.getId());
|
||||
|
||||
log.info("用户注册成功: userId={}, username={}", user.getId(), user.getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* 为用户分配默认角色
|
||||
*/
|
||||
private void assignDefaultRole(Long userId) {
|
||||
// 查询默认角色
|
||||
SysRole defaultRole = sysRoleMapper.selectByCode(DEFAULT_ROLE_CODE);
|
||||
if (defaultRole == null) {
|
||||
log.warn("默认角色 {} 不存在,跳过分配", DEFAULT_ROLE_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建用户角色关联
|
||||
SysUserRole userRole = new SysUserRole();
|
||||
userRole.setUserId(userId);
|
||||
userRole.setRoleId(defaultRole.getId());
|
||||
sysUserRoleMapper.insert(userRole);
|
||||
|
||||
log.info("为用户 {} 分配默认角色: {}", userId, DEFAULT_ROLE_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginVO refreshToken(String refreshToken) {
|
||||
// 验证 Refresh Token
|
||||
@@ -107,8 +165,25 @@ public class AuthServiceImpl implements AuthService {
|
||||
Long userId = jwtUtil.getUserIdFromToken(refreshToken);
|
||||
String username = jwtUtil.getUsernameFromToken(refreshToken);
|
||||
|
||||
// 生成新的 Access Token
|
||||
// 查询用户权限信息(包含角色和权限编码)
|
||||
Map<String, Object> permissionInfo = userPermissionService.getUserPermissionInfo(userId);
|
||||
|
||||
if (permissionInfo.isEmpty()) {
|
||||
throw new BusinessException("用户未分配任何权限");
|
||||
}
|
||||
|
||||
// 从缓存信息中提取角色和权限编码
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> roleCodes = (List<String>) permissionInfo.get("roleCodes");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> permissionCodes = (List<String>) permissionInfo.get("permissionCodes");
|
||||
boolean isAdmin = Boolean.TRUE.equals(permissionInfo.get("isAdmin"));
|
||||
|
||||
// 生成新的 Access Token,携带角色和权限信息
|
||||
Map<String, Object> extraClaims = new HashMap<>();
|
||||
extraClaims.put("roleCodes", roleCodes);
|
||||
extraClaims.put("permissionCodes", permissionCodes);
|
||||
extraClaims.put("isAdmin", isAdmin);
|
||||
String newAccessToken = jwtUtil.generateAccessToken(userId, username, extraClaims);
|
||||
|
||||
// 生成新的 Refresh Token
|
||||
@@ -118,10 +193,13 @@ public class AuthServiceImpl implements AuthService {
|
||||
LoginVO vo = new LoginVO();
|
||||
vo.setUserId(userId);
|
||||
vo.setUsername(username);
|
||||
vo.setRoleCodes(roleCodes);
|
||||
vo.setPermissionCodes(permissionCodes);
|
||||
vo.setIsAdmin(isAdmin);
|
||||
vo.setAccessToken(newAccessToken);
|
||||
vo.setRefreshToken(newRefreshToken);
|
||||
|
||||
log.info("Token 刷新成功: userId={}", userId);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.leiyuwei.mhxy.system.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 权限缓存服务
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionCacheServiceImpl {
|
||||
|
||||
// private final SysUserMapper sysUserMapper;
|
||||
//
|
||||
// @Cacheable(cacheNames = "sys:user:permissions", key = "#userId")
|
||||
// public List<UserPermissionsVO> getUserPermissions(Long userId) {
|
||||
// return sysUserMapper.selectUserPermissionsById(userId);
|
||||
// }
|
||||
//
|
||||
// @Cacheable(cacheNames = "sys:user:permissionCodes", key = "#userId")
|
||||
// public List<String> getUserPermissionCodes(Long userId) {
|
||||
// return sysUserMapper.selectUserPermissionsById(userId).stream()
|
||||
// .map(UserPermissionsVO::getPermissionCode)
|
||||
// .filter(permissionCode -> permissionCode != null)
|
||||
// .distinct()
|
||||
// .toList();
|
||||
// }
|
||||
//
|
||||
// @Cacheable(cacheNames = "sys:user:roles", key = "#userId")
|
||||
// public List<String> getUserRoles(Long userId) {
|
||||
// return sysUserMapper.selectUserPermissionsById(userId).stream()
|
||||
// .map(UserPermissionsVO::getRoleCode)
|
||||
// .filter(roleCode -> roleCode != null)
|
||||
// .distinct()
|
||||
// .toList();
|
||||
// }
|
||||
//
|
||||
// public boolean hasPermission(Long userId, String permissionCode) {
|
||||
// List<String> permissions = getUserPermissionCodes(userId);
|
||||
// return permissions != null && permissions.contains(permissionCode);
|
||||
// }
|
||||
//
|
||||
// @Cacheable(cacheNames = "sys:user:isAdmin", key = "#userId")
|
||||
// public boolean isAdmin(Long userId) {
|
||||
// return sysUserMapper.selectUserPermissionsById(userId).stream()
|
||||
// .anyMatch(p -> Boolean.TRUE.equals(p.getIsAdmin()));
|
||||
// }
|
||||
//
|
||||
// @Caching(evict = {
|
||||
// @CacheEvict(cacheNames = "sys:user:permissions", key = "#userId"),
|
||||
// @CacheEvict(cacheNames = "sys:user:permissionCodes", key = "#userId"),
|
||||
// @CacheEvict(cacheNames = "sys:user:roles", key = "#userId"),
|
||||
// @CacheEvict(cacheNames = "sys:user:isAdmin", key = "#userId")
|
||||
// })
|
||||
// public void evictUserCache(Long userId) {
|
||||
// log.info("已清除用户缓存,userId: {}", userId);
|
||||
// }
|
||||
//
|
||||
// @Caching(evict = {
|
||||
// @CacheEvict(cacheNames = "sys:user:permissions", allEntries = true),
|
||||
// @CacheEvict(cacheNames = "sys:user:permissionCodes", allEntries = true),
|
||||
// @CacheEvict(cacheNames = "sys:user:roles", allEntries = true),
|
||||
// @CacheEvict(cacheNames = "sys:user:isAdmin", allEntries = true)
|
||||
// })
|
||||
// public void evictAllCache() {
|
||||
// log.info("已清除所有权限缓存");
|
||||
// }
|
||||
}
|
||||
@@ -3,17 +3,28 @@ package com.leiyuwei.mhxy.system.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysPermissionMapper;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysRolePermissionMapper;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserMapper;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserRoleMapper;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysPermission;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysRole;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUser;
|
||||
import com.leiyuwei.mhxy.system.model.vo.PermissionTreeVO;
|
||||
import com.leiyuwei.mhxy.system.service.SysPermissionService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -27,26 +38,34 @@ import java.util.stream.Collectors;
|
||||
public class SysPermissionServiceImpl implements SysPermissionService {
|
||||
|
||||
private final SysPermissionMapper sysPermissionMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final SysUserRoleMapper sysUserRoleMapper;
|
||||
private final SysRolePermissionMapper sysRolePermissionMapper;
|
||||
|
||||
@Override
|
||||
public IPage<SysPermission> getPermissionList(Integer pageNum, Integer pageSize) {
|
||||
Page<SysPermission> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<SysPermission> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysPermission::getDeleted, 0)
|
||||
.orderByAsc(SysPermission::getParentId)
|
||||
wrapper.orderByAsc(SysPermission::getParentId)
|
||||
.orderByAsc(SysPermission::getSort);
|
||||
|
||||
return sysPermissionMapper.selectPage(page, wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysPermission> getPermissionListByRoleId(Long roleId) {
|
||||
if (roleId == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return sysPermissionMapper.selectPermissionsByRoleId(roleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(cacheNames = "sys:permission:tree", key = "'all'")
|
||||
public List<PermissionTreeVO> getPermissionTree() {
|
||||
// 查询所有启用的权限
|
||||
LambdaQueryWrapper<SysPermission> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysPermission::getDeleted, 0)
|
||||
.orderByAsc(SysPermission::getParentId)
|
||||
wrapper.orderByAsc(SysPermission::getParentId)
|
||||
.orderByAsc(SysPermission::getSort);
|
||||
|
||||
List<SysPermission> permissions = sysPermissionMapper.selectList(wrapper);
|
||||
|
||||
// 转换为树形结构
|
||||
@@ -54,46 +73,85 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
||||
}
|
||||
|
||||
@Override
|
||||
// @Cacheable(
|
||||
// cacheNames = "sys:permission:menus",
|
||||
// key = "T(org.springframework.security.core.context.SecurityContextHolder).getContext().getAuthentication().getName()"
|
||||
// )
|
||||
public List<PermissionTreeVO> getMenus() {
|
||||
// TODO: 实现从 SecurityContext 获取用户权限
|
||||
// 当前返回全部菜单(需要根据用户权限过滤)
|
||||
LambdaQueryWrapper<SysPermission> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysPermission::getDeleted, 0)
|
||||
.eq(SysPermission::getType, "MENU")
|
||||
.orderByAsc(SysPermission::getParentId)
|
||||
.orderByAsc(SysPermission::getSort);
|
||||
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
String username;
|
||||
if (principal instanceof UserDetails) {
|
||||
username = ((UserDetails) principal).getUsername();
|
||||
} else {
|
||||
username = principal.toString();
|
||||
}
|
||||
|
||||
List<SysPermission> permissions = sysPermissionMapper.selectList(wrapper);
|
||||
return buildTree(permissions);
|
||||
SysUser user = sysUserMapper.selectByUsername(username);
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
List<SysRole> roles = sysUserMapper.selectRolesByUserId(user.getId());
|
||||
if (roles == null || roles.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Long> roleIds = roles.stream()
|
||||
.map(SysRole::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<SysPermission> permissions = sysPermissionMapper.selectPermissionsByRoleIds(roleIds);
|
||||
if (permissions == null || permissions.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<SysPermission> menuPermissions = permissions.stream()
|
||||
.filter(p -> "MENU".equals(p.getType()) )
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return buildTree(menuPermissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Caching(evict = {
|
||||
@CacheEvict(cacheNames = "sys:permission:tree", key = "'all'"),
|
||||
@CacheEvict(cacheNames = "sys:permission:menus", allEntries = true),
|
||||
@CacheEvict(cacheNames = "sys:role:byId", allEntries = true)
|
||||
})
|
||||
public void createPermission(SysPermission permission) {
|
||||
// 设置默认值
|
||||
if (permission.getSort() == null) {
|
||||
permission.setSort(0);
|
||||
}
|
||||
|
||||
sysPermissionMapper.insert(permission);
|
||||
log.info("权限创建成功: permissionId={}, permissionName={}", permission.getId(), permission.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Caching(evict = {
|
||||
@CacheEvict(cacheNames = "sys:permission:tree", key = "'all'"),
|
||||
@CacheEvict(cacheNames = "sys:permission:menus", allEntries = true),
|
||||
@CacheEvict(cacheNames = "sys:role:byId", allEntries = true)
|
||||
})
|
||||
public void updatePermission(SysPermission permission) {
|
||||
// 检查权限是否存在
|
||||
SysPermission existPermission = sysPermissionMapper.selectById(permission.getId());
|
||||
if (existPermission == null) {
|
||||
throw new RuntimeException("权限不存在");
|
||||
}
|
||||
|
||||
sysPermissionMapper.updateById(permission);
|
||||
log.info("权限更新成功: permissionId={}, permissionName={}", permission.getId(), permission.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Caching(evict = {
|
||||
@CacheEvict(cacheNames = "sys:permission:tree", key = "'all'"),
|
||||
@CacheEvict(cacheNames = "sys:permission:menus", allEntries = true),
|
||||
@CacheEvict(cacheNames = "sys:role:byId", allEntries = true)
|
||||
})
|
||||
public void deletePermission(Long id) {
|
||||
// 检查权限是否存在
|
||||
SysPermission permission = sysPermissionMapper.selectById(id);
|
||||
@@ -105,15 +163,13 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
||||
LambdaQueryWrapper<SysPermission> childWrapper = new LambdaQueryWrapper<>();
|
||||
childWrapper.eq(SysPermission::getParentId, id);
|
||||
long childCount = sysPermissionMapper.selectCount(childWrapper);
|
||||
|
||||
if (childCount > 0) {
|
||||
throw new RuntimeException("该权限下存在子权限,无法删除");
|
||||
}
|
||||
|
||||
// 逻辑删除
|
||||
permission.setDeleted(1);
|
||||
// permission.setDeleted(1);
|
||||
sysPermissionMapper.updateById(permission);
|
||||
|
||||
log.info("权限删除成功: permissionId={}, permissionName={}", id, permission.getName());
|
||||
}
|
||||
|
||||
@@ -155,7 +211,6 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
||||
if (b.getSort() == null) return -1;
|
||||
return a.getSort().compareTo(b.getSort());
|
||||
});
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ public class SysRoleServiceImpl implements SysRoleService {
|
||||
private final SysRolePermissionMapper sysRolePermissionMapper;
|
||||
|
||||
@Override
|
||||
//@Cacheable(cacheNames = "sys:role:byId", key = "#roleId")
|
||||
public RoleVO getRoleById(Long roleId) {
|
||||
SysRole role = sysRoleMapper.selectById(roleId);
|
||||
if (role == null) {
|
||||
@@ -45,8 +46,7 @@ public class SysRoleServiceImpl implements SysRoleService {
|
||||
public IPage<RoleVO> getRoleList(Integer pageNum, Integer pageSize) {
|
||||
Page<SysRole> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysRole::getDeleted, 0)
|
||||
.orderByDesc(SysRole::getCreatedAt);
|
||||
wrapper.orderByDesc(SysRole::getCreatedAt);
|
||||
|
||||
IPage<SysRole> rolePage = sysRoleMapper.selectPage(page, wrapper);
|
||||
return rolePage.convert(this::convertToVO);
|
||||
@@ -54,12 +54,12 @@ public class SysRoleServiceImpl implements SysRoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
//@CacheEvict(cacheNames = "sys:permission:menus", allEntries = true)
|
||||
public void createRole(SysRole role) {
|
||||
// 检查角色编码是否已存在
|
||||
SysRole existRole = sysRoleMapper.selectOne(
|
||||
new LambdaQueryWrapper<SysRole>()
|
||||
.eq(SysRole::getCode, role.getCode())
|
||||
.eq(SysRole::getDeleted, 0)
|
||||
|
||||
);
|
||||
if (existRole != null) {
|
||||
throw new RuntimeException("角色编码已存在");
|
||||
@@ -71,19 +71,21 @@ public class SysRoleServiceImpl implements SysRoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
// @Caching(evict = {
|
||||
// @CacheEvict(cacheNames = "sys:role:byId", key = "#role.id"),
|
||||
// @CacheEvict(cacheNames = "sys:permission:menus", allEntries = true)
|
||||
// })
|
||||
public void updateRole(SysRole role) {
|
||||
// 检查角色是否存在
|
||||
SysRole existRole = sysRoleMapper.selectById(role.getId());
|
||||
if (existRole == null) {
|
||||
throw new RuntimeException("角色不存在");
|
||||
}
|
||||
|
||||
// 检查角色编码是否被其他角色使用
|
||||
SysRole duplicateRole = sysRoleMapper.selectOne(
|
||||
new LambdaQueryWrapper<SysRole>()
|
||||
.eq(SysRole::getCode, role.getCode())
|
||||
.ne(SysRole::getId, role.getId())
|
||||
.eq(SysRole::getDeleted, 0)
|
||||
|
||||
);
|
||||
if (duplicateRole != null) {
|
||||
throw new RuntimeException("角色编码已存在");
|
||||
@@ -95,17 +97,19 @@ public class SysRoleServiceImpl implements SysRoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
// @Caching(evict = {
|
||||
// @CacheEvict(cacheNames = "sys:role:byId", key = "#roleId"),
|
||||
// @CacheEvict(cacheNames = "sys:permission:menus", allEntries = true)
|
||||
// })
|
||||
public void deleteRole(Long roleId) {
|
||||
SysRole role = sysRoleMapper.selectById(roleId);
|
||||
if (role == null) {
|
||||
throw new RuntimeException("角色不存在");
|
||||
}
|
||||
|
||||
// 删除角色关联的权限
|
||||
sysRolePermissionMapper.deleteByRoleId(roleId);
|
||||
|
||||
// 逻辑删除角色
|
||||
role.setDeleted(1);
|
||||
// role.setDeleted(1);
|
||||
sysRoleMapper.updateById(role);
|
||||
|
||||
log.info("角色删除成功: roleId={}, roleName={}", roleId, role.getName());
|
||||
@@ -113,17 +117,18 @@ public class SysRoleServiceImpl implements SysRoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
// @Caching(evict = {
|
||||
// @CacheEvict(cacheNames = "sys:role:byId", key = "#roleId"),
|
||||
// @CacheEvict(cacheNames = "sys:permission:menus", allEntries = true)
|
||||
// })
|
||||
public void assignPermissions(Long roleId, List<Long> permissionIds) {
|
||||
// 检查角色是否存在
|
||||
SysRole role = sysRoleMapper.selectById(roleId);
|
||||
if (role == null) {
|
||||
throw new RuntimeException("角色不存在");
|
||||
}
|
||||
|
||||
// 清空角色现有权限
|
||||
sysRolePermissionMapper.deleteByRoleId(roleId);
|
||||
|
||||
// 批量插入新权限
|
||||
if (permissionIds != null && !permissionIds.isEmpty()) {
|
||||
for (Long permissionId : permissionIds) {
|
||||
SysRolePermission rp = new SysRolePermission();
|
||||
@@ -136,14 +141,10 @@ public class SysRoleServiceImpl implements SysRoleService {
|
||||
log.info("角色权限分配成功: roleId={}, permissionIds={}", roleId, permissionIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为 VO
|
||||
*/
|
||||
private RoleVO convertToVO(SysRole role) {
|
||||
RoleVO vo = new RoleVO();
|
||||
BeanUtils.copyProperties(role, vo);
|
||||
|
||||
// 查询角色的权限列表
|
||||
List<SysPermission> permissions = sysPermissionMapper.selectPermissionsByRoleId(role.getId());
|
||||
vo.setPermissions(permissions.stream()
|
||||
.map(p -> p.getCode())
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
package com.leiyuwei.mhxy.system.service.impl;
|
||||
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.leiyuwei.mhxy.common.exception.BusinessException;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysRoleMapper;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserMapper;
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserRoleMapper;
|
||||
import com.leiyuwei.mhxy.system.model.dto.UserRegisterDTO;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysRole;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUser;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUserRole;
|
||||
import com.leiyuwei.mhxy.system.model.vo.UserVO;
|
||||
import com.leiyuwei.mhxy.system.service.SysUserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统用户服务实现
|
||||
@@ -21,6 +31,8 @@ import org.springframework.stereotype.Service;
|
||||
public class SysUserServiceImpl implements SysUserService {
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final SysUserRoleMapper sysUserRoleMapper;
|
||||
private final SysRoleMapper sysRoleMapper;
|
||||
|
||||
@Override
|
||||
public UserVO getUserById(Long userId) {
|
||||
@@ -35,13 +47,120 @@ public class SysUserServiceImpl implements SysUserService {
|
||||
public IPage<UserVO> getUserList(Integer pageNum, Integer pageSize) {
|
||||
Page<SysUser> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysUser::getDeleted, 0)
|
||||
.orderByDesc(SysUser::getCreatedAt);
|
||||
wrapper.orderByDesc(SysUser::getCreatedAt);
|
||||
|
||||
|
||||
IPage<SysUser> userPage = sysUserMapper.selectPage(page, wrapper);
|
||||
return userPage.convert(this::convertToVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public UserVO createUser(UserRegisterDTO dto) {
|
||||
// 检查用户名是否已存在
|
||||
SysUser existUser = sysUserMapper.selectByUsername(dto.getUsername());
|
||||
if (existUser != null) {
|
||||
throw new BusinessException("用户名已存在");
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
String encryptedPassword = DigestUtil.md5Hex(dto.getPassword());
|
||||
|
||||
// 创建用户
|
||||
SysUser user = new SysUser();
|
||||
user.setUsername(dto.getUsername());
|
||||
user.setPassword(encryptedPassword);
|
||||
user.setNickname(dto.getUsername());
|
||||
user.setEnabled(true);
|
||||
|
||||
sysUserMapper.insert(user);
|
||||
|
||||
log.info("用户创建成功: userId={}, username={}", user.getId(), user.getUsername());
|
||||
return convertToVO(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateUser(Long id, UserRegisterDTO dto) {
|
||||
SysUser user = sysUserMapper.selectById(id);
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
user.setUsername(dto.getUsername());
|
||||
user.setNickname(dto.getUsername());
|
||||
sysUserMapper.updateById(user);
|
||||
|
||||
log.info("用户更新成功: userId={}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteUser(Long id) {
|
||||
SysUser user = sysUserMapper.selectById(id);
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
// 逻辑删除
|
||||
// user.setDeleted(1);
|
||||
sysUserMapper.updateById(user);
|
||||
|
||||
log.info("用户删除成功: userId={}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void resetPassword(Long id, String newPassword) {
|
||||
SysUser user = sysUserMapper.selectById(id);
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
user.setPassword(DigestUtil.md5Hex(newPassword));
|
||||
sysUserMapper.updateById(user);
|
||||
|
||||
log.info("密码重置成功: userId={}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void assignRoles(Long userId, List<Long> roleIds) {
|
||||
// 检查用户是否存在
|
||||
SysUser user = sysUserMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
// 清空用户现有角色
|
||||
sysUserRoleMapper.deleteByUserId(userId);
|
||||
|
||||
// 分配新角色
|
||||
if (roleIds != null && !roleIds.isEmpty()) {
|
||||
for (Long roleId : roleIds) {
|
||||
SysRole role = sysRoleMapper.selectById(roleId);
|
||||
if (role == null) {
|
||||
throw new BusinessException("角色不存在: " + roleId);
|
||||
}
|
||||
SysUserRole userRole = new SysUserRole();
|
||||
userRole.setUserId(userId);
|
||||
userRole.setRoleId(roleId);
|
||||
sysUserRoleMapper.insert(userRole);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("用户角色分配成功: userId={}, roleIds={}", userId, roleIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO getUserByUsername(String username) {
|
||||
SysUser user = sysUserMapper.selectByUsername(username);
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return convertToVO(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为 VO
|
||||
*/
|
||||
@@ -50,4 +169,4 @@ public class SysUserServiceImpl implements SysUserService {
|
||||
BeanUtils.copyProperties(user, vo);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.leiyuwei.mhxy.system.service.impl;
|
||||
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserMapper;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysUser;
|
||||
import com.leiyuwei.mhxy.system.model.vo.UserPermissionsVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Spring Security 用户详情服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
// 查询用户
|
||||
SysUser user = sysUserMapper.selectByUsername(username);
|
||||
if (user == null) {
|
||||
throw new UsernameNotFoundException("用户不存在: " + username);
|
||||
}
|
||||
|
||||
// 检查用户是否启用
|
||||
if (!user.getEnabled()) {
|
||||
throw new UsernameNotFoundException("用户已被禁用: " + username);
|
||||
}
|
||||
|
||||
// 查询用户权限信息
|
||||
List<UserPermissionsVO> userPermissions = sysUserMapper.selectUserPermissionsById(user.getId());
|
||||
|
||||
if (userPermissions.isEmpty()) {
|
||||
log.warn("用户 {} 未分配任何权限", username);
|
||||
// 返回空权限列表,认证会失败
|
||||
return User.builder()
|
||||
.username(username)
|
||||
.password(user.getPassword())
|
||||
.disabled(false)
|
||||
.accountExpired(false)
|
||||
.accountLocked(false)
|
||||
.credentialsExpired(false)
|
||||
.authorities(List.of())
|
||||
.build();
|
||||
}
|
||||
|
||||
List<String> authorities = userPermissions.stream()
|
||||
.flatMap(permissions -> Stream.of(permissions.getRoleCode(), permissions.getPermissionCode()))
|
||||
.filter(StringUtils::hasText)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 转换为 GrantedAuthority
|
||||
List<GrantedAuthority> grantedAuthorities = authorities.stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.debug("用户 {} 权限: {}", username, authorities);
|
||||
|
||||
return User.builder()
|
||||
.username(username)
|
||||
.password(user.getPassword())
|
||||
.disabled(false)
|
||||
.accountExpired(false)
|
||||
.accountLocked(false)
|
||||
.credentialsExpired(false)
|
||||
.authorities(grantedAuthorities)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.leiyuwei.mhxy.system.service.impl;
|
||||
|
||||
import com.leiyuwei.mhxy.system.mapper.SysUserMapper;
|
||||
import com.leiyuwei.mhxy.system.model.entity.SysRole;
|
||||
import com.leiyuwei.mhxy.system.model.vo.UserPermissionsVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户权限服务(使用 Caffeine 缓存)
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserPermissionServiceImpl {
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final PermissionCacheServiceImpl permissionCacheService;
|
||||
|
||||
/**
|
||||
* 获取用户完整权限信息(带缓存)
|
||||
*/
|
||||
public Map<String, Object> getUserPermissionInfo(Long userId) {
|
||||
log.debug("获取用户完整权限信息,userId: {}", userId);
|
||||
|
||||
List<UserPermissionsVO> permissions = sysUserMapper.selectUserPermissionsById(userId);
|
||||
|
||||
if (permissions.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
// 提取角色和权限
|
||||
List<String> roleCodes = permissions.stream()
|
||||
.map(UserPermissionsVO::getRoleCode)
|
||||
.filter(roleCode -> roleCode != null)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> permissionCodes = permissions.stream()
|
||||
.map(UserPermissionsVO::getPermissionCode)
|
||||
.filter(permissionCode -> permissionCode != null)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 判断是否为超级管理员
|
||||
boolean isAdmin = roleCodes.contains("ROLE_ADMIN") ||
|
||||
permissions.stream().anyMatch(p -> Boolean.TRUE.equals(p.getIsAdmin()));
|
||||
|
||||
return Map.of(
|
||||
"userId", userId,
|
||||
"roleCodes", roleCodes,
|
||||
"permissionCodes", permissionCodes,
|
||||
"isAdmin", isAdmin
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色列表
|
||||
*/
|
||||
public List<String> getUserRoles(Long userId) {
|
||||
List<String> roles = sysUserMapper.selectRolesByUserId(userId).stream()
|
||||
.map(SysRole::getCode).collect(Collectors.toList());
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户权限列表
|
||||
*/
|
||||
public List<String> getUserPermissions(Long userId) {
|
||||
List<String> permissionCodes = sysUserMapper.selectUserPermissionsById(userId).stream()
|
||||
.map(permission -> permission.getPermissionCode()).collect(Collectors.toList());
|
||||
return permissionCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有指定权限
|
||||
*/
|
||||
// public boolean hasPermission(Long userId, String permissionCode) {
|
||||
// return permissionCacheService.hasPermission(userId, permissionCode);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 检查用户是否有指定角色
|
||||
*/
|
||||
public boolean hasRole(Long userId, String roleCode) {
|
||||
List<String> roles = sysUserMapper.selectRolesByUserId(userId).stream()
|
||||
.map(SysRole::getCode).collect(Collectors.toList()) ;
|
||||
return roles.contains(roleCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否为超级管理员
|
||||
*/
|
||||
// public boolean isAdmin(Long userId) {
|
||||
// return permissionCacheService.isAdmin(userId);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 清除用户缓存(可在用户角色/权限变更后调用)
|
||||
*/
|
||||
// public void evictUserCache(Long userId) {
|
||||
// permissionCacheService.evictUserCache(userId);
|
||||
// }
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
|
||||
server:
|
||||
port: 18080
|
||||
|
||||
|
||||
spring:
|
||||
application:
|
||||
|
||||
name: mhxy-tools
|
||||
|
||||
# 数据库配置
|
||||
@@ -54,6 +60,20 @@ spring:
|
||||
# max-idle: 8
|
||||
# min-idle: 0
|
||||
|
||||
# Cache 配置
|
||||
|
||||
cache:
|
||||
type: caffeine
|
||||
caffeine:
|
||||
spec: maximumSize=10000,expireAfterWrite=30m
|
||||
# cache:
|
||||
# type: redis
|
||||
# redis:
|
||||
# time-to-live: 1800000 # 30分钟
|
||||
# cache-null-values: false
|
||||
# key-prefix: cache:
|
||||
# use-key-prefix: true
|
||||
|
||||
# 文件上传配置
|
||||
servlet:
|
||||
multipart:
|
||||
@@ -67,18 +87,25 @@ spring:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
default-property-inclusion: non_null
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
|
||||
# MyBatis Plus 配置
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
|
||||
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
page:
|
||||
max-limit: 1000
|
||||
global-config:
|
||||
field-strategy: 0
|
||||
db-config:
|
||||
id-type: auto
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
# logic-delete-field: isDeleted
|
||||
id-type: assign_id
|
||||
# logic-delete-field: deleted
|
||||
# logic-delete-value: 1
|
||||
# logic-not-delete-value: 0
|
||||
mapper-locations: classpath:mapper/**/*.xml
|
||||
|
||||
# Knife4j 配置
|
||||
@@ -108,8 +135,14 @@ jwt:
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.mhxy: debug
|
||||
root: info
|
||||
com.leiyuwei.mhxy.mapper: debug # mapper 包路径
|
||||
com.baomidou.mybatisplus: debug # MP 框架
|
||||
org.springframework.security: debug
|
||||
org.springframework.security.web.access.intercept: debug
|
||||
pattern:
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss} \u001B[32m[%thread]\u001B[0m \u001B[36m%-5level\u001B[0m \u001B[33m%logger{36}\u001B[0m - \u001B[0m%msg%n"
|
||||
console:
|
||||
enabled: true
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
Add missing columns to server table: enabled (boolean) and sort (int)
|
||||
*/
|
||||
|
||||
ALTER TABLE `server`
|
||||
ADD COLUMN `enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||||
ADD COLUMN `sort` int NOT NULL DEFAULT 0 COMMENT '排序顺序';
|
||||
19
src/main/resources/mapper/FileHashReferenceMapper.xml
Normal file
19
src/main/resources/mapper/FileHashReferenceMapper.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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.leiyuwei.mhxy.business.mapper.FileHashReferenceMapper">
|
||||
|
||||
<select id="selectByObjectName" resultType="com.leiyuwei.mhxy.game.model.entity.FileHashReference">
|
||||
SELECT id,
|
||||
created_at,
|
||||
updated_at,
|
||||
|
||||
file_hash,
|
||||
object_name,
|
||||
file_size,
|
||||
content_type,
|
||||
reference_count
|
||||
FROM file_hash_reference
|
||||
WHERE object_name = #{objectName}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
77
src/main/resources/mapper/ServerMapper.xml
Normal file
77
src/main/resources/mapper/ServerMapper.xml
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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.leiyuwei.mhxy.game.mapper.ServerMapper">
|
||||
|
||||
<!-- <select id="SELECT_ALL" resultType="com.leiyuwei.mhxy.game.model.entity.Server">-->
|
||||
<!-- SELECT id,-->
|
||||
<!-- server_name AS serverName,-->
|
||||
<!-- description,-->
|
||||
<!-- enabled,-->
|
||||
<!-- sort,-->
|
||||
<!-- created_at AS createdAt,-->
|
||||
<!-- updated_at AS updatedAt,-->
|
||||
<!-- deleted-->
|
||||
<!-- FROM server-->
|
||||
<!-- WHERE deleted = 0-->
|
||||
<!-- </select>-->
|
||||
|
||||
<!-- <select id="SELECT_PAGE" resultType="com.leiyuwei.mhxy.game.model.entity.Server">-->
|
||||
<!-- SELECT id,-->
|
||||
<!-- server_name AS serverName,-->
|
||||
<!-- description,-->
|
||||
<!-- enabled,-->
|
||||
<!-- sort,-->
|
||||
<!-- created_at AS createdAt,-->
|
||||
<!-- updated_at AS updatedAt,-->
|
||||
<!-- deleted-->
|
||||
<!-- FROM server-->
|
||||
<!-- WHERE deleted = 0-->
|
||||
<!-- <if test="serverName != null and serverName != ''">-->
|
||||
<!-- AND server_name LIKE CONCAT('%', #{serverName}, '%')-->
|
||||
<!-- </if>-->
|
||||
<!-- <if test="enabled != null">-->
|
||||
<!-- AND enabled = #{enabled}-->
|
||||
<!-- </if>-->
|
||||
<!-- ORDER BY id DESC-->
|
||||
<!-- LIMIT #{pageSize} OFFSET #{offset}-->
|
||||
<!-- </select>-->
|
||||
|
||||
<!-- <select id="SELECT_BY_ID" resultType="com.leiyuwei.mhxy.game.model.entity.Server">-->
|
||||
<!-- SELECT id,-->
|
||||
<!-- server_name AS serverName,-->
|
||||
<!-- description,-->
|
||||
<!-- enabled,-->
|
||||
<!-- sort,-->
|
||||
<!-- created_at AS createdAt,-->
|
||||
<!-- updated_at AS updatedAt,-->
|
||||
<!-- deleted-->
|
||||
<!-- FROM server-->
|
||||
<!-- WHERE id = #{id}-->
|
||||
<!-- AND deleted = 0-->
|
||||
<!-- </select>-->
|
||||
|
||||
<!-- <insert id="INSERT" useGeneratedKeys="true" keyProperty="id">-->
|
||||
<!-- INSERT INTO server (server_name, description, enabled, sort, deleted, created_at, updated_at)-->
|
||||
<!-- VALUES (#{serverName}, #{description}, #{enabled}, #{sort}, 0, NOW(), NOW())-->
|
||||
<!-- </insert>-->
|
||||
|
||||
<!-- <update id="UPDATE">-->
|
||||
<!-- UPDATE server-->
|
||||
<!-- SET server_name = #{serverName},-->
|
||||
<!-- description = #{description},-->
|
||||
<!-- enabled = #{enabled},-->
|
||||
<!-- sort = #{sort},-->
|
||||
<!-- updated_at = NOW()-->
|
||||
<!-- WHERE id = #{id}-->
|
||||
<!-- AND deleted = 0-->
|
||||
<!-- </update>-->
|
||||
|
||||
<!-- <update id="LOGICAL_DELETE">-->
|
||||
<!-- UPDATE server-->
|
||||
<!-- SET deleted = 1,-->
|
||||
<!-- updated_at = NOW()-->
|
||||
<!-- WHERE id = #{id}-->
|
||||
<!-- AND deleted = 0-->
|
||||
<!-- </update>-->
|
||||
|
||||
</mapper>
|
||||
@@ -16,7 +16,28 @@
|
||||
FROM sys_permission p
|
||||
INNER JOIN sys_role_permission rp ON p.id = rp.permission_id
|
||||
WHERE rp.role_id = #{roleId}
|
||||
AND p.deleted = 0
|
||||
|
||||
ORDER BY p.sort ASC
|
||||
</select>
|
||||
|
||||
<!-- 批量查询角色的权限列表 -->
|
||||
<select id="selectPermissionsByRoleIds" resultType="com.leiyuwei.mhxy.system.model.entity.SysPermission">
|
||||
SELECT DISTINCT
|
||||
p.id,
|
||||
p.code,
|
||||
p.name,
|
||||
p.type,
|
||||
p.resource,
|
||||
p.parent_id as parentId,
|
||||
p.icon,
|
||||
p.sort
|
||||
FROM sys_permission p
|
||||
INNER JOIN sys_role_permission rp ON p.id = rp.permission_id
|
||||
WHERE rp.role_id IN
|
||||
<foreach item="roleId" collection="#{roleIds}" open="(" separator="," close=")">
|
||||
#{roleId}
|
||||
</foreach>
|
||||
|
||||
ORDER BY p.sort ASC
|
||||
</select>
|
||||
|
||||
|
||||
40
src/main/resources/mapper/SysRoleMapper.xml
Normal file
40
src/main/resources/mapper/SysRoleMapper.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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.leiyuwei.mhxy.system.mapper.SysRoleMapper">
|
||||
|
||||
<select id="selectPermissionsByRoleId" resultType="com.leiyuwei.mhxy.system.model.entity.SysPermission">
|
||||
SELECT p.id,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
|
||||
p.parent_id,
|
||||
p.code,
|
||||
p.name,
|
||||
p.type,
|
||||
p.path,
|
||||
p.component,
|
||||
p.icon,
|
||||
p.sort,
|
||||
p.visible,
|
||||
p.enabled
|
||||
FROM sys_permission p
|
||||
INNER JOIN sys_role_permission rp ON p.id = rp.permission_id
|
||||
WHERE rp.role_id = #{roleId}
|
||||
</select>
|
||||
|
||||
<select id="selectByCode" resultType="com.leiyuwei.mhxy.system.model.entity.SysRole">
|
||||
SELECT id,
|
||||
created_at,
|
||||
updated_at,
|
||||
|
||||
code,
|
||||
name,
|
||||
description,
|
||||
sort,
|
||||
enabled
|
||||
FROM sys_role
|
||||
WHERE code = #{code}
|
||||
AND enabled = 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
7
src/main/resources/mapper/SysRolePermissionMapper.xml
Normal file
7
src/main/resources/mapper/SysRolePermissionMapper.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?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.leiyuwei.mhxy.system.mapper.SysRolePermissionMapper">
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user