first commit
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
package com.gameplatform.server;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.gameplatform.server.mapper")
|
||||
public class GamePlatformServerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GamePlatformServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gameplatform.server.controller;
|
||||
|
||||
import com.gameplatform.server.model.User;
|
||||
import com.gameplatform.server.service.UserService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/users")
|
||||
public class UserController {
|
||||
private final UserService userService;
|
||||
|
||||
public UserController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Mono<User> getById(@PathVariable Long id) {
|
||||
return userService.getById(id);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Flux<User> listAll() {
|
||||
return userService.listAll();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public Mono<User> create(@Valid @RequestBody User user) {
|
||||
return userService.create(user);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public Mono<Void> delete(@PathVariable Long id) {
|
||||
return userService.deleteById(id)
|
||||
.filter(Boolean::booleanValue)
|
||||
.then();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.gameplatform.server.controller.auth;
|
||||
|
||||
import com.gameplatform.server.model.dto.auth.LoginRequest;
|
||||
import com.gameplatform.server.model.dto.auth.LoginResponse;
|
||||
import com.gameplatform.server.security.JwtService;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
|
||||
private final com.gameplatform.server.service.auth.AuthService authService;
|
||||
private final JwtService jwtService;
|
||||
|
||||
public AuthController(com.gameplatform.server.service.auth.AuthService authService, JwtService jwtService) {
|
||||
this.authService = authService;
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public Mono<LoginResponse> login(@Valid @RequestBody LoginRequest req) {
|
||||
return authService.login(req);
|
||||
}
|
||||
|
||||
@GetMapping("/me")
|
||||
public Mono<Object> me(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) {
|
||||
String token = authorization != null && authorization.startsWith("Bearer ") ? authorization.substring(7) : authorization;
|
||||
return Mono.fromCallable(() -> jwtService.parse(token))
|
||||
.map(this::claimsToMe);
|
||||
}
|
||||
|
||||
private Object claimsToMe(Claims c) {
|
||||
return new java.util.LinkedHashMap<>() {{
|
||||
put("userType", c.get("userType"));
|
||||
put("userId", c.get("userId"));
|
||||
put("username", c.get("username"));
|
||||
put("role", c.get("role"));
|
||||
put("exp", c.getExpiration());
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.gameplatform.server.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Object handleBadRequest(IllegalArgumentException e) {
|
||||
return body(HttpStatus.BAD_REQUEST.value(), e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalStateException.class)
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public Object handleForbidden(IllegalStateException e) {
|
||||
return body(HttpStatus.FORBIDDEN.value(), e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Object handleOther(Exception e) {
|
||||
return body(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部错误");
|
||||
}
|
||||
|
||||
private Object body(int code, String message) {
|
||||
return new LinkedHashMap<>() {{
|
||||
put("code", code);
|
||||
put("message", message);
|
||||
put("timestamp", Instant.now().toString());
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
17
src/main/java/com/gameplatform/server/mapper/UserMapper.java
Normal file
17
src/main/java/com/gameplatform/server/mapper/UserMapper.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.gameplatform.server.mapper;
|
||||
|
||||
import com.gameplatform.server.model.User;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserMapper {
|
||||
User findById(@Param("id") Long id);
|
||||
|
||||
List<User> findAll();
|
||||
|
||||
int insert(User user);
|
||||
|
||||
int deleteById(@Param("id") Long id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gameplatform.server.mapper.admin;
|
||||
|
||||
import com.gameplatform.server.model.entity.admin.AdminUser;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface AdminUserMapper {
|
||||
AdminUser findByUsername(@Param("username") String username);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gameplatform.server.mapper.agent;
|
||||
|
||||
import com.gameplatform.server.model.entity.agent.Agent;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface AgentMapper {
|
||||
Agent findByLoginAccount(@Param("loginAccount") String loginAccount);
|
||||
}
|
||||
|
||||
51
src/main/java/com/gameplatform/server/model/User.java
Normal file
51
src/main/java/com/gameplatform/server/model/User.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.gameplatform.server.model;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class User {
|
||||
private Long id;
|
||||
|
||||
@NotBlank
|
||||
private String username;
|
||||
|
||||
@Email
|
||||
private String email;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.gameplatform.server.model.dto.auth;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class LoginRequest {
|
||||
// userType: admin | agent
|
||||
@NotBlank
|
||||
private String userType;
|
||||
@NotBlank
|
||||
private String username; // admin: username, agent: loginAccount
|
||||
@NotBlank
|
||||
private String password;
|
||||
|
||||
public String getUserType() { return userType; }
|
||||
public void setUserType(String userType) { this.userType = userType; }
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.gameplatform.server.model.dto.auth;
|
||||
|
||||
public class LoginResponse {
|
||||
private String tokenType = "Bearer";
|
||||
private String accessToken;
|
||||
private long expiresIn; // seconds
|
||||
private String userType; // admin | agent
|
||||
private Long userId;
|
||||
private String username;
|
||||
|
||||
public String getTokenType() { return tokenType; }
|
||||
public void setTokenType(String tokenType) { this.tokenType = tokenType; }
|
||||
public String getAccessToken() { return accessToken; }
|
||||
public void setAccessToken(String accessToken) { this.accessToken = accessToken; }
|
||||
public long getExpiresIn() { return expiresIn; }
|
||||
public void setExpiresIn(long expiresIn) { this.expiresIn = expiresIn; }
|
||||
public String getUserType() { return userType; }
|
||||
public void setUserType(String userType) { this.userType = userType; }
|
||||
public Long getUserId() { return userId; }
|
||||
public void setUserId(Long userId) { this.userId = userId; }
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.gameplatform.server.model.entity.admin;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class AdminUser {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String passwordHash;
|
||||
private String role; // SUPER / ADMIN
|
||||
private String status; // ENABLED / DISABLED
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
public String getPasswordHash() { return passwordHash; }
|
||||
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
|
||||
public String getRole() { return role; }
|
||||
public void setRole(String role) { this.role = role; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gameplatform.server.model.entity.agent;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class Agent {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String loginAccount;
|
||||
private String passwordHash;
|
||||
private String status; // ENABLED / DISABLED
|
||||
private Long pointsBalance;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getLoginAccount() { return loginAccount; }
|
||||
public void setLoginAccount(String loginAccount) { this.loginAccount = loginAccount; }
|
||||
public String getPasswordHash() { return passwordHash; }
|
||||
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Long getPointsBalance() { return pointsBalance; }
|
||||
public void setPointsBalance(Long pointsBalance) { this.pointsBalance = pointsBalance; }
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.gameplatform.server.security;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class JwtService {
|
||||
|
||||
private final SecretKey key;
|
||||
private final long accessTokenMinutes;
|
||||
|
||||
public JwtService(@Value("${security.jwt.secret}") String secret,
|
||||
@Value("${security.jwt.access-token-minutes:30}") long accessTokenMinutes) {
|
||||
// accept raw text secret; if base64 provided, still works with Decoders
|
||||
byte[] bytes = secret.length() < 32 ? (secret + "_pad_to_32_chars_secret_key_value").getBytes() : secret.getBytes();
|
||||
this.key = Keys.hmacShaKeyFor(bytes);
|
||||
this.accessTokenMinutes = accessTokenMinutes;
|
||||
}
|
||||
|
||||
public String generateToken(String subject, String userType, Long userId, String username, Map<String, Object> extra) {
|
||||
Instant now = Instant.now();
|
||||
var builder = Jwts.builder()
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(Date.from(now))
|
||||
.setExpiration(Date.from(now.plus(accessTokenMinutes, ChronoUnit.MINUTES)))
|
||||
.claim("userType", userType)
|
||||
.claim("userId", userId)
|
||||
.claim("username", username);
|
||||
if (extra != null) {
|
||||
extra.forEach(builder::claim);
|
||||
}
|
||||
return builder.signWith(key, SignatureAlgorithm.HS256).compact();
|
||||
}
|
||||
|
||||
public io.jsonwebtoken.Claims parse(String token) {
|
||||
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.gameplatform.server.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
return http
|
||||
.csrf(ServerHttpSecurity.CsrfSpec::disable)
|
||||
.cors(ServerHttpSecurity.CorsSpec::disable)
|
||||
.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
|
||||
.formLogin(ServerHttpSecurity.FormLoginSpec::disable)
|
||||
.authorizeExchange(ex -> ex
|
||||
.pathMatchers("/actuator/**").permitAll()
|
||||
.pathMatchers(HttpMethod.POST, "/api/auth/login").permitAll()
|
||||
.pathMatchers(HttpMethod.GET, "/api/auth/me").permitAll()
|
||||
.anyExchange().permitAll() // 其他接口后续再收紧
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gameplatform.server.service;
|
||||
|
||||
import com.gameplatform.server.mapper.UserMapper;
|
||||
import com.gameplatform.server.model.User;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
private final UserMapper userMapper;
|
||||
|
||||
public UserService(UserMapper userMapper) {
|
||||
this.userMapper = userMapper;
|
||||
}
|
||||
|
||||
public Mono<User> getById(Long id) {
|
||||
return Mono.fromCallable(() -> userMapper.findById(id))
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
public Flux<User> listAll() {
|
||||
return Mono.fromCallable(userMapper::findAll)
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.flatMapMany(Flux::fromIterable);
|
||||
}
|
||||
|
||||
public Mono<User> create(User user) {
|
||||
return Mono.fromCallable(() -> {
|
||||
userMapper.insert(user);
|
||||
return user;
|
||||
})
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
|
||||
public Mono<Boolean> deleteById(Long id) {
|
||||
return Mono.fromCallable(() -> userMapper.deleteById(id) > 0)
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.gameplatform.server.service.auth;
|
||||
|
||||
import com.gameplatform.server.mapper.admin.AdminUserMapper;
|
||||
import com.gameplatform.server.mapper.agent.AgentMapper;
|
||||
import com.gameplatform.server.model.dto.auth.LoginRequest;
|
||||
import com.gameplatform.server.model.dto.auth.LoginResponse;
|
||||
import com.gameplatform.server.model.entity.admin.AdminUser;
|
||||
import com.gameplatform.server.model.entity.agent.Agent;
|
||||
import com.gameplatform.server.security.JwtService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class AuthService {
|
||||
private final AdminUserMapper adminUserMapper;
|
||||
private final AgentMapper agentMapper;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtService jwtService;
|
||||
|
||||
public AuthService(AdminUserMapper adminUserMapper,
|
||||
AgentMapper agentMapper,
|
||||
PasswordEncoder passwordEncoder,
|
||||
JwtService jwtService) {
|
||||
this.adminUserMapper = adminUserMapper;
|
||||
this.agentMapper = agentMapper;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
|
||||
public Mono<LoginResponse> login(LoginRequest req) {
|
||||
String userType = req.getUserType().toLowerCase();
|
||||
if ("admin".equals(userType)) {
|
||||
return Mono.fromCallable(() -> adminUserMapper.findByUsername(req.getUsername()))
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.flatMap(admin -> validateAdminPassword(admin, req.getPassword()));
|
||||
} else if ("agent".equals(userType)) {
|
||||
return Mono.fromCallable(() -> agentMapper.findByLoginAccount(req.getUsername()))
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.flatMap(agent -> validateAgentPassword(agent, req.getPassword()));
|
||||
} else {
|
||||
return Mono.error(new IllegalArgumentException("unsupported userType: " + userType));
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<LoginResponse> validateAdminPassword(AdminUser admin, String rawPassword) {
|
||||
if (admin == null || admin.getPasswordHash() == null) {
|
||||
return Mono.error(new IllegalArgumentException("用户名或密码错误"));
|
||||
}
|
||||
boolean ok = passwordEncoder.matches(rawPassword, admin.getPasswordHash());
|
||||
if (!ok) return Mono.error(new IllegalArgumentException("用户名或密码错误"));
|
||||
if (!"ENABLED".equalsIgnoreCase(admin.getStatus())) {
|
||||
return Mono.error(new IllegalStateException("账户已禁用"));
|
||||
}
|
||||
String token = jwtService.generateToken(
|
||||
"admin:" + admin.getId(),
|
||||
"admin", admin.getId(), admin.getUsername(), Map.of("role", admin.getRole())
|
||||
);
|
||||
LoginResponse resp = new LoginResponse();
|
||||
resp.setAccessToken(token);
|
||||
resp.setUserType("admin");
|
||||
resp.setUserId(admin.getId());
|
||||
resp.setUsername(admin.getUsername());
|
||||
resp.setExpiresIn(60L * 30); // align with default 30min
|
||||
return Mono.just(resp);
|
||||
}
|
||||
|
||||
private Mono<LoginResponse> validateAgentPassword(Agent agent, String rawPassword) {
|
||||
if (agent == null || agent.getPasswordHash() == null) {
|
||||
return Mono.error(new IllegalArgumentException("用户名或密码错误"));
|
||||
}
|
||||
boolean ok = passwordEncoder.matches(rawPassword, agent.getPasswordHash());
|
||||
if (!ok) return Mono.error(new IllegalArgumentException("用户名或密码错误"));
|
||||
if (!"ENABLED".equalsIgnoreCase(agent.getStatus())) {
|
||||
return Mono.error(new IllegalStateException("账户已禁用"));
|
||||
}
|
||||
String token = jwtService.generateToken(
|
||||
"agent:" + agent.getId(),
|
||||
"agent", agent.getId(), agent.getLoginAccount(), Map.of("name", agent.getName())
|
||||
);
|
||||
LoginResponse resp = new LoginResponse();
|
||||
resp.setAccessToken(token);
|
||||
resp.setUserType("agent");
|
||||
resp.setUserId(agent.getId());
|
||||
resp.setUsername(agent.getLoginAccount());
|
||||
resp.setExpiresIn(60L * 30);
|
||||
return Mono.just(resp);
|
||||
}
|
||||
}
|
||||
|
||||
39
src/main/resources/application.yml
Normal file
39
src/main/resources/application.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
spring:
|
||||
application:
|
||||
name: gameplatform-server
|
||||
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/login_task_db?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: root
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
hikari:
|
||||
maximum-pool-size: 10
|
||||
minimum-idle: 2
|
||||
connection-timeout: 30000
|
||||
|
||||
mybatis:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
type-aliases-package: com.gameplatform.server.model
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
server:
|
||||
port: 18080
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
com.gameplatform.server: debug
|
||||
|
||||
security:
|
||||
jwt:
|
||||
secret: "change-this-secret-to-a-long-random-string-please"
|
||||
access-token-minutes: 30
|
||||
refresh-token-days: 7
|
||||
40
src/main/resources/mapper/UserMapper.xml
Normal file
40
src/main/resources/mapper/UserMapper.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.gameplatform.server.mapper.UserMapper">
|
||||
|
||||
<resultMap id="UserResultMap" type="com.gameplatform.server.model.User">
|
||||
<id property="id" column="id" />
|
||||
<result property="username" column="username" />
|
||||
<result property="email" column="email" />
|
||||
<result property="createdAt" column="created_at" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id, username, email, created_at
|
||||
</sql>
|
||||
|
||||
<select id="findById" parameterType="long" resultMap="UserResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM users
|
||||
WHERE id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="findAll" resultMap="UserResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM users
|
||||
ORDER BY id DESC
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="com.gameplatform.server.model.User" useGeneratedKeys="true" keyProperty="id">
|
||||
INSERT INTO users (username, email, created_at)
|
||||
VALUES (#{username}, #{email}, NOW())
|
||||
</insert>
|
||||
|
||||
<delete id="deleteById" parameterType="long">
|
||||
DELETE FROM users WHERE id = #{id}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
|
||||
21
src/main/resources/mapper/admin/AdminUserMapper.xml
Normal file
21
src/main/resources/mapper/admin/AdminUserMapper.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.gameplatform.server.mapper.admin.AdminUserMapper">
|
||||
<resultMap id="AdminUserMap" type="com.gameplatform.server.model.entity.admin.AdminUser">
|
||||
<id property="id" column="id" />
|
||||
<result property="username" column="username" />
|
||||
<result property="passwordHash" column="password_hash" />
|
||||
<result property="role" column="role" />
|
||||
<result property="status" column="status" />
|
||||
<result property="createdAt" column="created_at" />
|
||||
<result property="updatedAt" column="updated_at" />
|
||||
</resultMap>
|
||||
|
||||
<select id="findByUsername" parameterType="string" resultMap="AdminUserMap">
|
||||
SELECT id, username, password_hash, role, status, created_at, updated_at
|
||||
FROM admin_user
|
||||
WHERE username = #{username}
|
||||
LIMIT 1
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
22
src/main/resources/mapper/agent/AgentMapper.xml
Normal file
22
src/main/resources/mapper/agent/AgentMapper.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.gameplatform.server.mapper.agent.AgentMapper">
|
||||
<resultMap id="AgentMap" type="com.gameplatform.server.model.entity.agent.Agent">
|
||||
<id property="id" column="id" />
|
||||
<result property="name" column="name" />
|
||||
<result property="loginAccount" column="login_account" />
|
||||
<result property="passwordHash" column="password_hash" />
|
||||
<result property="status" column="status" />
|
||||
<result property="pointsBalance" column="points_balance" />
|
||||
<result property="createdAt" column="created_at" />
|
||||
<result property="updatedAt" column="updated_at" />
|
||||
</resultMap>
|
||||
|
||||
<select id="findByLoginAccount" parameterType="string" resultMap="AgentMap">
|
||||
SELECT id, name, login_account, password_hash, status, points_balance, created_at, updated_at
|
||||
FROM agent
|
||||
WHERE login_account = #{loginAccount}
|
||||
LIMIT 1
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
8
src/main/resources/schema.sql
Normal file
8
src/main/resources/schema.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- Initial database schema for game_platform
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
username VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(120) NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user