Compare commits

..

3 Commits

Author SHA1 Message Date
3eeb5bbd51 feat: 重构 2026-07-07 09:58:42 +08:00
a5fe28e536 feat: 重构游戏房间架构 2026-06-30 11:54:21 +08:00
0d61038e67 feat: 重构游戏房间架构 2026-06-30 11:27:14 +08:00
59 changed files with 147 additions and 292 deletions

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.sse;
package com.webgame.webgamebackend.adapter.sse;
import com.alibaba.fastjson2.JSON;
import com.webgame.webgamebackend.common.event.SseEvent;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.sse;
package com.webgame.webgamebackend.adapter.sse;
import com.webgame.webgamebackend.common.event.SseEvent;
import jakarta.annotation.PostConstruct;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.sse;
package com.webgame.webgamebackend.adapter.sse;
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
import com.webgame.webgamebackend.common.dto.ApiResponse;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.ws;
package com.webgame.webgamebackend.adapter.ws;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

View File

@@ -1,17 +1,17 @@
package com.webgame.webgamebackend.ws;
package com.webgame.webgamebackend.adapter.ws;
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.webgame.webgamebackend.entities.GameRoomEntity;
import com.webgame.webgamebackend.repository.GameRoomRepository;
import com.webgame.webgamebackend.service.RoomManager;
import com.webgame.webgamebackend.service.engine.GameEngine;
import com.webgame.webgamebackend.service.engine.GameEngineRegistry;
import com.webgame.webgamebackend.service.engine.RoomContext;
import com.webgame.webgamebackend.ws.dto.WsInboundMessage;
import com.webgame.webgamebackend.ws.dto.WsMessage;
import com.webgame.webgamebackend.ws.dto.WsOutboundMessage;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameRoomEntity;
import com.webgame.webgamebackend.infrastructure.persistence.repository.GameRoomRepository;
import com.webgame.webgamebackend.domain.room.RoomManager;
import com.webgame.webgamebackend.domain.engine.GameEngine;
import com.webgame.webgamebackend.domain.engine.GameEngineRegistry;
import com.webgame.webgamebackend.domain.engine.RoomContext;
import com.webgame.webgamebackend.adapter.ws.dto.WsInboundMessage;
import com.webgame.webgamebackend.adapter.ws.dto.WsMessage;
import com.webgame.webgamebackend.adapter.ws.dto.WsOutboundMessage;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -133,12 +133,12 @@ public class RoomWebSocketHandler extends TextWebSocketHandler {
return;
}
// 处理重连在注册会话之前避免广播到尚未完成握手的当前 session
roomManager.handleReconnect(roomId, userId);
// 注册会话
sessionManager.addSession(roomId, userId, session);
// 处理重连
roomManager.handleReconnect(roomId, userId);
log.info("[WS] 连接建立, roomId={}, userId={}, sessionId={}", roomId, userId, session.getId());
}

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.ws;
package com.webgame.webgamebackend.adapter.ws;
import com.alibaba.fastjson2.JSONObject;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.ws;
package com.webgame.webgamebackend.adapter.ws;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.ws.dto;
package com.webgame.webgamebackend.adapter.ws.dto;
/**
* 客户端服务端 WebSocket 消息类型

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.ws.dto;
package com.webgame.webgamebackend.adapter.ws.dto;
/**
* WebSocket 消息通用封装

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.ws.dto;
package com.webgame.webgamebackend.adapter.ws.dto;
/**
* 服务端客户端 WebSocket 消息类型

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.controller;
package com.webgame.webgamebackend.api;
import com.webgame.webgamebackend.common.dto.ApiResponse;
import com.webgame.webgamebackend.common.dto.account.LoginRequest;
@@ -6,7 +6,7 @@ import com.webgame.webgamebackend.common.dto.account.LoginResponse;
import com.webgame.webgamebackend.common.dto.account.LogoutRequest;
import com.webgame.webgamebackend.common.dto.account.RefreshTokenRequest;
import com.webgame.webgamebackend.common.dto.account.RegisterRequest;
import com.webgame.webgamebackend.service.AccountService;
import com.webgame.webgamebackend.domain.account.AccountService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;

View File

@@ -1,8 +1,8 @@
package com.webgame.webgamebackend.controller;
package com.webgame.webgamebackend.api;
import com.webgame.webgamebackend.common.dto.ApiResponse;
import com.webgame.webgamebackend.common.dto.game.GameListResponse;
import com.webgame.webgamebackend.service.GameService;
import com.webgame.webgamebackend.domain.game.GameService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

View File

@@ -1,9 +1,9 @@
package com.webgame.webgamebackend.controller;
package com.webgame.webgamebackend.api;
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
import com.webgame.webgamebackend.common.dto.ApiResponse;
import com.webgame.webgamebackend.common.dto.game.*;
import com.webgame.webgamebackend.service.RoomManager;
import com.webgame.webgamebackend.domain.room.RoomManager;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;

View File

@@ -1,11 +1,11 @@
package com.webgame.webgamebackend.controller;
package com.webgame.webgamebackend.api;
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
import com.webgame.webgamebackend.common.dto.ApiResponse;
import com.webgame.webgamebackend.common.dto.user.UpdateStatusRequest;
import com.webgame.webgamebackend.common.dto.user.UpdateUserInfoRequest;
import com.webgame.webgamebackend.common.dto.user.UserInfoResponse;
import com.webgame.webgamebackend.service.UserService;
import com.webgame.webgamebackend.domain.user.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;

View File

@@ -1,7 +1,7 @@
package com.webgame.webgamebackend.common.dto.game;
import com.alibaba.fastjson2.JSON;
import com.webgame.webgamebackend.entities.GameConfigEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameConfigEntity;
import java.util.List;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.common.dto.game;
import com.webgame.webgamebackend.entities.GameRoomEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameRoomEntity;
import java.util.List;
import java.util.Map;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.common.dto.user;
import com.webgame.webgamebackend.entities.UserEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UserEntity;
import java.time.LocalDate;
@@ -22,7 +22,7 @@ public record UserInfoResponse(
*/
public static UserInfoResponse from(UserEntity user) {
return new UserInfoResponse(
user.getId(),
user.getAccount().getId(),
user.getNickName(),
user.getAvatar(),
user.getSignature(),

View File

@@ -3,7 +3,7 @@ package com.webgame.webgamebackend.common.event;
import com.alibaba.fastjson2.JSON;
import com.webgame.webgamebackend.common.exception.BusinessException;
import com.webgame.webgamebackend.common.exception.ErrorCode;
import com.webgame.webgamebackend.sse.SseConnectionManager;
import com.webgame.webgamebackend.adapter.sse.SseConnectionManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.service.impl;
package com.webgame.webgamebackend.domain.account;
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
import com.webgame.webgamebackend.common.constants.UserConstants;
@@ -7,12 +7,12 @@ import com.webgame.webgamebackend.common.dto.account.LoginResponse;
import com.webgame.webgamebackend.common.dto.account.RegisterRequest;
import com.webgame.webgamebackend.common.exception.BusinessException;
import com.webgame.webgamebackend.common.exception.ErrorCode;
import com.webgame.webgamebackend.entities.AccountEntity;
import com.webgame.webgamebackend.entities.UserEntity;
import com.webgame.webgamebackend.repository.AccountRepository;
import com.webgame.webgamebackend.repository.UserRepository;
import com.webgame.webgamebackend.service.AccountService;
import com.webgame.webgamebackend.service.RefreshTokenService;
import com.webgame.webgamebackend.infrastructure.persistence.entity.AccountEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UserEntity;
import com.webgame.webgamebackend.infrastructure.persistence.repository.AccountRepository;
import com.webgame.webgamebackend.infrastructure.persistence.repository.UserRepository;
import com.webgame.webgamebackend.domain.account.AccountService;
import com.webgame.webgamebackend.domain.token.RefreshTokenService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -27,7 +27,7 @@ import java.util.concurrent.ThreadLocalRandom;
@Slf4j
@Service
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
public class AccountService {
private final AccountRepository accountRepository;
private final UserRepository userRepository;
@@ -35,7 +35,6 @@ public class AccountServiceImpl implements AccountService {
private final RefreshTokenService refreshTokenService;
private final AuthenticationProvider authProvider;
@Override
public LoginResponse login(LoginRequest request) {
AccountEntity account = accountRepository.findByUsername(request.username())
.orElseThrow(() -> new BusinessException(ErrorCode.BAD_CREDENTIALS));
@@ -50,7 +49,6 @@ public class AccountServiceImpl implements AccountService {
return new LoginResponse(accessToken, refreshToken);
}
@Override
@Transactional
public LoginResponse register(RegisterRequest request) {
// 检查用户名是否已存在
@@ -80,7 +78,6 @@ public class AccountServiceImpl implements AccountService {
return new LoginResponse(accessToken, refreshToken);
}
@Override
public LoginResponse refresh(String refreshToken) {
// 校验 refresh token有效则用它签发新的 access token
String userId = refreshTokenService.validate(refreshToken);
@@ -90,7 +87,6 @@ public class AccountServiceImpl implements AccountService {
return new LoginResponse(accessToken, refreshToken);
}
@Override
public void logout(String refreshToken) {
// 获取当前登录用户ID用于日志
String loginId = authProvider.getCurrentUserId();

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.service.engine;
package com.webgame.webgamebackend.domain.engine;
import com.fasterxml.jackson.databind.JsonNode;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.service.engine;
package com.webgame.webgamebackend.domain.engine;
/**
* 游戏引擎工厂接口

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.service.engine;
package com.webgame.webgamebackend.domain.engine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.service.engine;
package com.webgame.webgamebackend.domain.engine;
import lombok.Data;
import lombok.NoArgsConstructor;

View File

@@ -1,13 +1,13 @@
package com.webgame.webgamebackend.service.engine;
package com.webgame.webgamebackend.domain.engine;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.JsonNode;
import com.webgame.webgamebackend.common.exception.BusinessException;
import com.webgame.webgamebackend.common.exception.ErrorCode;
import com.webgame.webgamebackend.entities.GameMoveEntity;
import com.webgame.webgamebackend.repository.GameMoveRepository;
import com.webgame.webgamebackend.ws.dto.WsMessage;
import com.webgame.webgamebackend.ws.dto.WsOutboundMessage;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameMoveEntity;
import com.webgame.webgamebackend.infrastructure.persistence.repository.GameMoveRepository;
import com.webgame.webgamebackend.adapter.ws.dto.WsMessage;
import com.webgame.webgamebackend.adapter.ws.dto.WsOutboundMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.service.engine;
package com.webgame.webgamebackend.domain.engine;
import com.webgame.webgamebackend.ws.dto.WsMessage;
import com.webgame.webgamebackend.adapter.ws.dto.WsMessage;
import lombok.Getter;
import java.util.List;

View File

@@ -1,10 +1,10 @@
package com.webgame.webgamebackend.service.impl;
package com.webgame.webgamebackend.domain.game;
import com.webgame.webgamebackend.common.dto.game.GameItemResponse;
import com.webgame.webgamebackend.common.dto.game.GameListResponse;
import com.webgame.webgamebackend.entities.GameConfigEntity;
import com.webgame.webgamebackend.repository.GameConfigRepository;
import com.webgame.webgamebackend.service.GameService;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameConfigEntity;
import com.webgame.webgamebackend.infrastructure.persistence.repository.GameConfigRepository;
import com.webgame.webgamebackend.domain.game.GameService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -19,11 +19,10 @@ import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class GameServiceImpl implements GameService {
public class GameService {
private final GameConfigRepository gameConfigRepository;
@Override
public GameListResponse getGameList() {
List<GameConfigEntity> configs = gameConfigRepository.findByEnabledTrueOrderBySortOrderAsc();
List<GameItemResponse> list = configs.stream()

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.service;
package com.webgame.webgamebackend.domain.room;
import com.alibaba.fastjson2.JSON;
import com.webgame.webgamebackend.common.dto.game.*;
@@ -8,16 +8,17 @@ import com.webgame.webgamebackend.common.event.SseEventPublisher;
import com.webgame.webgamebackend.common.event.SseEventType;
import com.webgame.webgamebackend.common.exception.BusinessException;
import com.webgame.webgamebackend.common.exception.ErrorCode;
import com.webgame.webgamebackend.entities.*;
import com.webgame.webgamebackend.repository.*;
import com.webgame.webgamebackend.service.engine.*;
import com.webgame.webgamebackend.ws.RoomSessionManager;
import com.webgame.webgamebackend.ws.dto.WsMessage;
import com.webgame.webgamebackend.ws.dto.WsOutboundMessage;
import com.webgame.webgamebackend.infrastructure.persistence.entity.*;
import com.webgame.webgamebackend.infrastructure.persistence.repository.*;
import com.webgame.webgamebackend.domain.engine.*;
import com.webgame.webgamebackend.adapter.ws.RoomSessionManager;
import com.webgame.webgamebackend.adapter.ws.dto.WsMessage;
import com.webgame.webgamebackend.adapter.ws.dto.WsOutboundMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
@@ -95,6 +96,7 @@ public class RoomManager {
* @param creatorId 房主 accountId
* @return 创建结果
*/
@Transactional
public CreateRoomResponse createRoom(CreateRoomRequest request, String creatorId) {
// 校验游戏存在且启用
GameConfigEntity gameConfig = gameConfigRepository.findByGameId(request.gameId())
@@ -172,6 +174,7 @@ public class RoomManager {
* @param userId 用户 accountId
* @return 房间信息
*/
@Transactional
public RoomItemResponse joinRoom(JoinRoomRequest request, String userId) {
GameRoomEntity room = gameRoomRepository.findById(request.roomId())
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
@@ -236,6 +239,7 @@ public class RoomManager {
* @param roomId 房间 ID
* @param userId 用户 accountId
*/
@Transactional
public void leaveRoom(String roomId, String userId) {
GameRoomEntity room = gameRoomRepository.findById(roomId)
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
@@ -286,6 +290,7 @@ public class RoomManager {
* @param roomId 房间 ID
* @param ownerId 房主 accountId
*/
@Transactional
public void disbandRoom(String roomId, String ownerId) {
GameRoomEntity room = gameRoomRepository.findById(roomId)
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
@@ -324,6 +329,7 @@ public class RoomManager {
* @param ownerId 房主 accountId
* @param targetUserId 目标用户 accountId
*/
@Transactional
public void kickPlayer(String roomId, String ownerId, String targetUserId) {
GameRoomEntity room = gameRoomRepository.findById(roomId)
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
@@ -366,6 +372,7 @@ public class RoomManager {
* @param userId 用户 accountId
* @return 新的准备状态
*/
@Transactional
public boolean toggleReady(String roomId, String userId) {
GameRoomEntity room = gameRoomRepository.findById(roomId)
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
@@ -400,6 +407,7 @@ public class RoomManager {
* @param roomId 房间 ID
* @param userId 操作者 accountId必须是房主
*/
@Transactional
public void startGame(String roomId, String userId) {
GameRoomEntity room = gameRoomRepository.findById(roomId)
.orElseThrow(() -> new BusinessException(ErrorCode.ROOM_NOT_FOUND));
@@ -430,9 +438,10 @@ public class RoomManager {
room.setStatus("playing");
gameRoomRepository.save(room);
// 创建游戏记录
// 创建游戏记录resultType 初始为 pending游戏结束时由 onGameOver 更新
GameRecordEntity record = new GameRecordEntity()
.setRoomId(roomId)
.setResultType("pending")
.setStartedAt(LocalDateTime.now());
gameRecordRepository.save(record);
@@ -637,6 +646,7 @@ public class RoomManager {
* @param winnerId 胜者 accountIdnull 表示平局
* @param resultType 结果类型win/draw/resign
*/
@Transactional
public void onGameOver(String roomId, String winnerId, String resultType) {
GameRoomEntity room = gameRoomRepository.findById(roomId).orElse(null);
if (room == null) return;

View File

@@ -1,8 +1,8 @@
package com.webgame.webgamebackend.service.impl;
package com.webgame.webgamebackend.domain.token;
import com.webgame.webgamebackend.common.exception.BusinessException;
import com.webgame.webgamebackend.common.exception.ErrorCode;
import com.webgame.webgamebackend.service.RefreshTokenService;
import com.webgame.webgamebackend.domain.token.RefreshTokenService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@RequiredArgsConstructor
public class RefreshTokenServiceImpl implements RefreshTokenService {
public class RefreshTokenService {
/** Redis key 前缀 */
private static final String KEY_PREFIX = "xzg:refresh:";
@@ -43,7 +43,6 @@ public class RefreshTokenServiceImpl implements RefreshTokenService {
private final StringRedisTemplate stringRedisTemplate;
@Override
public String create(String userId) {
if (userId == null || userId.isBlank()) {
throw new BusinessException(ErrorCode.BAD_REQUEST, "用户ID不能为空");
@@ -64,7 +63,6 @@ public class RefreshTokenServiceImpl implements RefreshTokenService {
return token;
}
@Override
public String validate(String token) {
if (token == null || token.isBlank()) {
throw new BusinessException(ErrorCode.REFRESH_TOKEN_MISSING);
@@ -76,7 +74,6 @@ public class RefreshTokenServiceImpl implements RefreshTokenService {
return userId;
}
@Override
public String rotate(String oldToken) {
String userId = validate(oldToken);
// 先删反向映射再删正向映射
@@ -88,7 +85,6 @@ public class RefreshTokenServiceImpl implements RefreshTokenService {
return create(userId);
}
@Override
public void revoke(String token) {
if (token == null || token.isBlank()) {
return;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.service.impl;
package com.webgame.webgamebackend.domain.user;
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
import com.webgame.webgamebackend.common.dto.user.UpdateStatusRequest;
@@ -6,9 +6,9 @@ import com.webgame.webgamebackend.common.dto.user.UpdateUserInfoRequest;
import com.webgame.webgamebackend.common.dto.user.UserInfoResponse;
import com.webgame.webgamebackend.common.exception.BusinessException;
import com.webgame.webgamebackend.common.exception.ErrorCode;
import com.webgame.webgamebackend.entities.UserEntity;
import com.webgame.webgamebackend.repository.UserRepository;
import com.webgame.webgamebackend.service.UserService;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UserEntity;
import com.webgame.webgamebackend.infrastructure.persistence.repository.UserRepository;
import com.webgame.webgamebackend.domain.user.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -27,7 +27,7 @@ import java.util.Set;
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
public class UserService {
/** 头像存储目录(相对于项目工作目录) */
private static final String AVATAR_DIR = "uploads/avatars";
@@ -50,7 +50,6 @@ public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final AuthenticationProvider authProvider;
@Override
public UserInfoResponse getCurrentUserInfo() {
String accountId = authProvider.getCurrentUserId();
log.info("[用户服务] 获取当前登录用户信息, accountId: {}", accountId);
@@ -59,7 +58,6 @@ public class UserServiceImpl implements UserService {
return UserInfoResponse.from(user);
}
@Override
@Transactional
public UserInfoResponse updateUserInfo(String accountId, UpdateUserInfoRequest request) {
UserEntity user = userRepository.findByAccountId(accountId)
@@ -90,7 +88,6 @@ public class UserServiceImpl implements UserService {
return UserInfoResponse.from(user);
}
@Override
@Transactional
public void updateStatus(String accountId, UpdateStatusRequest request) {
UserEntity user = userRepository.findByAccountId(accountId)
@@ -101,7 +98,6 @@ public class UserServiceImpl implements UserService {
log.info("[用户服务] 更新在线状态, accountId={}, status={}", accountId, request.status());
}
@Override
@Transactional
public UserInfoResponse uploadAvatar(String accountId, MultipartFile file) {
// 校验文件类型
@@ -144,7 +140,6 @@ public class UserServiceImpl implements UserService {
}
}
@Override
@Transactional
public UserInfoResponse deleteAvatar(String accountId) {
UserEntity user = userRepository.findByAccountId(accountId)

View File

@@ -1,8 +1,8 @@
package com.webgame.webgamebackend.config;
package com.webgame.webgamebackend.infrastructure.config;
import com.webgame.webgamebackend.repository.GameMoveRepository;
import com.webgame.webgamebackend.service.engine.GameEngineRegistry;
import com.webgame.webgamebackend.service.engine.GomokuEngine;
import com.webgame.webgamebackend.infrastructure.persistence.repository.GameMoveRepository;
import com.webgame.webgamebackend.domain.engine.GameEngineRegistry;
import com.webgame.webgamebackend.domain.engine.GomokuEngine;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.common.config;
package com.webgame.webgamebackend.infrastructure.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.common.config;
package com.webgame.webgamebackend.infrastructure.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.common.config;
package com.webgame.webgamebackend.infrastructure.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.common.config;
package com.webgame.webgamebackend.infrastructure.config;
import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction;
import cn.dev33.satoken.router.SaHttpMethod;

View File

@@ -1,7 +1,7 @@
package com.webgame.webgamebackend.config;
package com.webgame.webgamebackend.infrastructure.config;
import com.webgame.webgamebackend.common.event.SseEventPublisher;
import com.webgame.webgamebackend.sse.RedisSseListener;
import com.webgame.webgamebackend.adapter.sse.RedisSseListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.common.config;
package com.webgame.webgamebackend.infrastructure.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.config;
package com.webgame.webgamebackend.infrastructure.config;
import com.webgame.webgamebackend.ws.RoomWebSocketHandler;
import com.webgame.webgamebackend.adapter.ws.RoomWebSocketHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.entities;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import com.webgame.webgamebackend.entities.base.UUIDBaseEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UUIDBaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.entities.base;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import jakarta.persistence.*;
import lombok.Getter;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.entities;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import com.webgame.webgamebackend.entities.base.UUIDBaseEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UUIDBaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.entities;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import com.webgame.webgamebackend.entities.base.UUIDBaseEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UUIDBaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.entities;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import com.webgame.webgamebackend.entities.base.UUIDBaseEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UUIDBaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@@ -34,8 +34,16 @@ public class GameRecordEntity extends UUIDBaseEntity {
@Column(name = "winner_id", length = 64)
private String winnerId;
/** 结果类型win一方获胜、draw平局、resign认输 */
@Column(name = "result_type", nullable = false, length = 16)
/**
* 结果类型
* <ul>
* <li>{@code pending} 对局进行中尚未产生结果游戏开始时设置</li>
* <li>{@code win} 一方获胜</li>
* <li>{@code draw} 平局</li>
* <li>{@code resign} 一方认输</li>
* </ul>
*/
@Column(name = "result_type", length = 16)
private String resultType;
/** 对局开始时间 */

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.entities;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import com.webgame.webgamebackend.entities.base.UUIDBaseEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UUIDBaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.entities;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import com.webgame.webgamebackend.entities.base.UUIDBaseEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UUIDBaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;

View File

@@ -1,4 +1,4 @@
package com.webgame.webgamebackend.entities.base;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import jakarta.persistence.*;
import lombok.Getter;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.entities;
package com.webgame.webgamebackend.infrastructure.persistence.entity;
import com.webgame.webgamebackend.entities.base.UUIDBaseEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UUIDBaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.repository;
package com.webgame.webgamebackend.infrastructure.persistence.repository;
import com.webgame.webgamebackend.entities.AccountEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.AccountEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.repository;
package com.webgame.webgamebackend.infrastructure.persistence.repository;
import com.webgame.webgamebackend.entities.GameConfigEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameConfigEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.repository;
package com.webgame.webgamebackend.infrastructure.persistence.repository;
import com.webgame.webgamebackend.entities.GameMoveEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameMoveEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.repository;
package com.webgame.webgamebackend.infrastructure.persistence.repository;
import com.webgame.webgamebackend.entities.GameRecordEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameRecordEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.repository;
package com.webgame.webgamebackend.infrastructure.persistence.repository;
import com.webgame.webgamebackend.entities.GameRoomEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.GameRoomEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.repository;
package com.webgame.webgamebackend.infrastructure.persistence.repository;
import com.webgame.webgamebackend.entities.RoomPlayerEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.RoomPlayerEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

View File

@@ -1,6 +1,6 @@
package com.webgame.webgamebackend.repository;
package com.webgame.webgamebackend.infrastructure.persistence.repository;
import com.webgame.webgamebackend.entities.UserEntity;
import com.webgame.webgamebackend.infrastructure.persistence.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

View File

@@ -1,42 +0,0 @@
package com.webgame.webgamebackend.service;
import com.webgame.webgamebackend.common.dto.account.LoginRequest;
import com.webgame.webgamebackend.common.dto.account.LoginResponse;
import com.webgame.webgamebackend.common.dto.account.RegisterRequest;
/**
* 账号服务接口
*/
public interface AccountService {
/**
* 登录
*
* @param request 登录请求
* @return 登录结果(含 token
*/
LoginResponse login(LoginRequest request);
/**
* 注册
*
* @param request 注册请求
* @return 注册结果(含双 token注册后自动登录
*/
LoginResponse register(RegisterRequest request);
/**
* 刷新令牌
*
* @param refreshToken 刷新令牌
* @return 新的双 tokenaccessToken + refreshToken
*/
LoginResponse refresh(String refreshToken);
/**
* 登出,使 access token 和 refresh token 失效
*
* @param refreshToken 刷新令牌
*/
void logout(String refreshToken);
}

View File

@@ -1,19 +0,0 @@
package com.webgame.webgamebackend.service;
import com.webgame.webgamebackend.common.dto.game.GameListResponse;
/**
* 游戏服务接口
*
* 管理游戏配置相关的查询。
* 房间管理已迁移到 {@link RoomManager}。
*/
public interface GameService {
/**
* 获取游戏列表
*
* @return 所有启用的游戏信息
*/
GameListResponse getGameList();
}

View File

@@ -1,40 +0,0 @@
package com.webgame.webgamebackend.service;
/**
* 刷新令牌服务接口,管理 refresh token 的生命周期
*/
public interface RefreshTokenService {
/**
* 为用户创建新的刷新令牌,存入 RedisTTL 30 天
*
* @param userId 用户ID
* @return 生成的 refresh token 字符串
*/
String create(String userId);
/**
* 校验刷新令牌是否有效
*
* @param token 刷新令牌
* @return 对应的用户ID
* @throws com.webgame.webgamebackend.common.exception.BusinessException 令牌无效时抛出
*/
String validate(String token);
/**
* 轮换刷新令牌:删除旧 token生成新 token防重放
*
* @param oldToken 旧的刷新令牌
* @return 新的刷新令牌
* @throws com.webgame.webgamebackend.common.exception.BusinessException 旧令牌无效时抛出
*/
String rotate(String oldToken);
/**
* 撤销刷新令牌(登出时调用)
*
* @param token 要撤销的刷新令牌
*/
void revoke(String token);
}

View File

@@ -1,57 +0,0 @@
package com.webgame.webgamebackend.service;
import com.webgame.webgamebackend.common.dto.user.UpdateStatusRequest;
import com.webgame.webgamebackend.common.dto.user.UpdateUserInfoRequest;
import com.webgame.webgamebackend.common.dto.user.UserInfoResponse;
import org.springframework.web.multipart.MultipartFile;
/**
* 用户服务接口
*/
public interface UserService {
/**
* 获取当前登录用户的资料
*
* @return 用户信息
*/
UserInfoResponse getCurrentUserInfo();
/**
* 更新当前登录用户的资料
*
* @param accountId 账号 ID
* @param request 更新请求(所有字段可选)
* @return 更新后的用户信息
*/
UserInfoResponse updateUserInfo(String accountId, UpdateUserInfoRequest request);
/**
* 更新当前登录用户的在线状态
*
* @param accountId 账号 ID
* @param request 状态更新请求
*/
void updateStatus(String accountId, UpdateStatusRequest request);
/**
* 上传并更新当前登录用户的头像
*
* 文件会被保存到 uploads/avatars/{accountId}.jpg并更新 DB 中的 avatar 字段。
*
* @param accountId 账号 ID
* @param file 头像文件(前端已裁剪为 200×200 JPEG
* @return 更新后的用户信息
*/
UserInfoResponse uploadAvatar(String accountId, MultipartFile file);
/**
* 删除当前登录用户的头像
*
* 删除磁盘文件,并将 DB 中 avatar 置为 null前端会回退显示默认头像
*
* @param accountId 账号 ID
* @return 更新后的用户信息
*/
UserInfoResponse deleteAvatar(String accountId);
}

View File

@@ -74,6 +74,15 @@ logging:
# #如果是true则用的是https而不是http,默认值是true
# secure: false
############## 游戏配置 ##############
game:
# 断线重连超时时间(秒)
disconnect-timeout-seconds: 60
# 聊天消息最大长度
chat-max-length: 500
# 五子棋棋盘大小
board-size: 15
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
# token 名称(同时也是 cookie 名称)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB