Compare commits

17 Commits
main ... dev

Author SHA1 Message Date
692f6d76c6 fix(dev): use correct storageclass
All checks were successful
dev middleware install / deploy (push) Successful in 9s
2026-03-09 10:37:55 +08:00
0b4a5b35be chore: change related images to fit new server system
All checks were successful
dev middleware install / deploy (push) Successful in 9s
2026-03-06 10:58:47 +08:00
564ef48d02 chore: change related images to fit new server system 2026-03-05 19:45:17 +08:00
merlin
ececf1f8ce fix: ci helm grammar false 2026-02-28 10:27:04 +08:00
merlin
b228ed3c9e fix: shut down bitnami image verify 2026-02-28 10:18:00 +08:00
merlin
df9c17f4e6 fix: ci, use correct deploy image 2026-02-27 18:02:17 +08:00
merlin
45caaa1e25 fix: ci 2026-02-27 18:00:28 +08:00
merlin
31ddd5c4fe feat: add group message dispatcher 2026-02-27 17:55:40 +08:00
merlin
d64de39541 feat: add testing env ci, deploy middleware for dev 2026-02-27 17:50:43 +08:00
merlin
94d5d8cabe fix: fixed bugs for runtime application 2025-12-16 18:28:26 +08:00
merlin
44133d3667 feat: introduce middleware rabbitmq and redis 2025-12-11 10:37:03 +08:00
merlin
a1a23bec7a feat: websocket basic frame built 2025-12-03 17:01:51 +08:00
merlin
b7f6d3477d feat: group related logic refactor 2025-12-03 15:01:35 +08:00
merlin
4597623cd9 feat: playroom related logic refactor 2025-12-02 18:52:48 +08:00
merlin
3fce5b2f01 feat: friends related logic refactor 2025-12-02 10:09:57 +08:00
merlin
0ea6e13064 feat: user related logic refactor 2025-11-27 17:42:31 +08:00
merlin
23cb31d4fe feat: register logic refactor 2025-11-17 18:02:58 +08:00
83 changed files with 3263 additions and 82 deletions

33
.gitea/workflows/dev.yaml Normal file
View File

@@ -0,0 +1,33 @@
name: dev middleware install
on:
push:
branches:
- dev
jobs:
deploy:
runs-on: gitea-runner-group-myplayer
container:
image: ${{ vars.HARBOR_URL }}/candlelight/action_deployer:v0.0.1
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: setup kubeconfig
env:
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
run: |
mkdir -p ~/.kube
echo "${KUBE_CONFIG}" > ~/.kube/config
- name: helm login
env:
HARBOR_USERNAME: ${{ secrets.HARBOR_ROBOT }}
HARBOR_PASSWORD: ${{ secrets.HARBOR_ROBOT_SECRET }}
HARBOR_URL: ${{ vars.HARBOR_URL }}
run: helm registry login ${HARBOR_URL} --username ${HARBOR_USERNAME} --password ${HARBOR_PASSWORD}
- name: deploy middleware in testing
env:
PROJECT_NAME: ${{ vars.PROJECT_NAME }}
run: |
helm upgrade --install ${PROJECT_NAME}-dev-rabbitmq oci://registry.merlin.xin/charts/bitnamicharts/rabbitmq --version 16.0.14 --namespace testing --values dev-rabbitmq.yaml
helm upgrade --install ${PROJECT_NAME}-dev-redis oci://registry.merlin.xin/charts/bitnamicharts/redis --version 24.1.3 --namespace testing --values dev-redis.yaml

20
dev-rabbitmq.yaml Normal file
View File

@@ -0,0 +1,20 @@
global:
security:
allowInsecureImages: true
storageClass: longhorn-hdd
persistence:
size: 1Gi
image:
registry: registry.merlin.xin
repository: mirrors/bitnamilegacy/rabbitmq
tag: 4.1.3-debian-12-r1
resources:
requests:
cpu: 0.1
memory: 128Mi
limits:
cpu: 0.2
memory: 256Mi

32
dev-redis.yaml Normal file
View File

@@ -0,0 +1,32 @@
global:
security:
allowInsecureImages: true
storageClass: longhorn-hdd
image:
registry: registry.merlin.xin
repository: mirrors/bitnamilegacy/redis
tag: 8.2.1-debian-12-r0
kubectl:
image:
registry: harbor.merlin.xin
repository: mirrors/bitnamilegacy/kubectl
tag: 1.33.4-debian-12-r0
architecture: standalone
replica:
replicaCount: 0
master:
count: 1
resources:
requests:
cpu: 0.1
memory: 128Mi
limits:
cpu: 0.2
memory: 256Mi
persistence:
size: 1Gi

2
mvnw.cmd vendored
View File

@@ -23,7 +23,7 @@
@REM @REM
@REM Optional ENV vars @REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution @REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven @REM MVNW_USERNAME/MVNW_PASSWORD - userinfo and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ---------------------------------------------------------------------------- @REM ----------------------------------------------------------------------------

65
pom.xml
View File

@@ -13,19 +13,6 @@
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>myplayer-backend</name> <name>myplayer-backend</name>
<description>myplayer-backend</description> <description>myplayer-backend</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>
@@ -51,9 +38,14 @@
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>spring-boot-starter-websocket</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.kafka</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-kafka</artifactId> <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -64,18 +56,13 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<optional>true</optional> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId> <artifactId>spring-security-test</artifactId>
@@ -105,11 +92,27 @@
<artifactId>mybatis-plus-jsqlparser</artifactId> <artifactId>mybatis-plus-jsqlparser</artifactId>
<version>3.5.11</version> <version>3.5.11</version>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.4</version>
</dependency>
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId> <artifactId>jjwt</artifactId>
<version>0.12.6</version> <version>0.12.6</version>
</dependency> </dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.14</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.45</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -122,29 +125,9 @@
</resource> </resource>
</resources> </resources>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>

View File

@@ -2,8 +2,11 @@ package xin.merlin.myplayerbackend;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import xin.merlin.myplayerbackend.config.security.JwtProperties;
@SpringBootApplication @SpringBootApplication
@EnableConfigurationProperties(JwtProperties.class)
public class MyplayerBackendApplication { public class MyplayerBackendApplication {
public static void main(String[] args) { public static void main(String[] args) {

View File

@@ -0,0 +1,23 @@
package xin.merlin.myplayerbackend.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
public static final String WS_MESSAGE_QUEUE = "ws.message";
public static final String WS_VIDEO_QUEUE = "ws.video";
@Bean
public Queue wsMessageQueue() {
return new Queue(WS_MESSAGE_QUEUE, true); // 持久化队列
}
@Bean
public Queue wsVideoQueue() {
return new Queue(WS_VIDEO_QUEUE, true);
}
}

View File

@@ -0,0 +1,24 @@
package xin.merlin.myplayerbackend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}

View File

@@ -0,0 +1,26 @@
package xin.merlin.myplayerbackend.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import xin.merlin.myplayerbackend.config.security.WebsocketInterceptor;
import xin.merlin.myplayerbackend.utils.websocket.CustomWebSocketHandler;
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebsocketConfig implements WebSocketConfigurer {
private final CustomWebSocketHandler handler;
private final WebsocketInterceptor interceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(handler, "/ws/online").addInterceptors(interceptor).setAllowedOrigins("*");
}
}

View File

@@ -0,0 +1,27 @@
package xin.merlin.myplayerbackend.config.security;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
String message = "认证失败";
if (exception instanceof InsufficientAuthenticationException) {
message = "未提供认证信息";
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(message);
}
}

View File

@@ -35,12 +35,12 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter {
try { try {
if (!jwtUtil.isTokenExpired(token)) { if (!jwtUtil.isTokenExpired(token)) {
System.out.println(token); // System.out.println("token expired: " + token);
String username = jwtUtil.getUAccount(token); String account = jwtUtil.getAccount(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { if (account != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList()); new UsernamePasswordAuthenticationToken(account, null, Collections.emptyList());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authenticationToken);
} }

View File

@@ -1,7 +1,6 @@
package xin.merlin.myplayerbackend.config.security; package xin.merlin.myplayerbackend.config.security;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -22,21 +21,24 @@ public class SecurityConfig {
private final JWTAuthenticationFilter jwtAuthenticationFilter; private final JWTAuthenticationFilter jwtAuthenticationFilter;
@Bean @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http,CustomAuthenticationEntryPoint entryPoint) throws Exception {
http http
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz .authorizeHttpRequests(authz -> authz
.requestMatchers( .requestMatchers(
"/login", "/error",
"/register", "/shadow/**",
"/health", "/health",
"/code/**", "/code/**",
"/blog/**" "/v3/api-docs/**",
"/account/mail/verify/**",
"/ws/**"
).permitAll() ).permitAll()
.anyRequest().authenticated() .anyRequest().authenticated()
) )
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(ex -> ex.authenticationEntryPoint(entryPoint));
return http.build(); return http.build();
} }
@@ -48,7 +50,7 @@ public class SecurityConfig {
@Override @Override
public void addCorsMappings(CorsRegistry registry) { public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") registry.addMapping("/**")
.allowedOriginPatterns("*") // 开发阶段允许所有来源 .allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*") .allowedHeaders("*")
.exposedHeaders("Authorization") .exposedHeaders("Authorization")

View File

@@ -0,0 +1,64 @@
package xin.merlin.myplayerbackend.config.security;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor
public class WebsocketInterceptor implements HandshakeInterceptor {
private final JwtUtil jwtUtil;
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
try {
// 获取 token
List<String> protocols = request.getHeaders().get("Sec-WebSocket-Protocol");
String token = null;
if (protocols == null){
log.info("no token!");
return false;
}
String tokenHeader = protocols.get(0);
// System.out.println(tokenHeader);
response.getHeaders().add("Sec-WebSocket-Protocol",tokenHeader);
token = tokenHeader.replace("token-", "");
if (jwtUtil.isTokenExpired(token)){
log.info("token expired");
return false;
}
Integer id = jwtUtil.getId(token);
String account = jwtUtil.getAccount(token);
attributes.put("id", id);
attributes.put("account", account);
if (request instanceof ServletServerHttpRequest servletRequest) {
String userName = servletRequest.getServletRequest().getParameter("name");
if (userName != null) {
attributes.put("name", userName); // 将u_name存储到Attributes中
}
}
return true;
}catch (Exception e){
log.error(e.getMessage());
return false;
}
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
}

View File

@@ -0,0 +1,57 @@
package xin.merlin.myplayerbackend.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import xin.merlin.myplayerbackend.entity.http.Code;
import xin.merlin.myplayerbackend.service.impl.AccountServiceImpl;
import xin.merlin.myplayerbackend.utils.result.Response;
/*
* 账户相关接口:
* 修改邮箱
* 修改密码
* */
@Slf4j
@RestController
@RequestMapping("/account")
@RequiredArgsConstructor
public class AccountController {
private final AccountServiceImpl accountService;
private final PasswordEncoder passwordEncoder;
@PostMapping("/change/email")
Response changeEmail(@RequestBody Code code, @RequestParam String email){
return accountService.changeEmail(code,email);
}
@PostMapping("/mail/verify/{encode}")
Response verifyEmail(@PathVariable String encode){
return accountService.verifyEmail(encode);
}
@PostMapping("/change/password")
Response changePassword(@RequestBody Code code){
return accountService.resetPassword(code);
}
// @PostMapping("/init")
// Response init(@RequestBody Account account) {
// try {
// if(account==null||account.getAccount()==null) return Response.success(ResultCode.ACCOUNT_INFO_LOST);
// account.setPassword(passwordEncoder.encode(account.getPassword()));
// accountService.init(account);
// return Response.success(ResultCode.SUCCESS);
// } catch (Exception e) {
// log.error(e.getMessage());
// return Response.fail(ResultCode.SERVER_ERROR);
// }
// }
}

View File

@@ -0,0 +1,80 @@
package xin.merlin.myplayerbackend.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import xin.merlin.myplayerbackend.entity.Audit;
import xin.merlin.myplayerbackend.service.UploadService;
import xin.merlin.myplayerbackend.service.impl.AuditServiceImpl;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/audit")
@RequiredArgsConstructor
public class AuditController {
private final AuditServiceImpl auditService;
private final UploadService uploadService;
private final JwtUtil jwtUtil;
@PostMapping("/upload/avatar")
@Transactional
Response uploadAvatar(@RequestParam("avatar") MultipartFile file,
@RequestParam("type") Integer type,
@RequestParam("applicant") Integer applicant,
@RequestParam("influence") Integer influence) throws IOException {
try {
String target = uploadService.uploadAvatar(file,type,influence);
Audit oldAudit = auditService.getOne(
Wrappers.<Audit>lambdaQuery().eq(Audit::getType, type)
.eq(Audit::getApplicant,applicant).eq(Audit::getInfluence,influence));
Audit audit = new Audit(type,applicant,influence,target);
if(oldAudit != null) {
audit = new Audit(oldAudit.getA_id(),type,applicant,influence,target);
}
auditService.saveOrUpdate(audit);
return Response.success(ResultCode.SUCCESS);
} catch (IOException e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
// 管理员api
@GetMapping("/get/all")
Response getAll(@RequestHeader("Authorization")String token,@RequestParam Integer size,@RequestParam Integer page) {
token = token.substring(7);
if(!jwtUtil.isAdmin(token)) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
return Response.success(ResultCode.SUCCESS,auditService.page(new Page<Audit>().setCurrent(page).setSize(size), Wrappers.<Audit>lambdaQuery().orderByAsc(Audit::getType)));
}
// TODO完善完基础类别的增删改查之后统一处理审核
@PostMapping("/update/status")
Response updateStatus(@RequestHeader("Authorization")String token,@RequestBody Audit audit) {
token = token.substring(7);
if(!jwtUtil.isAdmin(token)) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
auditService.passTypeOne(audit);
return null;
}
}

View File

@@ -0,0 +1,51 @@
package xin.merlin.myplayerbackend.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xin.merlin.myplayerbackend.entity.Account;
import xin.merlin.myplayerbackend.entity.http.Code;
import xin.merlin.myplayerbackend.service.CodeService;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
/*
* 验证码controller
* 包含发送和验证逻辑*/
@Slf4j
@RestController
@RequestMapping("/code")
@RequiredArgsConstructor
public class CodeController {
private final CodeService codeService;
//发送验证码
@PostMapping("/send")
Response send(@RequestBody Account account){
try {
return codeService.send(account);
} catch (Exception e) {
log.error(e.getMessage(), e);
return Response.fail(ResultCode.SERVER_ERROR);
}
}
//验证验证码是否存在、可用、正确
@PostMapping("/verify")
Response verify(@RequestBody Code code){
try {
return codeService.verify(code);
} catch (Exception e) {
log.error(e.getMessage(), e);
return Response.fail(ResultCode.SERVER_ERROR);
}
}
}

View File

@@ -0,0 +1,80 @@
package xin.merlin.myplayerbackend.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import xin.merlin.myplayerbackend.entity.Friends;
import xin.merlin.myplayerbackend.service.impl.FriendsServiceImpl;
import xin.merlin.myplayerbackend.service.impl.InvitingServiceImpl;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import java.util.List;
@Slf4j
@RequestMapping("/friend")
@RestController
@RequiredArgsConstructor
public class FriendController {
private final FriendsServiceImpl friendsService;
private final InvitingServiceImpl invitingService;
private final JwtUtil jwtUtil;
@GetMapping("/get")
Response getFriend(@RequestHeader("Authorization")String token, @RequestParam Integer size, @RequestParam Integer page) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
return Response.success(ResultCode.SUCCESS,friendsService.getFriends(id,size,page));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/delete")
Response deleteFriend(@RequestHeader("Authorization")String token, @RequestParam Integer f_id) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
friendsService.removeFriend(id,f_id);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/nickname")
Response nickname(@RequestHeader("Authorization")String token, @RequestBody Friends friends) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if (!id.equals(friends.getId())) return Response.success(ResultCode.SERVER_ERROR);
friendsService.saveOrUpdate(friends);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/status")
Response status(@RequestBody List<Integer> friends) {
try {
return Response.success(ResultCode.SUCCESS,friendsService.status(friends));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
}

View File

@@ -0,0 +1,174 @@
package xin.merlin.myplayerbackend.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import xin.merlin.myplayerbackend.entity.GroupInfo;
import xin.merlin.myplayerbackend.entity.Groups;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.service.impl.GroupServiceImpl;
import xin.merlin.myplayerbackend.service.impl.GroupsServiceImpl;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import static com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler.PrintlnLogErrorHandler.log;
@RestController
@RequestMapping("/group")
@RequiredArgsConstructor
public class GroupController {
private final GroupServiceImpl groupService;
private final GroupsServiceImpl groupsService;
private final JwtUtil jwtUtil;
private Boolean isAdmin(Integer id,Integer g_id){
return groupsService.groupIsAdmin(id,g_id)==0;
}
@PostMapping("/create")
Response createGroup(@RequestHeader("Authorization")String token, @RequestBody GroupInfo groupInfo){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
return Response.success(ResultCode.SUCCESS, groupService.createGroup(id,groupInfo));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@GetMapping("/get")
Response getGroup(@RequestHeader("Authorization")String token){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
return Response.success(ResultCode.SUCCESS,groupService.getGroups(id));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/search")
Response searchGroup(@RequestBody GroupInfo groupInfo){
// TODO视情况开放api参数currentPage和 pageSize
try {
Integer currentPage = 1;
Integer pageSize = 10;
return Response.success(ResultCode.SUCCESS,groupService.searchGroups(groupInfo,currentPage,pageSize));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
// 主动加入群聊
@GetMapping("/join/{g_id}")
Response joinGroup(@RequestHeader("Authorization")String token, @PathVariable Integer g_id){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!groupService.joinGroup(id,g_id)) return Response.success(ResultCode.GROUP_USER_EXISTED);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/join/{g_id}")
Response joinGroup(@PathVariable Integer g_id,@RequestBody UserInfo userInfo){
try {
if(!groupService.joinGroup(userInfo.getId(),g_id)) return Response.success(ResultCode.GROUP_USER_EXISTED);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@GetMapping("/leave/{g_id}")
Response leaveGroup(@RequestHeader("Authorization")String token, @PathVariable Integer g_id){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!groupService.leaveGroup(id,g_id)) return Response.success(ResultCode.GROUP_USER_NOT_EXISTED);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/leave/{g_id}")
Response leaveGroup(@RequestHeader("Authorization")String token,@PathVariable Integer g_id,@RequestBody UserInfo userInfo){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!isAdmin(id,g_id)) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
if(!groupService.leaveGroup(userInfo.getId(),g_id)) return Response.success(ResultCode.GROUP_USER_NOT_EXISTED);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/update")
Response updatePlayroom(@RequestHeader("Authorization")String token, @RequestBody GroupInfo groupInfo) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!isAdmin(id,groupInfo.getG_id())) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
groupService.updateById(groupInfo);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/delete")
Response deletePlayroom(@RequestHeader("Authorization")String token, @RequestBody GroupInfo groupInfo) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!isAdmin(id,groupInfo.getG_id())) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
return Response.success(ResultCode.SUCCESS,groupService.deletePlayroom(groupInfo));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@GetMapping("/member/{g_id}")
Response getMember(@PathVariable("g_id")Integer g_id,@RequestParam Integer currentPage,@RequestParam Integer pageSize){
try {
return Response.success(ResultCode.SUCCESS,groupService.getMember(g_id,currentPage,pageSize));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/member/delete/{g_id}")
Response deleteMember(@RequestHeader("Authorization")String token, @PathVariable("g_id")Integer g_id, @RequestBody UserInfo userInfo){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!isAdmin(id,g_id)) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
groupsService.remove(Wrappers.<Groups>lambdaQuery().eq(Groups::getG_id,g_id).eq(Groups::getId,userInfo.getId()));
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
}

View File

@@ -0,0 +1,17 @@
package xin.merlin.myplayerbackend.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
@RestController
public class HealthController {
@GetMapping("/health")
Response health() {
return Response.success(ResultCode.SUCCESS);
}
}

View File

@@ -0,0 +1,118 @@
package xin.merlin.myplayerbackend.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import xin.merlin.myplayerbackend.entity.Inviting;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.service.impl.InvitingServiceImpl;
import xin.merlin.myplayerbackend.service.impl.PlayroomsServiceImpl;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import java.util.Objects;
import static com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler.PrintlnLogErrorHandler.log;
@RequestMapping("/inviting")
@RestController
@RequiredArgsConstructor
public class InvitingController {
private final InvitingServiceImpl invitingService;
private final PlayroomsServiceImpl playroomsService;
private final JwtUtil jwtUtil;
// playroom鉴权
private Boolean isAdmin(Integer id,Integer r_id){
return playroomsService.playroomIsAdmin(id,r_id)==0;
}
@PostMapping("/friends")
Response requestFriend(@RequestHeader("Authorization")String token, @RequestBody UserInfo userInfo) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if (Objects.equals(id, userInfo.getId())) return Response.success(ResultCode.INVITING_ILLEGAL_REQUEST);
if(!invitingService.inviting(id,userInfo.getId(),null)) return Response.success(ResultCode.INVITING_RE_REQUEST);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@GetMapping("/get")
Response getFriend(@RequestHeader("Authorization")String token, @RequestParam Integer pageSize, @RequestParam Integer currentPage) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
return Response.success(ResultCode.SUCCESS, invitingService.getInvitingDetail(id, pageSize, currentPage));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/handle")
Response handleFriend(@RequestHeader("Authorization")String token, @RequestBody Inviting inviting) {
try {
if (Objects.equals(inviting.getInviter(), inviting.getTarget())) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!inviting.getTarget().equals(id)) return Response.success(ResultCode.INVITING_ILLEGAL_RESPONSE);
if(!invitingService.handleFriendInviting(inviting)) return Response.success(ResultCode.INVITING_ILLEGAL_REQUEST);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/playroom")
Response playRoom(@RequestBody Inviting inviting) {
try {
if(inviting.getRoom()==null || !inviting.getStatus().equals(0)) return Response.success(ResultCode.INVITING_REQUEST_ERROR);
if(!invitingService.inviting(inviting.getInviter(),inviting.getTarget(),inviting.getRoom())) return Response.success(ResultCode.INVITING_RE_REQUEST);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
// 管理员获取playroom
@GetMapping("/playroom/get/{r_id}")
Response playroomGet(@RequestHeader("Authorization")String token,@PathVariable("r_id") Integer r_id, @RequestParam Integer pageSize, @RequestParam Integer currentPage) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if (!isAdmin(id,r_id)) return Response.success(ResultCode.INVITING_AUTH_ERROR);
return Response.success(ResultCode.SUCCESS, invitingService.getSelfInvitingDetail(r_id, pageSize, currentPage));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
// 管理员对请求进行审批
@PostMapping("/playroom/handle/{r_id}")
Response playroomHandle(@RequestHeader("Authorization")String token,@PathVariable("r_id") Integer r_id, @RequestBody Inviting inviting) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if (!isAdmin(id,r_id)) return Response.success(ResultCode.INVITING_AUTH_ERROR);
if(!invitingService.handlePlayroomInviting(inviting)) return Response.success(ResultCode.INVITING_ILLEGAL_REQUEST);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
}

View File

@@ -0,0 +1,66 @@
package xin.merlin.myplayerbackend.controller;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xin.merlin.myplayerbackend.entity.Account;
import xin.merlin.myplayerbackend.entity.http.Code;
import xin.merlin.myplayerbackend.service.CodeService;
import xin.merlin.myplayerbackend.service.LoginService;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
/*
* 登录controller
* 包括:登录和注册逻辑
* */
@Slf4j
@RestController
@RequestMapping("/shadow")
@RequiredArgsConstructor
public class LoginController {
private final LoginService loginService;
private final CodeService codeService;
private final HttpServletRequest request;
// 账户密码登录逻辑
@PostMapping("/login")
Response login(@RequestBody Account account){
try {
return loginService.login(account);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
// 验证码登录逻辑
@PostMapping("/login/byCode")
Response login(@RequestBody Code code){
return null;
}
// 注册逻辑
@PostMapping("/register")
Response register(@RequestBody Code code){
if(code.getCode()==null|| code.getCode().isEmpty() || code.getC_id()==null || code.getC_id().isEmpty()) return Response.success(ResultCode.MAIL_VERIFY_NOT_EXIST);
try {
return loginService.register(code,request.getRemoteAddr());
} catch (Exception e) {
log.error(e.getMessage(), e);
return Response.fail(ResultCode.SERVER_ERROR);
}
}
}

View File

@@ -0,0 +1,124 @@
package xin.merlin.myplayerbackend.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import xin.merlin.myplayerbackend.entity.PlayroomInfo;
import xin.merlin.myplayerbackend.entity.Playrooms;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.service.impl.PlayroomServiceImpl;
import xin.merlin.myplayerbackend.service.impl.PlayroomsServiceImpl;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import static com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler.PrintlnLogErrorHandler.log;
@RestController
@RequestMapping("/playroom")
@RequiredArgsConstructor
public class PlayroomController {
private final JwtUtil jwtUtil;
private final PlayroomServiceImpl playroomService;
private final PlayroomsServiceImpl playroomsService;
private Boolean isAdmin(Integer id,Integer r_id){
return playroomsService.playroomIsAdmin(id,r_id)==0;
}
@PostMapping("/create")
Response createPlayroom(@RequestHeader("Authorization")String token, @RequestBody PlayroomInfo playroomInfo) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
return Response.success(ResultCode.SUCCESS, playroomService.createPlayroom(id,playroomInfo));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@GetMapping("/get")
Response getPlayroom(@RequestHeader("Authorization")String token){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
return Response.success(ResultCode.SUCCESS,playroomService.getPlayrooms(id));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/search")
Response searchPlayroom(@RequestBody PlayroomInfo playroomInfo) {
// TODO视情况开放api参数currentPage和 pageSize
try {
Integer currentPage = 1;
Integer pageSize = 10;
return Response.success(ResultCode.SUCCESS,playroomService.searchPlayroom(playroomInfo,currentPage,pageSize));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/update")
Response updatePlayroom(@RequestHeader("Authorization")String token, @RequestBody PlayroomInfo playroomInfo) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!isAdmin(id,playroomInfo.getR_id())) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
playroomService.updateById(playroomInfo);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/delete")
Response deletePlayroom(@RequestHeader("Authorization")String token, @RequestBody PlayroomInfo playroomInfo) {
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!isAdmin(id,playroomInfo.getR_id())) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
return Response.success(ResultCode.SUCCESS,playroomService.deletePlayroom(playroomInfo));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@GetMapping("/member/{r_id}")
Response getMember(@PathVariable("r_id")Integer r_id,@RequestParam Integer currentPage,@RequestParam Integer pageSize){
try {
return Response.success(ResultCode.SUCCESS,playroomService.getMember(r_id,currentPage,pageSize));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/member/delete/{r_id}")
Response deleteMember(@RequestHeader("Authorization")String token, @PathVariable("r_id")Integer r_id, @RequestBody UserInfo userInfo){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
if(!isAdmin(id,r_id)) return Response.success(ResultCode.ACCOUNT_PERMISSION_DENY);
playroomsService.remove(Wrappers.<Playrooms>lambdaQuery().eq(Playrooms::getR_id,r_id).eq(Playrooms::getId,userInfo.getId()));
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
}

View File

@@ -0,0 +1,76 @@
package xin.merlin.myplayerbackend.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.service.impl.UserServiceImpl;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import java.util.Map;
import java.util.Objects;
@Slf4j
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
private final UserServiceImpl userService;
private final JwtUtil jwtUtil;
Boolean consistencyTest(String token, Integer id) {
token = token.substring(7);
Integer i = jwtUtil.getId(token);
return Objects.equals(i, id);
}
@GetMapping("/info")
Response getInfo(@RequestHeader("Authorization")String token){
try {
token = token.substring(7);
Integer id = jwtUtil.getId(token);
return Response.success(ResultCode.SUCCESS,userService.getOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getId,id)));
} catch (Exception e) {
log.error(e.getMessage());
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/update")
Response updateInfo(@RequestHeader("Authorization")String token,@RequestBody UserInfo userinfo){
try {
if(!consistencyTest(token,userinfo.getId())) return Response.success(ResultCode.USER_ILLEGAL_REQUEST);
userService.updateById(userinfo);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
log.error(e.getMessage(),e);
return Response.fail(ResultCode.SERVER_ERROR);
}
}
@PostMapping("/search")
Response search(@RequestBody UserInfo userinfo){
// TODO视情况开放api参数currentPage和 pageSize
Integer currentPage = 1;
Integer pageSize = 10;
try {
if(userinfo.getU_id()!=null){
return Response.success(ResultCode.SUCCESS, Map.of("result",userService.searchByUID(userinfo.getU_id(),currentPage,pageSize)));
}
else
return Response.success(ResultCode.SUCCESS, Map.of("result",userService.searchByName(userinfo.getU_name(),currentPage,pageSize)));
} catch (Exception e) {
log.error(e.getMessage(),e);
return Response.fail(ResultCode.SERVER_ERROR);
}
}
}

View File

@@ -0,0 +1,29 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@TableName("account")
@NoArgsConstructor
public class Account {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String account;
private String password;
private String ip;
// 1 user; 0 admin
private Integer character;
public Account(String account, String password, String ip, Integer character) {
this.account = account;
this.password = password;
this.ip = ip;
this.character = character;
}
}

View File

@@ -0,0 +1,29 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@TableName("audit")
@AllArgsConstructor
@NoArgsConstructor
public class Audit {
@TableId("a_id")
private Integer a_id;
// 约定type == 0 已通过审核, 1 用户头像待审核, 2 群聊头像待审核, 3 playroom头像待审核 4 修改邮箱请求, 5 已经通过验证的邮箱修改请求, 6 已过期的审核请求
private Integer type;
private Integer applicant;
private Integer influence;
private String changed;
public Audit(Integer type,Integer applicant,Integer influence,String changed) {
this.type = type;
this.applicant = applicant;
this.influence = influence;
this.changed = changed;
}
}

View File

@@ -0,0 +1,17 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@TableName("friends")
@AllArgsConstructor
public class Friends {
private Integer id;
private Integer f_id;
private String nickname;
}

View File

@@ -0,0 +1,16 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("groupinfo")
public class GroupInfo {
@TableId("g_id")
private Integer g_id;
private String g_name;
private String g_introduction;
private String g_avatar;
}

View File

@@ -0,0 +1,16 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@TableName("groups")
@AllArgsConstructor
@NoArgsConstructor
public class Groups {
private Integer g_id;
private Integer id;
private Integer role;
}

View File

@@ -0,0 +1,22 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("inviting")
public class Inviting {
@TableId("i_id")
private Integer i_id;
private Integer inviter;
private Integer target;
// 0 表示未处理 1 表示同意 2 表示拒绝
private Integer status;
private Integer room;
private LocalDateTime time;
}

View File

@@ -0,0 +1,20 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("message")
public class Message {
@TableId("m_id")
private Integer m_id;
private Integer from;
private Integer to;
private String content;
private LocalDateTime time;
}

View File

@@ -0,0 +1,17 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("playroominfo")
public class PlayroomInfo {
@TableId(value = "r_id",type = IdType.AUTO)
private Integer r_id;
private String r_name;
private String r_introduction;
private String r_avatar;
}

View File

@@ -0,0 +1,16 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@TableName("playrooms")
@AllArgsConstructor
public class Playrooms {
private Integer r_id;
private Integer id;
private Integer role;
}

View File

@@ -0,0 +1,16 @@
package xin.merlin.myplayerbackend.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("userinfo")
public class UserInfo {
private String u_id;
@TableId("id")
private Integer id;
private String u_name;
private String u_introduction;
private String u_avatar;
}

View File

@@ -0,0 +1,11 @@
package xin.merlin.myplayerbackend.entity.http;
import lombok.Data;
@Data
public class Code {
private String account;
private String c_id;
private String code;
private String password;
}

View File

@@ -0,0 +1,24 @@
package xin.merlin.myplayerbackend.entity.http;
import lombok.Data;
import xin.merlin.myplayerbackend.entity.UserInfo;
@Data
public class Friend {
private String u_id;
private Integer id;
private String u_name;
private String u_introduction;
private String u_avatar;
private String nickname;
public Friend(UserInfo userInfo,String nickname) {
this.id = userInfo.getId();
this.u_id = userInfo.getU_id();
this.u_name = userInfo.getU_name();
this.u_introduction = userInfo.getU_introduction();
this.u_avatar = userInfo.getU_avatar();
this.nickname = nickname;
}
}

View File

@@ -0,0 +1,28 @@
package xin.merlin.myplayerbackend.entity.http;
import lombok.Data;
import lombok.NoArgsConstructor;
import xin.merlin.myplayerbackend.entity.GroupInfo;
import xin.merlin.myplayerbackend.entity.Groups;
@Data
@NoArgsConstructor
public class GroupDetails {
private Integer g_id;
private String g_name;
private String g_introduction;
private String g_avatar;
private Integer id;
private Integer role;
public GroupDetails(GroupInfo groupInfo, Groups groups) {
this.g_id = groupInfo.getG_id();
this.g_name = groupInfo.getG_name();
this.g_introduction = groupInfo.getG_introduction();
this.g_avatar = groupInfo.getG_avatar();
this.id = groups.getId();
this.role = groups.getRole();
}
}

View File

@@ -0,0 +1,35 @@
package xin.merlin.myplayerbackend.entity.http;
import lombok.Data;
import lombok.NoArgsConstructor;
import xin.merlin.myplayerbackend.entity.Inviting;
import xin.merlin.myplayerbackend.entity.UserInfo;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
public class InvitingDetails {
private Integer i_id;
private Integer inviter;
private String inviter_name;
private String inviter_avatar;
private Integer target;
// 0 表示未处理 1 表示已处理
private Integer status;
private Integer room;
private LocalDateTime time;
public InvitingDetails(Inviting inviting, UserInfo userInfo) {
this.i_id = inviting.getI_id();
this.inviter = inviting.getInviter();
this.target = inviting.getTarget();
this.status = inviting.getStatus();
this.room = inviting.getRoom();
this.inviter_name = userInfo.getU_name();
this.inviter_avatar = userInfo.getU_avatar();
this.time = inviting.getTime();
}
}

View File

@@ -0,0 +1,27 @@
package xin.merlin.myplayerbackend.entity.http;
import lombok.Data;
import lombok.NoArgsConstructor;
import xin.merlin.myplayerbackend.entity.PlayroomInfo;
import xin.merlin.myplayerbackend.entity.Playrooms;
@Data
@NoArgsConstructor
public class PlayroomDetails {
private Integer id;
private Integer r_id;
private String r_name;
private String r_introduction;
private String r_avatar;
private Integer role;
public PlayroomDetails(PlayroomInfo playroomInfo, Playrooms playrooms) {
this.id = playrooms.getId();
this.r_id = playrooms.getR_id();
this.role = playrooms.getRole();
this.r_name = playroomInfo.getR_name();
this.r_introduction = playroomInfo.getR_introduction();
this.r_avatar = playroomInfo.getR_avatar();
}
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.Account;
@Mapper
public interface AccountMapper extends BaseMapper<Account> {
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.Audit;
@Mapper
public interface AuditMapper extends BaseMapper<Audit> {
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.Friends;
@Mapper
public interface FriendsMapper extends BaseMapper<Friends> {
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.GroupInfo;
@Mapper
public interface GroupMapper extends BaseMapper<GroupInfo> {
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.Groups;
@Mapper
public interface GroupsMapper extends BaseMapper<Groups> {
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.Inviting;
@Mapper
public interface InvitingMapper extends BaseMapper<Inviting> {
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.Message;
@Mapper
public interface MessageMapper extends BaseMapper<Message> {
}

View File

@@ -0,0 +1,14 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import xin.merlin.myplayerbackend.entity.PlayroomInfo;
import xin.merlin.myplayerbackend.entity.http.PlayroomDetails;
import java.util.List;
@Mapper
public interface PlayroomMapper extends BaseMapper<PlayroomInfo> {
List<PlayroomDetails> selectPlayroomDetails(@Param("userId") Integer id);
}

View File

@@ -0,0 +1,12 @@
<select id="selectPlayroomDetails" resultType="xin.merlin.myplayerbackend.entity.http.PlayroomDetails">
SELECT
pr.id AS id,
pr.r_id AS r_id,
pr.role AS role,
pi.r_name AS r_name,
pi.r_introduction AS r_introduction,
pi.r_avatar AS r_avatar
FROM playrooms pr
LEFT JOIN playroominfo pi ON pr.r_id = pi.r_id
WHERE pr.id = #{userId}
</select>

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.Playrooms;
@Mapper
public interface PlayroomsMapper extends BaseMapper<Playrooms> {
}

View File

@@ -0,0 +1,9 @@
package xin.merlin.myplayerbackend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xin.merlin.myplayerbackend.entity.UserInfo;
@Mapper
public interface UserMapper extends BaseMapper<UserInfo> {
}

View File

@@ -0,0 +1,117 @@
package xin.merlin.myplayerbackend.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.merlin.myplayerbackend.entity.Account;
import xin.merlin.myplayerbackend.entity.http.Code;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@RequiredArgsConstructor
public class CodeService {
// 验证码字典
private static final Cache<String, String> waitingList = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
// 抛出以便登录时访问字典
public Cache<String, String> getWaitingList() {
return waitingList;
}
// 冷却缓存:限制邮箱请求频率
private static final Cache<String, Boolean> emailCooldown = Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS) // 冷却 60 秒
.build();
// 验证码验证次数
private static final Cache<String, Integer> codeFailCount = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
// // 验证因子队列
// private static final Cache<String, String> authList = Caffeine.newBuilder()
// .expireAfterWrite(5, TimeUnit.MINUTES)
// .build();
//
// // 验证因子队列对外可访问
// public Cache<String, String> getAuthList() {
// return authList;
// }
private final MailService mailService;
// 发送验证码
public Response send(Account account){
String a = account.getAccount();
if(a==null) return Response.success(ResultCode.MAIL_ACCOUNT_NOT_PROVIDED);
log.info("sending code to ----->{}", a);
// 检查是否在冷却中
if (emailCooldown.getIfPresent(a) != null) {
return Response.success(ResultCode.MAIL_REQUEST_TOO_FAST);
}
String tempId = UUID.randomUUID().toString()
.replace("-", "");
try {
// 加入验证码字典
waitingList.put(tempId, mailService.sendMail(a));
// 加入验证码冷却
emailCooldown.put(a, true);
return Response.success(ResultCode.SUCCESS, Map.of("c_id", tempId));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//验证验证码是否存在、可用、正确
public Response verify(Code code){
String c_id = code.getC_id();
String account = code.getAccount();
String c = code.getCode();
if(c_id==null||account==null||c==null) return Response.success(ResultCode.MAIL_INFO_LOST);
Integer count = codeFailCount.getIfPresent(c_id);
//设置并查看最大尝试次数
try {
if(count == null){
codeFailCount.put(c_id, 1);
}else if(count<=5){
codeFailCount.put(c_id, count+1);
}else {
codeFailCount.invalidate(c_id);
return Response.success(ResultCode.MAIL_VERIFY_FAIL_TOO_MANY);
}
//执行验证逻辑
String tempCode = waitingList.getIfPresent(c_id);
log.info("verifying...{},{}--?=--{}", account,code, tempCode);
if (tempCode == null) return Response.success(ResultCode.MAIL_VERIFY_NOT_EXIST);
if (!tempCode.equals(code.getCode())) return Response.success(ResultCode.MAIL_VERIFY_CODE_ERROR);
codeFailCount.invalidate(c_id);
emailCooldown.invalidate(account);
// waitingList.invalidate(c_id);
//// 生成认证因子
// String auth = UUID.randomUUID().toString();
// authList.put(auth, account);
return Response.success(ResultCode.SUCCESS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,85 @@
package xin.merlin.myplayerbackend.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
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 xin.merlin.myplayerbackend.entity.Account;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.entity.http.Code;
import xin.merlin.myplayerbackend.mapper.AccountMapper;
import xin.merlin.myplayerbackend.mapper.UserMapper;
import xin.merlin.myplayerbackend.utils.JwtUtil;
import xin.merlin.myplayerbackend.utils.RandomCode;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class LoginService{
private final AccountMapper accountMapper;
private final UserMapper userMapper;
private final CodeService codeService;
private final PasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;
// 账户密码登录服务逻辑
public Response login(Account account){
try {
Account ta = accountMapper.selectOne(Wrappers.<Account>lambdaQuery().eq(Account::getAccount,account.getAccount()));
if(ta == null) return Response.success(ResultCode.USER_NOT_FOUND);
if(!passwordEncoder.matches(account.getPassword(),ta.getPassword())){
return Response.success(ResultCode.ACCOUNT_PWD_ERROR);
}
String token = jwtUtil.generateToken(ta);
UserInfo userinfo = userMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getId,ta.getId()));
return Response.success(ResultCode.SUCCESS, Map.of("token",token,"token_type","Bearer","userinfo",userinfo));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 注册服务逻辑
@Transactional
public Response register(Code code, String ip){
try {
if(accountMapper.selectOne(Wrappers.<Account>lambdaQuery().eq(Account::getAccount,code.getAccount())) != null) return Response.success(ResultCode.ACCOUNT_EXIST);
Response response = codeService.verify(code);
if(response.getCode().equals("200")){
Account account = new Account(code.getAccount(),passwordEncoder.encode(code.getPassword()),ip,1);
accountMapper.insert(account);
account = accountMapper.selectOne(Wrappers.<Account>lambdaQuery().eq(Account::getAccount, account.getAccount()));
UserInfo u = new UserInfo();
u.setId(account.getId());
do{
u.setU_id(RandomCode.generateID());
}while (userMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getU_id,u.getU_id()))!=null);
userMapper.insert(u);
u = userMapper.selectById(u.getId());
codeService.getWaitingList().invalidate(code.getC_id());
return Response.success(ResultCode.SUCCESS,Map.of("token",jwtUtil.generateToken(account),"token_type","Bearer","userinfo",u));
}
return response;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,51 @@
package xin.merlin.myplayerbackend.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class MailService {
private final JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String mail;
public String sendMail(String receiver){
try {
String code = Double.toString(Math.random()).substring(2,8);
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(mail);
message.setTo(receiver);
message.setSubject("Welcome to use Merlin`s product");
message.setText("欢迎使用Merlin.xin产品 \n"+"您的验证码为:"+code+"\n有效期五分钟请勿泄露");
mailSender.send(message);
return code;
} catch (MailException e) {
log.error("e: ", e);
throw new RuntimeException(e);
}
}
public Boolean sendTextMail(String receiver, String text){
try {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(mail);
message.setTo(receiver);
message.setSubject("Welcome to use Merlin`s product");
message.setText("欢迎使用Merlin.xin产品 \n"+text);
mailSender.send(message);
return true;
} catch (MailException e) {
log.error("e: ", e);
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,45 @@
package xin.merlin.myplayerbackend.service;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
@RequiredArgsConstructor
public class OnlineStatusService {
private static final String ONLINE_USERS_KEY = "online:users";
private final StringRedisTemplate redis;
/** 用户上线 */
public void online(Integer uid) {
redis.opsForSet().add(ONLINE_USERS_KEY, uid.toString());
}
/** 用户下线 */
public void offline(Integer uid) {
redis.opsForSet().remove(ONLINE_USERS_KEY, uid.toString());
}
/** 是否在线 */
public boolean isOnline(Integer uid) {
return Boolean.TRUE.equals(redis.opsForSet().isMember(ONLINE_USERS_KEY, uid.toString()));
}
/** 获取所有在线用户 */
public Set<String> getOnlineUsers() {
return redis.opsForSet().members(ONLINE_USERS_KEY);
}
@PreDestroy
public void cleanup() {
// 清空在线状态
redis.delete(ONLINE_USERS_KEY);
}
}

View File

@@ -0,0 +1,86 @@
package xin.merlin.myplayerbackend.service;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
@Service
@RequiredArgsConstructor
public class UploadService {
@Value("${resources.user.avatar}")
private String userAvatarDir;
@Value("${resources.group.avatar}")
private String groupAvatarDir;
@Value("${resources.playroom.avatar}")
private String playroomAvatarDir;
@Value("${resources.public}")
private String publicAvatarDir;
private static final List<String> AVATAR_ALLOWED_EXTENSIONS = Arrays.asList(".jpg", ".jpeg", ".png", ".gif");
/**
* 上传用户、群组、放映室头像
* @param file 目标文件
* @param type 上传类型
* @param influence 受影响的id
* @return host后的访问url
* @throws IOException 抛出io异常
*/
public String uploadAvatar(MultipartFile file, Integer type,Integer influence) throws IOException {
String DIR = switch (type) {
case 1 -> userAvatarDir;
case 2 -> groupAvatarDir;
case 3 -> playroomAvatarDir;
default -> publicAvatarDir;
};
String fileName = getFilename(file, influence, DIR);
Path targetPath = Paths.get(DIR, fileName);
Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
return "/resources/user/avatar/" + fileName; // 返回访问 URL
}
/**
*
* @param file 目标文件
* @param influence 受影响的id
* @param DIR 储存的位置
* @return fileName 文件在储存之后的名字
* @throws IOException 抛出io异常
*/
private static String getFilename(MultipartFile file, Integer influence, String DIR) throws IOException {
File dir = new File(DIR);
if (!dir.exists()) dir.mkdirs();
// 取文件扩展名并检查是否合法
String originalFileName = file.getOriginalFilename();
String fileExtension = "";
if (originalFileName != null && originalFileName.contains(".")) {
fileExtension = originalFileName.substring(originalFileName.lastIndexOf(".")).toLowerCase();
}
if (!AVATAR_ALLOWED_EXTENSIONS.contains(fileExtension)) {
throw new IOException("仅支持 JPG, PNG, GIF 格式");
}
// 以id进行命名方便直接替换
return influence.toString() + fileExtension;
}
}

View File

@@ -0,0 +1,166 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.merlin.myplayerbackend.entity.Account;
import xin.merlin.myplayerbackend.entity.Audit;
import xin.merlin.myplayerbackend.entity.http.Code;
import xin.merlin.myplayerbackend.mapper.AccountMapper;
import xin.merlin.myplayerbackend.mapper.AuditMapper;
import xin.merlin.myplayerbackend.mapper.UserMapper;
import xin.merlin.myplayerbackend.service.CodeService;
import xin.merlin.myplayerbackend.service.MailService;
import xin.merlin.myplayerbackend.utils.AESUtil;
import xin.merlin.myplayerbackend.utils.result.Response;
import xin.merlin.myplayerbackend.utils.result.ResultCode;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
@RequiredArgsConstructor
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> {
private static final Cache<Integer, String> verifyURL = Caffeine.newBuilder()
.expireAfterWrite(24, TimeUnit.HOURS)
.build();
private final AccountMapper accountMapper;
private final AuditMapper auditMapper;
private final CodeService codeService;
private final MailService mailService;
private final AESUtil aesUtil;
private final PasswordEncoder passwordEncoder;
private Integer findByAccount(String account) {
Account accountEntity = accountMapper.selectOne(
Wrappers.<Account>lambdaQuery()
.select(Account::getId)
.eq(Account::getAccount, account)
);
return accountEntity == null ? null : accountEntity.getId();
}
/**
*
* @param code 验证码因子结构体
* @param email 新邮箱
* @return Response响应体
*/
@Transactional
public Response changeEmail(Code code, String email) {
try {
Response response = codeService.verify(code);
if (response.getCode().equals("200")){
Integer id = findByAccount(code.getAccount());
Audit audit = auditMapper.selectOne(Wrappers.<Audit>lambdaQuery().eq(Audit::getType,4).eq(Audit::getApplicant,id).eq(Audit::getInfluence,id));
if (audit != null){
audit.setType(6);
auditMapper.updateById(audit);
}
String url = aesUtil.encrypt(id+":"+email);
if (id != null) {
verifyURL.put(id, url);
}
else {
return Response.success(ResultCode.MAIL_INFO_LOST);
}
mailService.sendTextMail(email,"验证链接:\n"+"https://myplayer.merlin.xin/account/mail/verify/"+ url+"\n链接一天内有效请尽快验证");
auditMapper.insert(new Audit(4,id,id,email));
codeService.getWaitingList().invalidate(code.getC_id());
return Response.success(ResultCode.SUCCESS);
}
else{
return response;
}
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
/**
*
* @param encode 认证链接
* @return response响应体
*/
@Transactional
public Response verifyEmail(String encode) {
try {
Map<String, String> decode = aesUtil.decryptAndSplit(encode);
Integer id = Integer.parseInt(decode.get("id"));
String email = decode.get("email");
Audit audit = auditMapper.selectOne(Wrappers.<Audit>lambdaQuery().eq(Audit::getType,4).eq(Audit::getApplicant,id).eq(Audit::getInfluence,id));
if (audit == null) return Response.success(ResultCode.AUDIT_NO_RECORD);
if (!encode.equals(verifyURL.getIfPresent(id))){
audit.setType(6);
auditMapper.updateById(audit);
return Response.success(ResultCode.AUDIT_NO_RECORD);
}
if (!audit.getChanged().equals(email)) return Response.success(ResultCode.ACCOUNT_ILLEGAL_CHANGE);
Account account = accountMapper.selectById(id);
account.setAccount(email);
accountMapper.updateById(account);
audit.setType(5);
auditMapper.updateById(audit);
verifyURL.invalidate(id);
return Response.success(ResultCode.SUCCESS);
} catch (NumberFormatException e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
/**
*
* @param code 验证码认证因子
* @return response响应体
*/
@Transactional
public Response resetPassword(Code code){
try {
Response response = codeService.verify(code);
if (response.getCode().equals("200")){
Integer id = findByAccount(code.getAccount());
Account account = accountMapper.selectById(id);
account.setPassword(passwordEncoder.encode(code.getPassword()));
accountMapper.updateById(account);
codeService.getWaitingList().invalidate(code.getC_id());
return Response.success(ResultCode.SUCCESS);
}
return response;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
// @Transactional
// public void init(Account account) {
// try {
// account = accountMapper.selectOne(Wrappers.<Account>lambdaQuery().eq(Account::getAccount, account.getAccount()));
// if(accountMapper.updateById(account)==0) throw new RuntimeException(account.getAccount()+"初始化未成功");
// User u = new User();
// u.setId(account.getId());
// do{
// u.setU_id(RandomCode.generateID());
// }while (userMapper.selectById(u.getU_id())!=null);
// userMapper.insert(u);
// } catch (RuntimeException e) {
// throw new RuntimeException(e);
// }
// }
}

View File

@@ -0,0 +1,21 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import xin.merlin.myplayerbackend.entity.Audit;
import xin.merlin.myplayerbackend.mapper.AuditMapper;
import xin.merlin.myplayerbackend.mapper.UserMapper;
@Service
@RequiredArgsConstructor
public class AuditServiceImpl extends ServiceImpl<AuditMapper, Audit> {
private final AuditMapper auditMapper;
private final UserMapper userMapper;
public Boolean passTypeOne(Audit audit) {
return false;
}
}

View File

@@ -0,0 +1,69 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.merlin.myplayerbackend.entity.Friends;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.entity.http.Friend;
import xin.merlin.myplayerbackend.mapper.FriendsMapper;
import xin.merlin.myplayerbackend.mapper.UserMapper;
import xin.merlin.myplayerbackend.service.OnlineStatusService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class FriendsServiceImpl extends ServiceImpl<FriendsMapper, Friends> {
private final FriendsMapper friendsMapper;
private final UserMapper userMapper;
private final OnlineStatusService onlineStatusService;
public List<Friend> getFriends(Integer id, Integer size, Integer page) {
List<Friends> friends = friendsMapper.selectList(new Page<>(page,size),Wrappers.<Friends>lambdaQuery().eq(Friends::getId, id));
List<Friend> fs = new ArrayList<>();
for (Friends f : friends) {
fs.add(new Friend(userMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getId,f.getF_id())),f.getNickname()));
}
return fs;
}
@Transactional
public void removeFriend(Integer id, Integer f_id) {
try {
friendsMapper.delete(Wrappers.<Friends>lambdaQuery().eq(Friends::getF_id,f_id).eq(Friends::getId,id));
friendsMapper.delete(Wrappers.<Friends>lambdaQuery().eq(Friends::getF_id,id).eq(Friends::getId,f_id));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Map<Integer,Integer> status(List<Integer> friends){
try {
Map<Integer,Integer> map = new HashMap<>();
for (Integer f : friends) {
if(onlineStatusService.isOnline(f)){
map.put(f,1);
}else {
map.put(f,0);
}
}
return map;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,117 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.merlin.myplayerbackend.entity.*;
import xin.merlin.myplayerbackend.entity.http.GroupDetails;
import xin.merlin.myplayerbackend.mapper.GroupMapper;
import xin.merlin.myplayerbackend.mapper.GroupsMapper;
import xin.merlin.myplayerbackend.mapper.UserMapper;
import java.util.ArrayList;
import java.util.List;
@Service
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupInfo> {
private final UserMapper userMapper;
private final GroupMapper groupMapper;
private final GroupsMapper groupsMapper;
@Transactional
public GroupInfo createGroup(Integer id, GroupInfo groupInfo) {
try {
groupMapper.insert(groupInfo);
groupsMapper.insert(new Groups(groupInfo.getG_id(),id,0));
return groupInfo;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
// TODO: 优化点使用xml语句写关联查询
public List<GroupDetails> getGroups(Integer id) {
try {
List<Groups> groups = groupsMapper.selectList(Wrappers.<Groups>lambdaQuery().eq(Groups::getId,id));
// System.out.println(groups);
List<GroupDetails> groupDetails = new ArrayList<>();
for (Groups group : groups) {
groupDetails.add(new GroupDetails(groupMapper.selectById(group.getG_id()),group));
}
return groupDetails;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
public Page<GroupInfo> searchGroups(GroupInfo groupInfo, Integer currentPage, Integer pageSize) {
try {
return groupMapper.selectPage(new Page<>(currentPage,pageSize),Wrappers.<GroupInfo>lambdaQuery().like(GroupInfo::getG_name,groupInfo.getG_name()));
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
@Transactional
public Integer deletePlayroom(GroupInfo groupInfo) {
try {
groupsMapper.delete(Wrappers.<Groups>lambdaQuery().eq(Groups::getG_id,groupInfo.getG_id()));
return groupMapper.deleteById(groupInfo.getG_id());
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
public List<UserInfo> getMember(Integer g_id, Integer currentPage, Integer pageSize) {
try {
List<Groups> ids = groupsMapper.selectPage(new Page<>(currentPage,pageSize),Wrappers.<Groups>lambdaQuery().eq(Groups::getG_id,g_id)).getRecords();
List<UserInfo> members = new ArrayList<>();
for(Groups group:ids){
members.add(userMapper.selectById(group.getId()));
}
return members;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
@Transactional
public Boolean joinGroup(Integer id, Integer g_id) {
try {
if(groupsMapper.selectOne(Wrappers.<Groups>lambdaQuery().eq(Groups::getG_id,g_id).eq(Groups::getId,id))!=null) return false;
else {
groupsMapper.insert(new Groups(g_id,id,1));
return true;
}
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
public Boolean leaveGroup(Integer id, Integer g_id) {
try {
if(groupsMapper.selectOne(Wrappers.<Groups>lambdaQuery().eq(Groups::getG_id,g_id).eq(Groups::getId,id))==null) return false;
else {
groupsMapper.delete(Wrappers.<Groups>lambdaQuery().eq(Groups::getG_id,g_id).eq(Groups::getId,id));
return true;
}
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,26 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import xin.merlin.myplayerbackend.entity.Groups;
import xin.merlin.myplayerbackend.mapper.GroupsMapper;
@Service
@RequiredArgsConstructor
public class GroupsServiceImpl extends ServiceImpl<GroupsMapper, Groups> {
private final GroupsMapper groupsMapper;
public Integer groupIsAdmin(Integer id, Integer g_id) {
try {
Groups groups = groupsMapper.selectOne(Wrappers.<Groups>lambdaQuery().eq(Groups::getId,id).eq(Groups::getG_id,g_id));
if (groups == null) return 1;
else return groups.getRole();
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,126 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.merlin.myplayerbackend.entity.Friends;
import xin.merlin.myplayerbackend.entity.Inviting;
import xin.merlin.myplayerbackend.entity.Playrooms;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.entity.http.InvitingDetails;
import xin.merlin.myplayerbackend.mapper.FriendsMapper;
import xin.merlin.myplayerbackend.mapper.InvitingMapper;
import xin.merlin.myplayerbackend.mapper.PlayroomsMapper;
import xin.merlin.myplayerbackend.mapper.UserMapper;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class InvitingServiceImpl extends ServiceImpl<InvitingMapper, Inviting> {
private final InvitingMapper invitingMapper;
private final UserMapper userMapper;
private final FriendsMapper friendsMapper;
private final PlayroomsMapper playroomsMapper;
// TODO: 修改关联查询逻辑
private Map<String, Object> getInvitingDetails(List<Inviting> invitings) {
List<InvitingDetails> invitingDetails = new ArrayList<>();
for (Inviting inviting : invitings){
InvitingDetails invitingDetail = new InvitingDetails(inviting,
userMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getId,inviting.getInviter())));
invitingDetails.add(invitingDetail);
}
return Map.of("result",invitingDetails);
}
public Boolean inviting(Integer inviter, Integer target,Integer room){
Inviting inviting = new Inviting();
inviting.setInviter(inviter);
inviting.setTarget(target);
inviting.setStatus(0);
inviting.setTime(LocalDateTime.now());
if (room != null){
inviting.setRoom(room);
}
if (invitingMapper.selectCount(Wrappers.<Inviting>lambdaQuery().eq(Inviting::getInviter,inviter).eq(Inviting::getTarget,target).eq(Inviting::getStatus,0).isNull(Inviting::getRoom)) > 0) return false;
return invitingMapper.insert(inviting) == 1;
}
public Map<String,Object> getInvitingDetail(Integer target, Integer size, Integer page){
try {
List<Inviting> invitings = invitingMapper.selectList(new Page<>(page,size), Wrappers.<Inviting>lambdaQuery().eq(Inviting::getTarget,target));
return getInvitingDetails(invitings);
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
public Map<String,Object> getSelfInvitingDetail(Integer room, Integer size, Integer page){
try {
List<Inviting> invitings = invitingMapper.selectList(new Page<>(page,size), Wrappers.<Inviting>lambdaQuery().eq(Inviting::getRoom,room).apply("inviter = target"));
return getInvitingDetails(invitings);
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
@Transactional
public Boolean handleFriendInviting(Inviting inviting) {
try {
if (inviting.getStatus().equals(0)) return false;
else{
if(inviting.getStatus().equals(1)){
friendsMapper.insert(new Friends(inviting.getInviter(), inviting.getTarget(), null));
friendsMapper.insert(new Friends(inviting.getTarget(), inviting.getInviter(), null));
}
inviting.setTime(LocalDateTime.now());
invitingMapper.updateById(inviting);
return true;
}
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
// public Integer playroomIsAdmin(Integer id, Integer r_id) {
// try {
// Playrooms playrooms = playroomsMapper.selectOne(Wrappers.<Playrooms>lambdaQuery().eq(Playrooms::getId,id).eq(Playrooms::getR_id,r_id));
// if (playrooms == null) return 1;
// else return playrooms.getRole();
// } catch (Exception e) {
// log.error(e.getMessage());
// throw new RuntimeException(e);
// }
// }
public Boolean handlePlayroomInviting(Inviting inviting) {
try {
if (inviting.getStatus().equals(0) || inviting.getRoom() == null) return false;
else if (inviting.getStatus().equals(1)){
playroomsMapper.insert(new Playrooms(inviting.getRoom(), inviting.getTarget(), 1));
return true;
}
else return true;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,10 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import xin.merlin.myplayerbackend.entity.Message;
import xin.merlin.myplayerbackend.mapper.MessageMapper;
@Service
public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> {
}

View File

@@ -0,0 +1,92 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.merlin.myplayerbackend.entity.PlayroomInfo;
import xin.merlin.myplayerbackend.entity.Playrooms;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.entity.http.PlayroomDetails;
import xin.merlin.myplayerbackend.mapper.PlayroomMapper;
import xin.merlin.myplayerbackend.mapper.PlayroomsMapper;
import xin.merlin.myplayerbackend.mapper.UserMapper;
import java.util.ArrayList;
import java.util.List;
@Service
@RequiredArgsConstructor
public class PlayroomServiceImpl extends ServiceImpl<PlayroomMapper, PlayroomInfo> {
private final PlayroomMapper playroomMapper;
private final PlayroomsMapper playroomsMapper;
private final UserMapper userMapper;
@Transactional
public PlayroomInfo createPlayroom(Integer id, PlayroomInfo playroomInfo) {
try {
playroomMapper.insert(playroomInfo);
playroomsMapper.insert(new Playrooms(playroomInfo.getR_id(),id,0));
return playroomInfo;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
// TODO: 优化点使用xml语句写关联查询
public List<PlayroomDetails> getPlayrooms(Integer id) {
try {
List<Playrooms> playrooms = playroomsMapper.selectList(Wrappers.<Playrooms>lambdaQuery().eq(Playrooms::getId,id));
// System.out.println(playrooms);
List<PlayroomDetails> playroomDetails = new ArrayList<>();
for (Playrooms playroom : playrooms) {
playroomDetails.add(new PlayroomDetails(playroomMapper.selectById(playroom.getR_id()),playroom));
}
return playroomDetails;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
public Page<PlayroomInfo> searchPlayroom(PlayroomInfo playroomInfo, Integer currentPage, Integer pageSize) {
try {
return playroomMapper.selectPage(new Page<>(currentPage,pageSize),Wrappers.<PlayroomInfo>lambdaQuery().like(PlayroomInfo::getR_name,playroomInfo.getR_name()));
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
@Transactional
public Integer deletePlayroom(PlayroomInfo playroomInfo) {
try {
playroomsMapper.delete(Wrappers.<Playrooms>lambdaQuery().eq(Playrooms::getR_id,playroomInfo.getR_id()));
return playroomMapper.deleteById(playroomInfo.getR_id());
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
public List<UserInfo> getMember(Integer r_id, Integer currentPage, Integer pageSize) {
try {
List<Playrooms> ids = playroomsMapper.selectPage(new Page<>(currentPage,pageSize),Wrappers.<Playrooms>lambdaQuery().eq(Playrooms::getR_id,r_id)).getRecords();
List<UserInfo> members = new ArrayList<>();
for(Playrooms playroom:ids){
members.add(userMapper.selectById(playroom.getId()));
}
return members;
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,30 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.merlin.myplayerbackend.entity.PlayroomInfo;
import xin.merlin.myplayerbackend.entity.Playrooms;
import xin.merlin.myplayerbackend.mapper.PlayroomMapper;
import xin.merlin.myplayerbackend.mapper.PlayroomsMapper;
@Service
@RequiredArgsConstructor
public class PlayroomsServiceImpl extends ServiceImpl<PlayroomsMapper, Playrooms> {
private final PlayroomsMapper playroomsMapper;
public Integer playroomIsAdmin(Integer id, Integer r_id) {
try {
Playrooms playrooms = playroomsMapper.selectOne(Wrappers.<Playrooms>lambdaQuery().eq(Playrooms::getId,id).eq(Playrooms::getR_id,r_id));
if (playrooms == null) return 1;
else return playrooms.getRole();
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,36 @@
package xin.merlin.myplayerbackend.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import xin.merlin.myplayerbackend.entity.UserInfo;
import xin.merlin.myplayerbackend.mapper.UserMapper;
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserInfo> {
private final UserMapper userMapper;
public Object searchByUID(String u_id,Integer currentPage,Integer pageSize) {
try {
Page<UserInfo> page = new Page<>(currentPage, pageSize);
return userMapper.selectList(page,Wrappers.<UserInfo>lambdaQuery().like(UserInfo::getU_id, u_id));
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
public Object searchByName(String u_name,Integer currentPage,Integer pageSize) {
try {
Page<UserInfo> page = new Page<>(currentPage, pageSize);
return userMapper.selectList(page,Wrappers.<UserInfo>lambdaQuery().like(UserInfo::getU_name, u_name));
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,67 @@
package xin.merlin.myplayerbackend.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Component
public class AESUtil {
// 16 位密钥(必须 16/24/32 位)
private final String key;
private static final String ALGORITHM = "AES";
public AESUtil(@Value("${aes.key}") String key) {
this.key = key;
}
/** 加密 */
public String encrypt(String plainText) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("AES 加密失败", e);
}
}
/** 解密 */
public String decrypt(String cipherText) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decoded = Base64.getDecoder().decode(cipherText);
byte[] original = cipher.doFinal(decoded);
return new String(original, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("AES 解密失败", e);
}
}
public Map<String, String> decryptAndSplit(String cipherText) {
String plainText = this.decrypt(cipherText); // 先解密
String[] parts = plainText.split(":", 2); // 只分割一次,防止 email 里有 :
if (parts.length != 2) {
throw new IllegalArgumentException("解密数据格式不正确:" + plainText);
}
Map<String, String> result = new HashMap<>();
result.put("id", parts[0]);
result.put("email", parts[1]);
return result;
}
}

View File

@@ -3,9 +3,12 @@ package xin.merlin.myplayerbackend.utils;
import io.jsonwebtoken.*; import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.config.security.JwtProperties; import xin.merlin.myplayerbackend.config.security.JwtProperties;
import xin.merlin.myplayerbackend.entity.Account;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
@@ -13,6 +16,8 @@ import java.nio.charset.StandardCharsets;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
@Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class JwtUtil { public class JwtUtil {
@@ -35,17 +40,28 @@ public class JwtUtil {
} }
} }
@PreDestroy
public void destroy() {
log.info("JWT 组件正在关闭...");
// 如有线程池、定时任务、IO 连接,在这里 shutdown/close
// 本例仅把引用置空,帮助 GC可选
this.jwtParser = null;
this.key = null;
log.info("JWT 组件已关闭");
}
/** /**
* 生成 JWT Token * 生成 JWT Token
*/ */
public String generateToken(String uAccount, Integer uId) { public String generateToken(Account account) {
Date now = new Date(); Date now = new Date();
Date expireDate = new Date(now.getTime() + jwtProperties.getExpire() * 1000L); Date expireDate = new Date(now.getTime() + jwtProperties.getExpire() * 1000L);
return Jwts.builder() return Jwts.builder()
.subject(uAccount) .subject(account.getAccount())
.claim("id", uId) .claim("id", account.getId())
.claim("account", uAccount) .claim("account", account.getAccount())
.claim("character", account.getCharacter())
.id(UUID.randomUUID().toString()) .id(UUID.randomUUID().toString())
.issuedAt(now) .issuedAt(now)
.expiration(expireDate) .expiration(expireDate)
@@ -57,10 +73,10 @@ public class JwtUtil {
/** /**
* 解析 Token 获取 Claims * 解析 Token 获取 Claims
*/ */
public Claims getClaims(String token) { private Claims getClaims(String token) {
try { try {
Jws<Claims> jws = jwtParser.parseSignedClaims(token); Jws<Claims> jws = jwtParser.parseSignedClaims(token);
System.out.println(jws.getPayload()); // System.out.println(jws.getPayload());
return jws.getPayload(); return jws.getPayload();
} catch (ExpiredJwtException e) { } catch (ExpiredJwtException e) {
throw new TokenExpiredException("Token 已过期", e); throw new TokenExpiredException("Token 已过期", e);
@@ -82,7 +98,7 @@ public class JwtUtil {
/** /**
* 获取账号 * 获取账号
*/ */
public String getUAccount(String token) { public String getAccount(String token) {
Claims claims = getClaims(token); Claims claims = getClaims(token);
return claims.getSubject(); return claims.getSubject();
} }
@@ -90,9 +106,19 @@ public class JwtUtil {
/** /**
* 获取用户ID * 获取用户ID
*/ */
public String getUId(String token) { public Integer getId(String token) {
Claims claims = getClaims(token); Claims claims = getClaims(token);
return claims.get("id", String.class); return claims.get("id", Integer.class);
}
// public Integer getCharacter(String token) {
// Claims claims = getClaims(token);
// return claims.get("character", Integer.class);
// }
public Boolean isAdmin(String token) {
Claims claims = getClaims(token);
return claims.get("character", Integer.class)==0;
} }
// 自定义异常类 // 自定义异常类

View File

@@ -0,0 +1,20 @@
package xin.merlin.myplayerbackend.utils;
import java.util.Random;
public class RandomCode {
private static final Random rand = new Random();
public static String generateID(){
// 生成一个0到999999999的随机数然后格式化为9位数字字符串
return String.format("%09d", rand.nextInt(1000000000));
}
public static String generateCode(){
// 生成一个0到999999的随机数然后格式化为6位数字字符串
return String.format("%06d", rand.nextInt(1000000));
}
}

View File

@@ -0,0 +1,19 @@
package xin.merlin.myplayerbackend.utils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class SHA256Util {
public static String sha256(String s) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] bytes = md.digest(s.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : bytes) sb.append(String.format("%02x", b));
return sb.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,15 @@
package xin.merlin.myplayerbackend.utils;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Base64;
public class SecretKeyGenerator {
public static void main(String[] args) {
SecretKey key = Keys.secretKeyFor(io.jsonwebtoken.SignatureAlgorithm.HS256);
String base64Key = Base64.getEncoder().encodeToString(key.getEncoded());
System.out.println("Your secure Base64 key:");
System.out.println(base64Key);
}
}

View File

@@ -10,23 +10,23 @@ import java.time.LocalDateTime;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class Response<T> { public class Response<T> {
private int code; private String code;
private String message; private String message;
private T data; private T data;
// 请求成功 // 请求成功
public static <T> Response<T> success(ResultCode code , T data){ public static <T> Response<T> success(ResultCode code , T data){
System.out.println( LocalDateTime.now()+" success : \n"+code); System.out.println( LocalDateTime.now()+" success : "+code);
return new Response<T>(code.getCode(), code.getMessage(), data); return new Response<T>(code.getCode(), code.getMessage(), data);
} }
public static <T> Response<T> success(ResultCode code){ public static <T> Response<T> success(ResultCode code){
System.out.println( LocalDateTime.now()+" success : \n"+code); System.out.println( LocalDateTime.now()+" success : "+code);
return new Response<T>(code.getCode(), code.getMessage(),null); return new Response<T>(code.getCode(), code.getMessage(),null);
} }
// 请求失败 // 请求失败
public static <T> Response<T> fail(ResultCode code){ public static <T> Response<T> fail(ResultCode code){
System.out.println( LocalDateTime.now()+" fail : \n"+code); System.out.println( LocalDateTime.now()+" fail : "+code);
return new Response<T>(code.getCode(),code.getMessage(), null); return new Response<T>(code.getCode(),code.getMessage(), null);
} }

View File

@@ -8,29 +8,64 @@ import lombok.Getter;
@Getter @Getter
public enum ResultCode { public enum ResultCode {
SUCCESS(200, "成功"), SUCCESS("200", "成功"),
BAD_REQUEST(400, "请求参数错误"), BAD_REQUEST("400", "请求参数错误"),
UNAUTHORIZED(401, "未认证或登录已过期"), UNAUTHORIZED("401", "未认证或登录已过期"),
FORBIDDEN(403, "无权限访问"), FORBIDDEN("403", "无权限访问"),
NOT_FOUND(404, "资源不存在"), NOT_FOUND("404", "资源不存在"),
SERVER_ERROR(500, "服务器内部错误"), SERVER_ERROR("500", "服务器内部错误"),
// 自定义业务错误码 // 自定义业务错误码
USER_BANNED(1000,"用户被封禁"),
USER_NOT_FOUND(1001, "用户不存在"), //账户相关
USER_EXIST(1003,"户已存在"), ACCOUNT_EXIST("3001","户已存在"),
USER_PASSWORD_ERROR(1004,"用户密码错误"), ACCOUNT_NOT_INIT("3002","账户未初始化"),
USER_VERIFICATION_ERROR(1005,"验证码不存在或错误"), ACCOUNT_PWD_ERROR("3003","账户密码错误"),
USER_SEND_TOO_FAST(1006,"用户请求过快"), ACCOUNT_INFO_LOST("3004","账户信息丢失"),
USER_SEND_TOO_OFTEN(1007,"请求次数过多,已被限制"), ACCOUNT_ILLEGAL_CHANGE("3005","账户非法篡改"),
ORDER_NOT_FOUND(2000, "订单不存在"); ACCOUNT_PERMISSION_DENY("3006","用户权限不足"),
//用户相关
USER_BANNED("4000","用户被封禁"),
USER_NOT_FOUND("4001", "用户不存在"),
USER_EXIST("4003","用户已存在"),
USER_PASSWORD_ERROR("4004","用户密码错误"),
USER_VERIFICATION_ERROR("4005","验证码不存在或错误"),
USER_SEND_TOO_FAST("4006","用户请求过快"),
USER_SEND_TOO_OFTEN("4007","请求次数过多,已被限制"),
USER_ILLEGAL_REQUEST("4008", "用户非法请求"),
//邮箱相关
MAIL_ACCOUNT_NOT_PROVIDED("4101","未提供验证码接受账户"),
MAIL_REQUEST_TOO_FAST("4102","验证码请求过于频繁"),
MAIL_INFO_LOST("4103","验证信息丢失"),
MAIL_VERIFY_FAIL_TOO_MANY("4104","验证码错误过多,请重新申请验证码"),
MAIL_VERIFY_NOT_EXIST("4105","验证码元素丢失,请重新申请验证码"),
MAIL_VERIFY_CODE_ERROR("4106","验证码错误,请重新输入"),
//审核相关
AUDIT_NO_RECORD("4201","无审核记录条目"),
//邀请相关
INVITING_REQUEST_ERROR("4301","邀请请求错误"),
INVITING_ILLEGAL_REQUEST("4302","非法邀请"),
INVITING_ILLEGAL_RESPONSE("4303","非法邀请"),
INVITING_RE_REQUEST("4304","重复请求"),
INVITING_AUTH_ERROR("4305","权限错误"),
//群组相关
GROUP_USER_EXISTED("4401","用户已加入群组"),
GROUP_USER_NOT_EXISTED("4402","用户不在群组")
;
private final int code;
private final String code;
private final String message; private final String message;
ResultCode(int code, String message) { ResultCode(String code, String message) {
this.code = code; this.code = code;
this.message = message; this.message = message;
} }

View File

@@ -0,0 +1,86 @@
package xin.merlin.myplayerbackend.utils.websocket;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import xin.merlin.myplayerbackend.config.RabbitMQConfig;
import xin.merlin.myplayerbackend.service.OnlineStatusService;
@Slf4j
@Component
@RequiredArgsConstructor
public class CustomWebSocketHandler extends TextWebSocketHandler {
private final WebSocketSessionManager webSocketSessionManager;
private final RabbitTemplate rabbitTemplate;
private final OnlineStatusService onlineStatusService;
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
/*
TODO这里需要处理在数据库中未发送的离线消息
初步想法:
开多线程分页获取所有相关信息然后送入rabbitmq送入之后删除对应的message
但是需要处理的是用户一上线即下线的问题如何终止这个多线程任务以及送入rabbitmq的message可能又会回到数据库中并出现重复数据
*/
Integer userId = (Integer) session.getAttributes().get("id");
onlineStatusService.online(userId);
webSocketSessionManager.addSession(userId, session);
log.info("用户 {} 已连接", userId);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
rabbitTemplate.convertAndSend(RabbitMQConfig.WS_MESSAGE_QUEUE, payload);
// JSONObject msg = JSON.parseObject(payload);
// String type = msg.getString("type");
// switch (type) {
// case "chat":
// Integer toUser = msg.getInteger("toUser");
// webSocketSessionManager.sendToUser(toUser, payload);
// break;
//
// case "broadcast":
// webSocketSessionManager.broadcast(payload);
// break;
//
// case "signal":
// webSocketSessionManager.sendToUser(msg.getInteger("toUser"), payload);
// break;
//
// }
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
Integer userId = (Integer) session.getAttributes().get("id");
onlineStatusService.offline(userId);
webSocketSessionManager.removeSession(userId);
log.info("用户 {} 已断开", userId);
}
@EventListener
public void onShutdown(ContextClosedEvent event) {
log.info("Shutting down... closing websocket sessions");
webSocketSessionManager.closeAll();
}
}

View File

@@ -0,0 +1,44 @@
package xin.merlin.myplayerbackend.utils.websocket;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.config.RabbitMQConfig;
import xin.merlin.myplayerbackend.utils.websocket.command.CommandDispatcher;
@Slf4j
@Component
@RequiredArgsConstructor
public class WebSocketMessageConsumer {
private final WebSocketSessionManager sessionManager;
private final CommandDispatcher commandDispatcher;
@RabbitListener(queues = RabbitMQConfig.WS_MESSAGE_QUEUE)
public void onMessage(String json) {
try {
JSONObject msg = JSON.parseObject(json);
commandDispatcher.dispatch(msg);
} catch (Exception e) {
log.info(e.getMessage());
}
}
@RabbitListener(queues = RabbitMQConfig.WS_VIDEO_QUEUE)
public void onVideoMessage(String json) {
try {
JSONObject msg = JSON.parseObject(json);
commandDispatcher.dispatch(msg);
} catch (Exception e) {
log.info(e.getMessage());
}
}
}

View File

@@ -0,0 +1,49 @@
package xin.merlin.myplayerbackend.utils.websocket;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class WebSocketSessionManager {
private static final Map<Integer, WebSocketSession> websocketSessions = new ConcurrentHashMap<>();
public void addSession(Integer id, WebSocketSession session) {websocketSessions.put(id, session);}
public WebSocketSession getSession(Integer id) {return websocketSessions.get(id);}
public void removeSession(Integer id) {websocketSessions.remove(id);}
public Map<Integer, WebSocketSession> getSessions() {return websocketSessions;}
public void sendToUser(Integer userId, String message) throws IOException {
WebSocketSession session = websocketSessions.get(userId);
if (session != null && session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
}
public void broadcast(String message) throws IOException {
for (WebSocketSession session : websocketSessions.values()) {
if (session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
}
}
public void closeAll() {
websocketSessions.forEach((uid, session) -> {
try {
session.close(CloseStatus.GOING_AWAY);
} catch (Exception ignored) {}
});
}
}

View File

@@ -0,0 +1,10 @@
package xin.merlin.myplayerbackend.utils.websocket.command;
import com.alibaba.fastjson2.JSONObject;
import java.io.IOException;
public interface BaseCommandHandler {
void handle(JSONObject msg) throws IOException;
}

View File

@@ -0,0 +1,38 @@
package xin.merlin.myplayerbackend.utils.websocket.command;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.command.impl.*;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class CommandDispatcher {
private final Ping pingCommand;
private final Message messageCommand;
private final Typing typingCommand;
private final Heartbeat heartbeatCommand;
private final SystemNotify systemNotifyCommand;
private final PersonalNotify personalNotifyCommand;
private final GroupMessage groupMessageCommand;
public void dispatch(JSONObject msg) throws IOException {
String cmd = msg.getString("cmd");
switch (cmd) {
case "PING" -> pingCommand.handle(msg);
case "MESSAGE" -> messageCommand.handle(msg);
case "TYPING" -> typingCommand.handle(msg);
case "HEARTBEAT" -> heartbeatCommand.handle(msg);
case "SYSTEM_NOTIFY" -> systemNotifyCommand.handle(msg);
case "PERSONAL_NOTIFY" -> personalNotifyCommand.handle(msg);
case "GROUP_MESSAGE" -> groupMessageCommand.handle(msg);
default -> {
System.err.println("Unknown command: " + cmd);
}
}
}
}

View File

@@ -0,0 +1,47 @@
package xin.merlin.myplayerbackend.utils.websocket.command.impl;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.entity.Groups;
import xin.merlin.myplayerbackend.mapper.GroupsMapper;
import xin.merlin.myplayerbackend.utils.websocket.WebSocketSessionManager;
import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler;
import java.io.IOException;
import java.util.List;
@Component
@RequiredArgsConstructor
public class GroupMessage implements BaseCommandHandler {
private final GroupsMapper groupsMapper;
private final WebSocketSessionManager sessionManager;
@Override
public void handle(JSONObject msg) throws IOException {
Integer userId = msg.getInteger("from");
Integer groupId = msg.getInteger("group");
List<Object> objs = groupsMapper.selectObjs(
Wrappers.<Groups>lambdaQuery()
.select(Groups::getId)
.eq(Groups::getG_id, groupId)
.ne(Groups::getId, userId)
);
List<Integer> ids = objs.stream()
.map(o -> (Integer) o)
.toList();
for (Integer id : ids) {
JSONObject copy = new JSONObject(msg);
copy.put("to", id);
sessionManager.sendToUser(id, copy.toJSONString());
}
}
}

View File

@@ -0,0 +1,22 @@
package xin.merlin.myplayerbackend.utils.websocket.command.impl;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler;
import java.time.Duration;
@Component
@RequiredArgsConstructor
public class Heartbeat implements BaseCommandHandler {
private final RedisTemplate<String, String> redis;
@Override
public void handle(JSONObject msg) {
String userId = msg.getString("from");
redis.opsForValue().set("online:" + userId, "1", Duration.ofMinutes(5));
}
}

View File

@@ -0,0 +1,26 @@
package xin.merlin.myplayerbackend.utils.websocket.command.impl;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.WebSocketSessionManager;
import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class Message implements BaseCommandHandler {
private final WebSocketSessionManager sessionManager;
@Override
public void handle(JSONObject msg) throws IOException {
/*
TODO: 这里需要处理如果目标用户不在线,如何去完成消息的存储
*/
String to = msg.getString("to");
sessionManager.sendToUser(Integer.valueOf(to), msg.toJSONString());
}
}

View File

@@ -0,0 +1,22 @@
package xin.merlin.myplayerbackend.utils.websocket.command.impl;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.WebSocketSessionManager;
import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class PersonalNotify implements BaseCommandHandler {
private final WebSocketSessionManager sessionManager;
@Override
public void handle(JSONObject msg) throws IOException {
String to = msg.getString("to");
sessionManager.sendToUser(Integer.valueOf(to), msg.toJSONString());
}
}

View File

@@ -0,0 +1,24 @@
package xin.merlin.myplayerbackend.utils.websocket.command.impl;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.WebSocketSessionManager;
import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class Ping implements BaseCommandHandler {
private final WebSocketSessionManager sessionManager;
@Override
public void handle(JSONObject msg) throws IOException {
String from = msg.getString("from");
msg.put("cmd", "PONG");
sessionManager.sendToUser(Integer.valueOf(from), msg.toJSONString());
}
}

View File

@@ -0,0 +1,22 @@
package xin.merlin.myplayerbackend.utils.websocket.command.impl;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.WebSocketSessionManager;
import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class SystemNotify implements BaseCommandHandler {
private final WebSocketSessionManager sessionManager;
@Override
public void handle(JSONObject msg) throws IOException {
sessionManager.broadcast(msg.toJSONString());
}
}

View File

@@ -0,0 +1,22 @@
package xin.merlin.myplayerbackend.utils.websocket.command.impl;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.WebSocketSessionManager;
import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class Typing implements BaseCommandHandler {
private final WebSocketSessionManager sessionManager;
@Override
public void handle(JSONObject msg) throws IOException {
String to = msg.getString("to");
sessionManager.sendToUser(Integer.valueOf(to), msg.toJSONString());
}
}