创建cloud父组件,并将项目改为主服务+微服务MatchingSystem
This commit is contained in:
parent
c3c890be51
commit
3cc2c3c698
|
@ -0,0 +1,33 @@
|
||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
Binary file not shown.
|
@ -0,0 +1,18 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
|
||||||
|
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
|
|
@ -0,0 +1,130 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.kob</groupId>
|
||||||
|
<artifactId>backendcloud</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.kob.backend</groupId>
|
||||||
|
<artifactId>backend</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<!-- dependencies 里面存放依赖-->
|
||||||
|
<dependencies>
|
||||||
|
<!-- JDBC 依赖,用于操作连接数据库-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 简化代码,可以帮助写一些构造函数, set() , get() 函数等-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.26</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- mysql 连接驱动-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<version>8.0.32</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- mybatis-plus 用于低代码开发-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>3.5.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- mybatis-plus-generator 用于自动生成一些函数-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-generator</artifactId>
|
||||||
|
<version>3.5.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 用于安全验证-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 用于使用 JWT 验证-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
<version>0.11.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-impl</artifactId>
|
||||||
|
<version>0.11.5</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
|
<version>0.11.5</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 用于配置使用 websocket-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- json 用于前后端通信-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>2.0.24</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains</groupId>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<version>13.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.kob.backend;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class BackendApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(BackendApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.kob.backend.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
// 这个类用于解决CORS跨域问题
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig implements Filter {
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
|
||||||
|
HttpServletResponse response = (HttpServletResponse) res;
|
||||||
|
HttpServletRequest request = (HttpServletRequest) req;
|
||||||
|
|
||||||
|
String origin = request.getHeader("Origin");
|
||||||
|
if (origin != null) {
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
String headers = request.getHeader("Access-Control-Request-Headers");
|
||||||
|
if (headers != null) {
|
||||||
|
response.setHeader("Access-Control-Allow-Headers", headers);
|
||||||
|
response.setHeader("Access-Control-Expose-Headers", headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setHeader("Access-Control-Allow-Methods", "*");
|
||||||
|
response.setHeader("Access-Control-Max-Age", "3600");
|
||||||
|
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 需要用到哪些东西的时候,就定义一个它的 @Configuration 再加一个 @Bean 注解,返回它的实例即可
|
||||||
|
// 在需要使用的类里面加上一个 @Autowired 注入即可
|
||||||
|
// 向后端(匹配系统)发请求的工具,想取得什么,就加一个 Bean 注解
|
||||||
|
package com.kob.backend.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
// 加注解 Configuration
|
||||||
|
// RestTemplate 用于处理 Spring 服务之间的 http 请求
|
||||||
|
@Configuration
|
||||||
|
public class RestTemplateConfig {
|
||||||
|
@Bean
|
||||||
|
public RestTemplate getRestTemplate(){
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.kob.backend.config;
|
||||||
|
|
||||||
|
/*
|
||||||
|
主要作用:放行登录、注册等接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.kob.backend.config.filter.JwtAuthenticationTokenFilter;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
// TODO: 2023/2/19 WebSecurityConfigurerAdapter 已被弃用,注意替换
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
@Autowired
|
||||||
|
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
|
||||||
|
|
||||||
|
// 配置密码加密方式
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Override
|
||||||
|
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||||
|
return super.authenticationManagerBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||||
|
用于配置公开链接
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.csrf().disable()
|
||||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
.and()
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||||
|
.antMatchers("/pk/start/").hasIpAddress("127.0.0.1")
|
||||||
|
.antMatchers(HttpMethod.OPTIONS).permitAll()
|
||||||
|
.anyRequest().authenticated();
|
||||||
|
|
||||||
|
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于配置 websocket:放行所有的 websocket/ 链接
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) throws Exception {
|
||||||
|
web.ignoring().antMatchers("/websocket/**");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.kob.backend.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class WebSocketConfig {
|
||||||
|
@Bean
|
||||||
|
public ServerEndpointExporter serverEndpointExporter() {
|
||||||
|
return new ServerEndpointExporter();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.kob.backend.config.filter;
|
||||||
|
|
||||||
|
//filter 类用于验证
|
||||||
|
/*
|
||||||
|
主要作用:
|
||||||
|
1. 用来验证jwt token,如果验证成功,则将User信息注入上下文中
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.kob.backend.mapper.UserMapper;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import com.kob.backend.utils.JwtUtil;
|
||||||
|
import com.sun.xml.internal.bind.v2.TODO;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
|
@Autowired
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
String token = request.getHeader("Authorization");
|
||||||
|
|
||||||
|
// TODO 这里可以更改 bearar 为其他字符作为验证前缀
|
||||||
|
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = token.substring(7);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 核心验证逻辑
|
||||||
|
String userid;
|
||||||
|
try {
|
||||||
|
// 将 token 解析,如果能成功解析出 userid 表示合法,否则表示不合法
|
||||||
|
Claims claims = JwtUtil.parseJWT(token);
|
||||||
|
userid = claims.getSubject();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userMapper.selectById(Integer.parseInt(userid));
|
||||||
|
|
||||||
|
// User user = userMapper.selectById(new JwtAuthenticationUtil().getUserId(token));
|
||||||
|
if (user == null) {
|
||||||
|
throw new RuntimeException("用户名未登录");
|
||||||
|
}
|
||||||
|
|
||||||
|
UserDetailsImpl loginUser = new UserDetailsImpl(user);
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(loginUser, null, null);
|
||||||
|
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
package com.kob.backend.consumer;
|
||||||
|
|
||||||
|
// WebSocket用于前后端通信
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.kob.backend.config.RestTemplateConfig;
|
||||||
|
import com.kob.backend.consumer.utils.Game;
|
||||||
|
import com.kob.backend.consumer.utils.JwtAuthenticationUtil;
|
||||||
|
import com.kob.backend.mapper.RecordMapper;
|
||||||
|
import com.kob.backend.mapper.UserMapper;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import javax.websocket.*;
|
||||||
|
import javax.websocket.server.PathParam;
|
||||||
|
import javax.websocket.server.ServerEndpoint;
|
||||||
|
import javax.xml.stream.events.StartDocument;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ServerEndpoint("/websocket/{token}") // 注意不要以'/'结尾
|
||||||
|
public class WebSocketServer {
|
||||||
|
/*
|
||||||
|
存储所有链接:对所有 websocket 可见的全局变量,存储为 static 静态变量
|
||||||
|
因为每个 websocket 实例都在一个独立的线程里,所以该公共变量应该是线程安全的
|
||||||
|
使用线程安全的 Hash 表 ConcurrentHashMap<>(),
|
||||||
|
将 userId 映射到 webSocketServer
|
||||||
|
*/
|
||||||
|
public static final ConcurrentHashMap<Integer, WebSocketServer> users = new ConcurrentHashMap<>();
|
||||||
|
// 用于和 MatchingSystem 进行通信
|
||||||
|
private static RestTemplate restTemplate;
|
||||||
|
// 在 WebSocketServer 中注入数据库的方法演示-> 使用 static 定义为独一份的变量
|
||||||
|
private static UserMapper userMapper;
|
||||||
|
// 注入 RecordMapper 用于调用实现游戏数据到数据库的存储
|
||||||
|
public static RecordMapper recordMapper;
|
||||||
|
// 后端向前端发送信息,首先需要创建一个 session
|
||||||
|
private Session session = null;
|
||||||
|
// 用户信息:定义一个成员变量
|
||||||
|
private User user;
|
||||||
|
private Game game = null;
|
||||||
|
// addPlayer 添加用户到匹配池的 URL; removePlayer 从匹配池移除用户的 URL
|
||||||
|
private final static String addPlayerUrl = "http://127.0.0.1:3001/player/add/";
|
||||||
|
private final static String removePlayerUrl = "http://127.0.0.1:3001/player/remove/";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setRestTemplate(RestTemplate restTemplate) {
|
||||||
|
// 注入 RestTemplate:作用是微服务之间发送请求
|
||||||
|
WebSocketServer.restTemplate = restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注入方法
|
||||||
|
@Autowired
|
||||||
|
public void setUserMapper(UserMapper userMapper) {
|
||||||
|
// 静态变量 userMapper 访问需要使用类名 WebSocketServer 访问
|
||||||
|
WebSocketServer.userMapper = userMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setRecordMapper(RecordMapper recordMapper) {
|
||||||
|
WebSocketServer.recordMapper = recordMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// @OnOpen 创建链接时自动触发这个函数
|
||||||
|
@OnOpen
|
||||||
|
public void onOpen(Session session, @PathParam("token") String token) throws IOException {
|
||||||
|
// 建立链接时需要将 session 存下来
|
||||||
|
this.session = session;
|
||||||
|
// 成功建立连接时,输出 connected!
|
||||||
|
System.out.println("backend connected!");
|
||||||
|
// 将 userId 取出来,通过 userId 将 user 找出来
|
||||||
|
// int userId = Integer.parseInt(token);
|
||||||
|
int userId = JwtAuthenticationUtil.getUserId(token);
|
||||||
|
this.user = userMapper.selectById(userId);
|
||||||
|
|
||||||
|
// 如果 user 存在, 表示用户登录成功, 用户信息是存在的
|
||||||
|
if (this.user != null) {
|
||||||
|
// 将 user 存到 users HashMap里
|
||||||
|
users.put(userId, this);
|
||||||
|
|
||||||
|
// (测试)后台输出看用户信息
|
||||||
|
// System.out.println(user);
|
||||||
|
}
|
||||||
|
// 否则,断开连接(这里需要抛出异常)
|
||||||
|
else {
|
||||||
|
this.session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClose
|
||||||
|
public void onClose() {
|
||||||
|
// 关闭链接
|
||||||
|
System.out.println("backend disconnected!");
|
||||||
|
// 断开连接时,需要将 user 从 users 里面删掉
|
||||||
|
if (this.user != null) {
|
||||||
|
users.remove(this.user.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配逻辑函数
|
||||||
|
public static void startGame(Integer aId, Integer bId) {
|
||||||
|
User a = userMapper.selectById(aId);
|
||||||
|
User b = userMapper.selectById(bId);
|
||||||
|
|
||||||
|
// 匹配成功时,创建联机地图
|
||||||
|
Game game = new Game(13, 14, 20, a.getId(), b.getId());
|
||||||
|
game.createMap(); // 初始化地图
|
||||||
|
// 当用户不为空时,才能执行下面的操作(防止有人退出游戏后用户已经成为空指针)
|
||||||
|
if (users.get(a.getId()) != null)
|
||||||
|
users.get(a.getId()).game = game; // 将 game 赋给 a 玩家
|
||||||
|
|
||||||
|
if (users.get(b.getId()) != null)
|
||||||
|
users.get(b.getId()).game = game;
|
||||||
|
|
||||||
|
game.start(); // 开启新线程,执行函数
|
||||||
|
|
||||||
|
JSONObject respGameData = new JSONObject();
|
||||||
|
respGameData.put("game_map", game.getG());
|
||||||
|
respGameData.put("rows", game.getRows());
|
||||||
|
respGameData.put("cols", game.getCols());
|
||||||
|
respGameData.put("inner_walls_count", game.getInnerWallsCount());
|
||||||
|
respGameData.put("a_id", game.getPlayerA().getId());
|
||||||
|
respGameData.put("a_sx", game.getPlayerA().getSx());
|
||||||
|
respGameData.put("a_sy", game.getPlayerA().getSy());
|
||||||
|
respGameData.put("b_id", game.getPlayerB().getId());
|
||||||
|
respGameData.put("b_sx", game.getPlayerB().getSx());
|
||||||
|
respGameData.put("b_sy", game.getPlayerB().getSy());
|
||||||
|
|
||||||
|
// 将 a 配对成功的消息传回客户端
|
||||||
|
JSONObject respA = new JSONObject();
|
||||||
|
respA.put("event", "start-matching");
|
||||||
|
respA.put("opponent_username", b.getUsername());
|
||||||
|
respA.put("opponent_photo", b.getPhoto());
|
||||||
|
respA.put("game_data", respGameData);
|
||||||
|
// 获取 a 的链接,并通过 sendMessage 将消息传给前端
|
||||||
|
if (users.get(a.getId()) != null)
|
||||||
|
users.get(a.getId()).sendMessage(respA.toJSONString());
|
||||||
|
|
||||||
|
// 同理,将 b 的匹配成功信息传回给前端
|
||||||
|
JSONObject respB = new JSONObject();
|
||||||
|
respB.put("event", "start-matching");
|
||||||
|
respB.put("opponent_username", a.getUsername());
|
||||||
|
respB.put("opponent_photo", a.getPhoto());
|
||||||
|
respB.put("game_data", respGameData);
|
||||||
|
if (users.get(b.getId()) != null)
|
||||||
|
users.get(b.getId()).sendMessage(respB.toJSONString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始匹配的逻辑部分:开始匹配时向 MatchingSystem 服务端发一个请求
|
||||||
|
private void startMatching() {
|
||||||
|
System.out.println("start matching");
|
||||||
|
// 这里的参数列表需要与 MatchingSystem(微服务)->MatchingController->addPlayer 的参数对应
|
||||||
|
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
|
||||||
|
data.add("user_id", this.user.getId().toString());
|
||||||
|
data.add("rating", this.user.getRating().toString());
|
||||||
|
// postForObject(请求的URL,传出的数据,返回值的类型.class) -> Java 反射机制
|
||||||
|
String res = restTemplate.postForObject(addPlayerUrl, data, String.class);
|
||||||
|
System.out.println(this.user.getId().toString() + " " + this.user.getUsername() + " " + res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消匹配的逻辑部分:向 MatchingSystem 发送请求:从匹配池移除一个用户
|
||||||
|
private void stopMatching() {
|
||||||
|
System.out.println("stop matching");
|
||||||
|
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
|
||||||
|
data.add("user_id", this.user.getId().toString());
|
||||||
|
String res = restTemplate.postForObject(removePlayerUrl, data, String.class);
|
||||||
|
System.out.println(this.user.getId().toString() + " " + this.user.getUsername() + " " + res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// direction 传入 move(移动) 方向参数
|
||||||
|
private void move(int direction) {
|
||||||
|
// 判断角色:如果是 A 角色,则将获取到的方向设置为 A 的 nextStep 方向
|
||||||
|
// user.getId() 是获取当前链接的用户 id
|
||||||
|
if (game.getPlayerA().getId().equals(user.getId())) {
|
||||||
|
game.setNextStepA(direction);
|
||||||
|
} else if (game.getPlayerB().getId().equals(user.getId())) {
|
||||||
|
game.setNextStepB(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @OnMessage 用于从前端接收请求: 一般 onMessage 当成路由使用,做为消息判断处理的中间部分
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(String message, Session session) {
|
||||||
|
// 从 Client 接收消息
|
||||||
|
System.out.println("receive message");
|
||||||
|
// 解析从前端接收到的请求
|
||||||
|
JSONObject data = JSONObject.parseObject(message);
|
||||||
|
String event = data.getString("event");
|
||||||
|
// System.out.println(event);
|
||||||
|
|
||||||
|
// 匹配状态函数调用
|
||||||
|
if ("start-matching".equals(event)) {
|
||||||
|
startMatching();
|
||||||
|
} else if ("stop-matching".equals(event)) {
|
||||||
|
stopMatching();
|
||||||
|
} else if ("move".equals(event)) {
|
||||||
|
move(data.getInteger("direction"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnError
|
||||||
|
public void onError(Session session, Throwable error) {
|
||||||
|
error.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从后端向前端发送信息
|
||||||
|
public void sendMessage(String message) {
|
||||||
|
// 异步通信过程,先加一个锁
|
||||||
|
synchronized (this.session) {
|
||||||
|
try {
|
||||||
|
// 将 message 发送到前端
|
||||||
|
this.session.getBasicRemote().sendText(message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.kob.backend.consumer.utils;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Cell {
|
||||||
|
int x, y;
|
||||||
|
}
|
|
@ -0,0 +1,370 @@
|
||||||
|
package com.kob.backend.consumer.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.kob.backend.consumer.WebSocketServer;
|
||||||
|
import com.kob.backend.pojo.Record;
|
||||||
|
|
||||||
|
import javax.swing.event.InternalFrameEvent;
|
||||||
|
import java.sql.Time;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
// 用来管理整个游戏流程:这里需要多线程
|
||||||
|
public class Game extends Thread {
|
||||||
|
// 游戏地图:行数,列数,内部障碍物数量
|
||||||
|
private final Integer rows;
|
||||||
|
private final Integer cols;
|
||||||
|
private final Integer inner_walls_count;
|
||||||
|
// 地图数组
|
||||||
|
private final int[][] g;
|
||||||
|
// 定义"上右下左"四个方向的 dx, dy偏移量
|
||||||
|
private final int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
|
||||||
|
private final Player playerA, playerB;
|
||||||
|
// 玩家下一步操作状态定义
|
||||||
|
private Integer nextStepA = null;
|
||||||
|
private Integer nextStepB = null;
|
||||||
|
// 加锁:解决读写冲突用
|
||||||
|
private ReentrantLock lock = new ReentrantLock();
|
||||||
|
// 整个游戏的当前状态: playing(正在进行) --> finished(游戏结束)
|
||||||
|
private String status = "playing";
|
||||||
|
// 定义失败者: all(平局), A(A输), B(B输)
|
||||||
|
private String loser = "";
|
||||||
|
|
||||||
|
|
||||||
|
// 初始化(有参)构造函数
|
||||||
|
public Game(Integer rows, Integer cols, Integer inner_walls_count, Integer idA, Integer idB) {
|
||||||
|
this.rows = rows;
|
||||||
|
this.cols = cols;
|
||||||
|
this.inner_walls_count = inner_walls_count;
|
||||||
|
this.g = new int[rows][cols];
|
||||||
|
playerA = new Player(idA, rows - 2, 1, new ArrayList<>());
|
||||||
|
playerB = new Player(idB, 1, cols - 2, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayerA() {
|
||||||
|
return playerA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayerB() {
|
||||||
|
return playerB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRows() {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCols() {
|
||||||
|
return cols;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInnerWallsCount() {
|
||||||
|
return inner_walls_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getGameMapString() {
|
||||||
|
StringBuilder res = new StringBuilder();
|
||||||
|
// 将地图数据展开成一维
|
||||||
|
/* for (int i = 0; i < rows; i++) {
|
||||||
|
for (int j = 0; j < cols; j++) {
|
||||||
|
res.append(g[i][j]);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
for (int[] row : g) {
|
||||||
|
for (int col : row) {
|
||||||
|
res.append(col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextStepA(Integer nextStepA) {
|
||||||
|
// 为了防止两方读写冲突,需要加线程锁之后操作 nextStep 值
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
this.nextStepA = nextStepA;
|
||||||
|
} finally {
|
||||||
|
// 操作完成后解锁
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextStepB(Integer nextStepB) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
this.nextStepB = nextStepB;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回生成的地图
|
||||||
|
public int[][] getG() {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 联通检测方法---true(联通)---false(不通),参数: 起点坐标 sx,sy ,终点坐标 tx,ty
|
||||||
|
private boolean check_connectivity(int sx, int sy, int tx, int ty) {
|
||||||
|
// 起点就是终点时,结果联通,直接返回 true
|
||||||
|
if (sx == tx && sy == ty) return true;
|
||||||
|
g[sx][sy] = 1;
|
||||||
|
|
||||||
|
//枚举"上右下左"四个方向,求当前点下一个相邻点的坐标
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
int x = sx + dx[i];
|
||||||
|
int y = sy + dy[i];
|
||||||
|
|
||||||
|
// 判断是否撞到障碍物( g[x][y] == 0 表示没有碰到障碍物 ),如果没有赚到障碍物,且可以找到重点的话,返回 true(联通)
|
||||||
|
if (x >= 0 && x < this.rows && y >= 0 && y < this.cols && g[x][y] == 0) {
|
||||||
|
if (check_connectivity(x, y, tx, ty)) {
|
||||||
|
// 还原状态
|
||||||
|
g[sx][sy] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 还原状态
|
||||||
|
g[sx][sy] = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 画地图
|
||||||
|
public boolean draw() {
|
||||||
|
// 一开始现将所有障碍物初始化为 false
|
||||||
|
for (int r = 0; r < this.rows; r++) {
|
||||||
|
for (int c = 0; c < this.cols; c++) {
|
||||||
|
// 0 表示空地, 1 表示障碍物(墙)
|
||||||
|
g[r][c] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 地图左右边缘障碍物
|
||||||
|
for (int r = 0; r < this.rows; r++) {
|
||||||
|
g[r][0] = g[r][this.cols - 1] = 1;
|
||||||
|
}
|
||||||
|
// 地图上下边缘障碍物
|
||||||
|
for (int c = 0; c < this.cols; c++) {
|
||||||
|
g[0][c] = g[this.rows - 1][c] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
// 创建内部随机障碍物,每次计算时会生成两个障碍物,因此这里的循环次数 this.inner_walls_count 需要处以 2
|
||||||
|
for (int i = 0; i < this.inner_walls_count / 2; i++) {
|
||||||
|
// 避免位置重复:重复遍历 1000 次,只要找到了已经存在障碍物的位置就禁止随机
|
||||||
|
for (int j = 0; j < 1000; j++) {
|
||||||
|
// 找出本次随机到的行-r 列-c 坐标
|
||||||
|
int r = random.nextInt(this.rows); // random.nextInt(7) 返回 0-7之间的随机整数
|
||||||
|
int c = random.nextInt(this.cols);
|
||||||
|
|
||||||
|
// 中心对称:当本坐标或者它的中心对称坐标已经存在障碍物了,则重新计算下一个坐标
|
||||||
|
if (g[r][c] == 1 || g[this.rows - 1 - r][this.cols - 1 - c] == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 避免内部障碍物覆盖掉左下角和右上角的 A-B 角色出发点,如果是这两个位置,则重新计算下一个坐标
|
||||||
|
if (r == this.rows - 2 && c == 1 || r == 1 && c == this.cols - 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 将计算求得的随机障碍物合法的位置赋值为 1 ,以对该位置进行绘制(包括本坐标及其中心对称坐标)
|
||||||
|
g[r][c] = g[this.rows - 1 - r][this.cols - 1 - c] = 1;
|
||||||
|
|
||||||
|
// 1000 次遍历中,规定数量的内部障碍物已经够数之后就 break 掉
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 A-B 角色的运动区域是联通的:如果不连通,则直接在创建地图对象之前取消绘制( return false )
|
||||||
|
return check_connectivity(this.rows - 2, 1, 1, this.cols - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createMap() {
|
||||||
|
// 循环绘制:如果发现哪次循环中画地图成功了,则跳出循环,绘制结束
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
if (draw())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取两名玩家的下一步操作
|
||||||
|
private boolean nextStep() {
|
||||||
|
try {
|
||||||
|
// 返回下一步操作之前,先睡眠 200 毫秒,用于等待前端渲染完成,避免渲染速度跟不上后端运算处理速度
|
||||||
|
// 前端定义了每 1 秒钟 5 个格子,则每 200 毫秒走一格(每一步至少需要 200 毫秒的时间)
|
||||||
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环 50 秒钟,每次 100 毫秒,判断用户输入(50 * 100 = 5000 ms后如果没有接收到输入,则判断输赢)
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
try {
|
||||||
|
// 两次接收输入的等待间隔
|
||||||
|
Thread.sleep(100);
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
// 判断两名玩家是否都已经读取到输入:如果读入都非空,则表示读取结束,返回 true
|
||||||
|
if (nextStepA != null && nextStepB != null) {
|
||||||
|
// 将下一步操作存下来
|
||||||
|
playerA.getSteps().add(nextStepA);
|
||||||
|
playerB.getSteps().add(nextStepB);
|
||||||
|
|
||||||
|
// 成功获取到两名玩家的下一步操作,返回 true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 碰撞合法性检测辅助函数:需要判断 snakeA 和 snakeB 是否重合
|
||||||
|
private boolean checkValid(List<Cell> cellsA, List<Cell> cellsB) {
|
||||||
|
int n = cellsA.size(); // cellsA 的当前长度
|
||||||
|
Cell cell = cellsA.get(n - 1); // 取出 cellsA 的蛇头位置(数组最后一位)(第0个元素是蛇尾巴)
|
||||||
|
if (g[cell.x][cell.y] == 1) return false; // 判断 A 的最后一位(蛇头)的坐标是否是障碍物(值为1),如果是障碍物则返回false(判断已碰撞)
|
||||||
|
|
||||||
|
// 判断 A蛇头位置是否和A除了蛇头以外的其他身体部分坐标有重合
|
||||||
|
for (int i = 0; i < n - 1; i++) {
|
||||||
|
if (cellsA.get(i).x == cell.x && cellsA.get(i).y == cell.y)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Cell cellB : cellsB) { // 判断 A蛇头位置是否和B蛇身体各部分坐标有重合:有重合返回不合法(表示已经碰撞)
|
||||||
|
if (cellB.x == cell.x && cellB.y == cell.y)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以上判断条件如果都合法,则返回 true合法(表示没有碰撞)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断两名玩家下一步操作是否合法
|
||||||
|
private void judge() {
|
||||||
|
// 取出 A,B 两蛇
|
||||||
|
List<Cell> cellsA = playerA.getCells();
|
||||||
|
List<Cell> cellsB = playerB.getCells();
|
||||||
|
|
||||||
|
boolean validA = checkValid(cellsA, cellsB);
|
||||||
|
boolean validB = checkValid(cellsB, cellsA);
|
||||||
|
|
||||||
|
// 如果 A 和 B 有人这一步不合法(已经产生碰撞),将游戏状态改为 "finished",表示游戏结束
|
||||||
|
if (!validA || !validB) {
|
||||||
|
status = "finished";
|
||||||
|
|
||||||
|
// 游戏结束时,判断输赢
|
||||||
|
if (!validA && !validB) {
|
||||||
|
loser = "all";
|
||||||
|
} else if (!validA && validB) {
|
||||||
|
loser = "A";
|
||||||
|
} else if (validA && !validB) {
|
||||||
|
loser = "B";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向两个 Client 广播信息
|
||||||
|
private void sendAllMessage(String message) {
|
||||||
|
if (WebSocketServer.users.get(playerA.getId()) != null)
|
||||||
|
WebSocketServer.users.get(playerA.getId()).sendMessage(message);
|
||||||
|
if (WebSocketServer.users.get(playerB.getId()) != null)
|
||||||
|
WebSocketServer.users.get(playerB.getId()).sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向两个 Client 传递移动信息
|
||||||
|
private void sendMove() {
|
||||||
|
//由于这里需要读入 nextStepA, nextStepB, 这里需要加一下线程锁
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
JSONObject resp = new JSONObject();
|
||||||
|
resp.put("event", "move"); // event(事件类型): move(移动信息)
|
||||||
|
resp.put("a_direction", nextStepA); // a_direction( playerA 移动的方向)
|
||||||
|
resp.put("b_direction", nextStepB); // b_direction( playerB 移动的方向)
|
||||||
|
sendAllMessage(resp.toJSONString());
|
||||||
|
// 本次获取完下一步操作的同时需要进行再下一步操作输入,在这之前,需要将当前的 nextStep 清空
|
||||||
|
nextStepA = nextStepB = null;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将游戏结果存到数据库中
|
||||||
|
private void saveToDataBase() {
|
||||||
|
Record record = new Record(
|
||||||
|
null,
|
||||||
|
playerA.getId(),
|
||||||
|
playerA.getSx(),
|
||||||
|
playerA.getSy(),
|
||||||
|
playerB.getId(),
|
||||||
|
playerB.getSx(),
|
||||||
|
playerB.getSy(),
|
||||||
|
playerA.getStepsString(),
|
||||||
|
playerB.getStepsString(),
|
||||||
|
getGameMapString(),
|
||||||
|
loser,
|
||||||
|
new Date()
|
||||||
|
);
|
||||||
|
|
||||||
|
WebSocketServer.recordMapper.insert(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 向两个 Client 公布结果
|
||||||
|
private void sendResult() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
JSONObject resp = new JSONObject();
|
||||||
|
resp.put("event", "result"); // event(事件类型): result(结果)
|
||||||
|
resp.put("loser", loser); // 失败者
|
||||||
|
// 将最后碰撞时的蛇的眼睛指向传给前端
|
||||||
|
resp.put("a_eyes_finally_direction", nextStepA);
|
||||||
|
resp.put("b_eyes_finally_direction", nextStepB);
|
||||||
|
// 发送结果之前,先将结果存到数据库中
|
||||||
|
saveToDataBase();
|
||||||
|
sendAllMessage(resp.toJSONString());
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重写线程函数
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 循环 1000 步: 13格x14格x(最大)3步/格 < 1000,保证 1000 次一定可以走完
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
if (nextStep()) { // 是否获取到两条蛇的下一步操作
|
||||||
|
// 判断下一步操作是否合法
|
||||||
|
judge();
|
||||||
|
|
||||||
|
// 如果游戏状态还是 playing,则向两个玩家广播移动信息
|
||||||
|
if ("playing".equals(status)) {
|
||||||
|
sendMove();
|
||||||
|
} else if ("finished".equals(status)) {
|
||||||
|
// 如果游戏状态已经更改为了 finished,则将结果返回给 Client 并 break 中断执行后续操作
|
||||||
|
sendResult();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 未获取到某蛇的下一步操作,则将游戏状态改为 finished
|
||||||
|
status = "finished";
|
||||||
|
// 判断失败者:这里需要加锁,因为涉及到对 nextStep 的读操作
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
if (nextStepA == null && nextStepB == null) {
|
||||||
|
loser = "all";
|
||||||
|
} else if (nextStepA == null) {
|
||||||
|
loser = "A";
|
||||||
|
} else {
|
||||||
|
loser = "B";
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.kob.backend.consumer.utils;
|
||||||
|
|
||||||
|
import com.kob.backend.utils.JwtUtil;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
|
||||||
|
//Jwt 验证配置工具类
|
||||||
|
public class JwtAuthenticationUtil {
|
||||||
|
public static Integer getUserId(String token) {
|
||||||
|
// 默认 userid 赋值为 -1 ,表示不存在
|
||||||
|
int userId = -1;
|
||||||
|
try {
|
||||||
|
// 将 token 解析,如果能成功解析出 userid 表示合法,否则表示不合法
|
||||||
|
Claims claims = JwtUtil.parseJWT(token);
|
||||||
|
userId = Integer.parseInt(claims.getSubject());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.kob.backend.consumer.utils;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// 玩家信息
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Player {
|
||||||
|
private Integer id;
|
||||||
|
private Integer sx;
|
||||||
|
private Integer sy;
|
||||||
|
// 玩家走过的路径中每一步的方向
|
||||||
|
private List<Integer> steps;
|
||||||
|
|
||||||
|
// 检验当前回合蛇的长度(蛇尾)是否需要增加
|
||||||
|
public boolean checkTailIncreasing(int step) {
|
||||||
|
// 前 10 回合每次都增加一节蛇尾,后面每 3 回合增加一节蛇尾
|
||||||
|
if (step <= 10) return true;
|
||||||
|
if (step % 3 == 1) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 蛇身 Cell 列表:记录蛇身各部分坐标
|
||||||
|
public List<Cell> getCells() {
|
||||||
|
List<Cell> res = new ArrayList<>();
|
||||||
|
|
||||||
|
// 四方向偏移量
|
||||||
|
int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
|
||||||
|
int x = sx, y = sy; // 起始坐标
|
||||||
|
int step = 0; // 定义当前回合数: 开始时为 0 回合
|
||||||
|
res.add(new Cell(x, y)); // 现将蛇头添加进 List
|
||||||
|
for (int d : steps) { // 使用下一步方向求出下一个结点的坐标
|
||||||
|
x += dx[d];
|
||||||
|
y += dy[d];
|
||||||
|
res.add(new Cell(x, y)); // 添加新的身体结点
|
||||||
|
step++; // 回合数 +1
|
||||||
|
// 判断蛇尾要不要增加:如果本回合蛇尾不增加,则将本回合新生成的蛇尾(列表的第0个元素)删掉
|
||||||
|
if (!checkTailIncreasing(step)) {
|
||||||
|
res.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// steps 转换为 string 的辅助函数
|
||||||
|
public String getStepsString() {
|
||||||
|
StringBuilder res = new StringBuilder();
|
||||||
|
for (int d : steps) {
|
||||||
|
res.append(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.kob.backend.controller.pk;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kob.backend.service.pk.StartGameService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class StartGameController {
|
||||||
|
@Autowired
|
||||||
|
private StartGameService startGameService;
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/pk/start/")
|
||||||
|
public String startGame(@RequestParam MultiValueMap<String, String> data) {
|
||||||
|
Integer aId = Integer.parseInt(Objects.requireNonNull(data.getFirst("a_id")));
|
||||||
|
Integer bId = Integer.parseInt(Objects.requireNonNull(data.getFirst("b_id")));
|
||||||
|
return startGameService.startGame(aId, bId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.kob.backend.controller.user;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.kob.backend.mapper.UserMapper;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
//实现对用户类 User 的增删查操作->这个类仅用于调试,故 Deparecated 掉
|
||||||
|
@Deprecated
|
||||||
|
@RestController
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
// 用到数据库里的 Mapper 时,需要加 @Autowired 注解
|
||||||
|
@Autowired
|
||||||
|
UserMapper userMapper;
|
||||||
|
|
||||||
|
// 返回所有用户
|
||||||
|
@GetMapping("/user/all")
|
||||||
|
public List<User> getAll() {
|
||||||
|
// null 表示查询所有的
|
||||||
|
return userMapper.selectList(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@GetMapping("/user/{userId}/")
|
||||||
|
public User getUser(@PathVariable int userId) {
|
||||||
|
// 查询某个用户
|
||||||
|
// return userMapper.selectById(userId);
|
||||||
|
|
||||||
|
// 封装查询语句:查询某个 id 的用户
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("id", userId);
|
||||||
|
return userMapper.selectOne(queryWrapper);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@GetMapping("/user/{userId}")
|
||||||
|
public List<User> getUser(@PathVariable int userId) {
|
||||||
|
// 封装查询语句:查询某个范围 id 的用户: ge 是大于等于, gt 是大于, le 是小于等于, lt 是小于
|
||||||
|
QueryWrapper<User> queryWrapper1 = new QueryWrapper<>();
|
||||||
|
queryWrapper1.ge("id", 2).le("id", 4);
|
||||||
|
return userMapper.selectList(queryWrapper1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/user/add/{userId}/{username}/{password}")
|
||||||
|
public String addUser(@PathVariable int userId,
|
||||||
|
@PathVariable String username,
|
||||||
|
@PathVariable String password) {
|
||||||
|
if(password.length() < 6){
|
||||||
|
return "密码少于6位,请重设密码";
|
||||||
|
}
|
||||||
|
// 加密明文密码并存入密文到数据库
|
||||||
|
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||||
|
String encodedPassword = passwordEncoder.encode(password);
|
||||||
|
User user = new User(userId, username, encodedPassword,"",null);
|
||||||
|
userMapper.insert(user);
|
||||||
|
return "Add User Successfully";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/user/delete/{userId}")
|
||||||
|
public String deleteUser(@PathVariable int userId){
|
||||||
|
userMapper.deleteById(userId);
|
||||||
|
return "Delete User Successfully";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.kob.backend.controller.user.account;
|
||||||
|
|
||||||
|
import com.kob.backend.service.user.account.InfoService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class InfoController {
|
||||||
|
@Autowired
|
||||||
|
private InfoService infoService;
|
||||||
|
|
||||||
|
// 获取信息
|
||||||
|
@GetMapping("/user/account/info/")
|
||||||
|
public Map<String,String> getInfo(){
|
||||||
|
return infoService.getInfo();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.kob.backend.controller.user.account;
|
||||||
|
|
||||||
|
import com.kob.backend.service.user.account.LoginService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
//@RestController 配合 @PostMapping("/.../.../") 使用:映射 Url 地址
|
||||||
|
@RestController
|
||||||
|
public class LoginController {
|
||||||
|
// @Autowired 注入接口
|
||||||
|
@Autowired
|
||||||
|
private LoginService loginService;
|
||||||
|
|
||||||
|
// 登录使用 POST 请求,密文传输,更安全
|
||||||
|
@PostMapping("/user/account/token/")
|
||||||
|
public Map<String,String> getToken(@RequestParam Map<String,String> map){
|
||||||
|
String username = map.get("username");
|
||||||
|
String password = map.get("password");
|
||||||
|
|
||||||
|
return loginService.getToken(username,password);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.kob.backend.controller.user.account;
|
||||||
|
|
||||||
|
import com.kob.backend.service.user.account.RegisterService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class RegisterController {
|
||||||
|
@Autowired
|
||||||
|
private RegisterService registerService;
|
||||||
|
|
||||||
|
@PostMapping("/user/account/register/")
|
||||||
|
public Map<String, String> register(@RequestParam Map<String, String> map) {
|
||||||
|
String username = map.get("username");
|
||||||
|
String password = map.get("password");
|
||||||
|
String confirmedPassword = map.get("confirmedPassword");
|
||||||
|
return registerService.register(username, password, confirmedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.kob.backend.controller.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.service.user.bot.AddService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class AddController {
|
||||||
|
@Autowired
|
||||||
|
private AddService addService;
|
||||||
|
|
||||||
|
@PostMapping("/user/bot/add/")
|
||||||
|
public Map<String,String> add(@RequestParam Map<String,String> data){
|
||||||
|
return addService.add(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.kob.backend.controller.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.pojo.Bot;
|
||||||
|
import com.kob.backend.service.user.bot.GetListService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class GetListController {
|
||||||
|
@Autowired
|
||||||
|
private GetListService getListService;
|
||||||
|
|
||||||
|
@GetMapping("/user/bot/getlist/")
|
||||||
|
public List<Bot> getList(){
|
||||||
|
return getListService.getList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.kob.backend.controller.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.service.user.bot.RemoveService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class RemoveController {
|
||||||
|
@Autowired
|
||||||
|
private RemoveService removeService;
|
||||||
|
|
||||||
|
@PostMapping("/user/bot/remove/")
|
||||||
|
public Map<String,String> remove(@RequestParam Map<String,String> data){
|
||||||
|
return removeService.remove(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.kob.backend.controller.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.service.user.bot.UpdateService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class UpdateController {
|
||||||
|
@Autowired
|
||||||
|
private UpdateService updateService;
|
||||||
|
|
||||||
|
@PostMapping("/user/bot/update/")
|
||||||
|
public Map<String,String> update(@RequestParam Map<String,String> data){
|
||||||
|
return updateService.update(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.kob.backend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.kob.backend.pojo.Bot;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface BotMapper extends BaseMapper<Bot> {
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.kob.backend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.kob.backend.pojo.Record;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface RecordMapper extends BaseMapper<Record> {
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
//用于将 class 的操作转化成 sql 语句
|
||||||
|
package com.kob.backend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface UserMapper extends BaseMapper<User> {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// 用于将 Mysql 表 Bot 转换为 class
|
||||||
|
// 数据库中 user_id 这种在 pojo 里要命名为 userId
|
||||||
|
package com.kob.backend.pojo;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Bot {
|
||||||
|
// 主键自增
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
// 注意:在pojo中需要定义成userId,在queryWrapper中的名称仍然为user_id
|
||||||
|
private Integer userId;
|
||||||
|
private String title;
|
||||||
|
private String description;
|
||||||
|
private String content;
|
||||||
|
// pojo中定义日期格式的注解:@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
// timezone 用于修改时区
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||||
|
private Date createtime;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||||
|
private Date modifytime;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.kob.backend.pojo;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Timer;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Record {
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private Integer aId;
|
||||||
|
private Integer aSx;
|
||||||
|
private Integer aSy;
|
||||||
|
private Integer bId;
|
||||||
|
private Integer bSx;
|
||||||
|
private Integer bSy;
|
||||||
|
|
||||||
|
private String aSteps;
|
||||||
|
private String bSteps;
|
||||||
|
private String map;
|
||||||
|
private String loser;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||||
|
private Date createtime;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//用于将 mysql 表 User 转换为 class
|
||||||
|
package com.kob.backend.pojo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Data 用于编译时自动生成 getter setter 方法;
|
||||||
|
NoArgsConstructor 用于生成无参构造函数;
|
||||||
|
AllArgsConstructor 用于生成所有参数构造函数.
|
||||||
|
*/
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
// 使用对象类型定义而不是 Int 防止 Mybatis 报错
|
||||||
|
// @TableId(type = IdType.AUTO) 用于 id 的自增
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Integer id;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String photo;
|
||||||
|
|
||||||
|
private Integer rating;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.kob.backend.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.kob.backend.mapper.UserMapper;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
//自定义 Spring-Starter-Security 登录信息; @Autowired 需要在外层加入 @Service 注解
|
||||||
|
@Service
|
||||||
|
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserMapper userMapper;
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
/*
|
||||||
|
从数据库里读取用户信息,比对用户名密码与数据库存在的用户名密码,信息一致,给用户发一个 sessionID ,
|
||||||
|
存到用户本地浏览器,服务器同时会自己存一份。未来用户再登录时服务器会将自己的 sessionID 与用户发过来
|
||||||
|
请求包含的 sessionID 进行比照,如果一致,则授权成功。
|
||||||
|
*/
|
||||||
|
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("username",username);
|
||||||
|
User user = userMapper.selectOne(queryWrapper);
|
||||||
|
if(user == null){
|
||||||
|
throw new RuntimeException("用户不存在");
|
||||||
|
}
|
||||||
|
return new UserDetailsImpl(user);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.kob.backend.service.impl.pk;
|
||||||
|
|
||||||
|
import com.kob.backend.consumer.WebSocketServer;
|
||||||
|
import com.kob.backend.service.pk.StartGameService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class StartGameServiceImpl implements StartGameService {
|
||||||
|
@Override
|
||||||
|
public String startGame(Integer aId, Integer bId) {
|
||||||
|
System.out.println("start game " + aId + " " + bId);
|
||||||
|
WebSocketServer.startGame(aId, bId); // 调用 WebSocketServer->startGame 函数
|
||||||
|
return "start game success";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.kob.backend.service.impl.user.account;
|
||||||
|
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import com.kob.backend.service.user.account.InfoService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class InfoServiceImpl implements InfoService {
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getInfo() {
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
|
UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal();
|
||||||
|
User user = loginUser.getUser();
|
||||||
|
|
||||||
|
Map<String,String> map = new HashMap<>();
|
||||||
|
map.put("error_message","success");
|
||||||
|
map.put("id",user.getId().toString());
|
||||||
|
map.put("username",user.getUsername());
|
||||||
|
map.put("photo",user.getPhoto());
|
||||||
|
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.kob.backend.service.impl.user.account;
|
||||||
|
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import com.kob.backend.service.user.account.LoginService;
|
||||||
|
import com.kob.backend.utils.JwtUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
//@Service 注解用于注入接口实现到 Spring 里面
|
||||||
|
@Service
|
||||||
|
public class LoginServiceImpl implements LoginService {
|
||||||
|
|
||||||
|
// @Autowired 用于注入
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getToken(String username, String password) {
|
||||||
|
|
||||||
|
// 封装用户名和密码成 UsernamePasswordAuthenticationToken 类:此类不存储明文,而是存储加密之后的字符串
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password);
|
||||||
|
|
||||||
|
// 验证是否正常登录,登录验证失败时会自动处理(报异常)
|
||||||
|
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
|
||||||
|
// 登录成功,取出用户
|
||||||
|
UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal();
|
||||||
|
User user = loginUser.getUser();
|
||||||
|
|
||||||
|
// 封装 jwt 信息:将 userID 转换为 String
|
||||||
|
String jwt = JwtUtil.createJWT(user.getId().toString());
|
||||||
|
|
||||||
|
/*
|
||||||
|
成功之后返回结果:
|
||||||
|
1. "error_message" 为 success;失败之后会报异常,自动处理掉
|
||||||
|
2. "token" 返回 jwt-token 信息
|
||||||
|
*/
|
||||||
|
Map<String,String> map = new HashMap<>();
|
||||||
|
map.put("error_message","success");
|
||||||
|
map.put("token",jwt);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package com.kob.backend.service.impl.user.account;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.mapper.UserMapper;
|
||||||
|
import com.kob.backend.service.user.account.RegisterService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RegisterServiceImpl implements RegisterService {
|
||||||
|
// 这里使用 @Autowired 注入是为了调用数据库,然后进行数据库查询,比较是否有用户名重复
|
||||||
|
@Autowired
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> register(String username, String password, String confirmedPassword) {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
// 如果用户名为空,则提示用户返回
|
||||||
|
if (username == null) {
|
||||||
|
map.put("error_message", "用户名不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
if (password == null || confirmedPassword == null) {
|
||||||
|
map.put("error_message", "密码不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
// 用户名需要将输入的首尾空格删掉
|
||||||
|
username = username.trim();
|
||||||
|
if (username.length() == 0) {
|
||||||
|
map.put("error_message", "用户名不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
if (password.length() == 0) {
|
||||||
|
map.put("error_message", "密码不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
if (username.length() > 100) {
|
||||||
|
map.put("error_message", "用户名过长");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
查询数据库里是否有用户名 this.username 已存在的用户,并将结果存入 users 中,
|
||||||
|
如果发现 users 不是空的,则告诉注册用户当前用户名已存在
|
||||||
|
*/
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
|
||||||
|
queryWrapper.eq("username", username);
|
||||||
|
List<User> users = userMapper.selectList(queryWrapper);
|
||||||
|
if (!users.isEmpty()) {
|
||||||
|
map.put("error_message", "用户名已存在");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
// 密码验证是 String 类型比较,应该用 equals() 方法
|
||||||
|
else if (!confirmedPassword.equals(password)) {
|
||||||
|
map.put("error_message", "两次密码输入不一致");
|
||||||
|
return map;
|
||||||
|
} else if (password.length() < 2) {
|
||||||
|
map.put("error_message", "密码不能少于2位");
|
||||||
|
return map;
|
||||||
|
} else if (password.length() > 100) {
|
||||||
|
map.put("error_message", "密码过长");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异常情况判断结束,开始将合法用户注册信息注入数据库
|
||||||
|
// 对密码进行加密
|
||||||
|
String encodedPassword = new BCryptPasswordEncoder().encode(password);
|
||||||
|
// 默认头像
|
||||||
|
String photo = "https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202302251824054.png";
|
||||||
|
// id 是数据库自增,这里生成新用户只需要将 id 参数写为 null 即可
|
||||||
|
User user = new User(null, username, encodedPassword, photo, 1500);
|
||||||
|
userMapper.insert(user);
|
||||||
|
|
||||||
|
map.put("error_message", "successRegister");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.kob.backend.service.impl.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.mapper.BotMapper;
|
||||||
|
import com.kob.backend.pojo.Bot;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import com.kob.backend.service.user.bot.AddService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AddServiceImpl implements AddService {
|
||||||
|
// 使用 @Autowired 注入接口,调用 BotMapper 来修改数据库
|
||||||
|
@Autowired
|
||||||
|
private BotMapper botMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> add(Map<String, String> data) {
|
||||||
|
// 取出 User
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal();
|
||||||
|
User user = loginUser.getUser();
|
||||||
|
|
||||||
|
String title = data.get("title");
|
||||||
|
String description = data.get("description");
|
||||||
|
String content = data.get("content");
|
||||||
|
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
|
||||||
|
if (title == null || title.length() == 0) {
|
||||||
|
map.put("error_message", "标题不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title.length() > 100) {
|
||||||
|
map.put("error_message", "标题长度不能大于100");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description == null || description.length() == 0) {
|
||||||
|
// 提供默认描述信息
|
||||||
|
description = "这个用户很懒,什么也没留下~";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description != null && description.length() > 300) {
|
||||||
|
map.put("error_message", "描述信息长度不能大于300");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content == null || content.length() == 0) {
|
||||||
|
map.put("error_message", "代码不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.length() > 10000) {
|
||||||
|
map.put("error_message", "代码长度不能超过一万");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义当前时间
|
||||||
|
Date now = new Date();
|
||||||
|
// 定义一个新 Bot ;创建时间和修改时间开始应该一样
|
||||||
|
Bot bot = new Bot(null, user.getId(), title, description, content, now, now);
|
||||||
|
|
||||||
|
// 将新建的 Bot 注入数据库中
|
||||||
|
botMapper.insert(bot);
|
||||||
|
map.put("error_message", "success");
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.kob.backend.service.impl.user.bot;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.kob.backend.mapper.BotMapper;
|
||||||
|
import com.kob.backend.pojo.Bot;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import com.kob.backend.service.user.bot.GetListService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class GetListServiceImpl implements GetListService {
|
||||||
|
@Autowired
|
||||||
|
private BotMapper botMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Bot> getList() {
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal();
|
||||||
|
User user = loginUser.getUser();
|
||||||
|
|
||||||
|
QueryWrapper<Bot> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("user_id",user.getId());
|
||||||
|
|
||||||
|
return botMapper.selectList(queryWrapper);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.kob.backend.service.impl.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.mapper.BotMapper;
|
||||||
|
import com.kob.backend.pojo.Bot;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import com.kob.backend.service.user.bot.RemoveService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RemoveServiceImpl implements RemoveService {
|
||||||
|
@Autowired
|
||||||
|
private BotMapper botMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> remove(Map<String, String> data) {
|
||||||
|
// 先取出当前用户信息
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal();
|
||||||
|
User user = loginUser.getUser();
|
||||||
|
|
||||||
|
// 从用户输入参数判断要删除的 bot id
|
||||||
|
int bot_id = Integer.parseInt(data.get("bot_id"));
|
||||||
|
Bot bot = botMapper.selectById(bot_id);
|
||||||
|
|
||||||
|
// 定义返回值
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
|
||||||
|
// 如果 bot 不存在
|
||||||
|
if (bot == null) {
|
||||||
|
map.put("error_message", "Bot 不存在或已被删除");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 bot 的 user_id 不等于 User 的 id
|
||||||
|
if (!bot.getUserId().equals(user.getId())) {
|
||||||
|
map.put("error_message", "你无权删除别人的 bot");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断成功后进行删除
|
||||||
|
botMapper.deleteById(bot_id);
|
||||||
|
map.put("error_message", "success");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.kob.backend.service.impl.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.mapper.BotMapper;
|
||||||
|
import com.kob.backend.pojo.Bot;
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||||
|
import com.kob.backend.service.user.bot.UpdateService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UpdateServiceImpl implements UpdateService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BotMapper botMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> update(Map<String, String> data) {
|
||||||
|
// 先获取当前用户,用于判断更新对象是否有权限
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal();
|
||||||
|
User user = loginUser.getUser();
|
||||||
|
|
||||||
|
int bot_id = Integer.parseInt(data.get("bot_id"));
|
||||||
|
|
||||||
|
String title = data.get("title");
|
||||||
|
String description = data.get("description");
|
||||||
|
String content = data.get("content");
|
||||||
|
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
|
||||||
|
if (title == null || title.length() == 0) {
|
||||||
|
map.put("error_message", "标题不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
if (title.length() > 100) {
|
||||||
|
map.put("error_message", "标题长度不能大于100");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
if (description == null || description.length() == 0) {
|
||||||
|
// 提供默认描述信息
|
||||||
|
description = "这个用户很懒,什么也没留下~";
|
||||||
|
}
|
||||||
|
if (description != null && description.length() > 300) {
|
||||||
|
map.put("error_message", "描述信息长度不能大于300");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
if (content == null || content.length() == 0) {
|
||||||
|
map.put("error_message", "代码不能为空");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
if (content.length() > 10000) {
|
||||||
|
map.put("error_message", "代码长度不能超过一万");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bot bot = botMapper.selectById(bot_id);
|
||||||
|
|
||||||
|
if (bot == null) {
|
||||||
|
map.put("error_message", "所查 bot 不存在或已被删除");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bot.getUserId().equals(user.getId())) {
|
||||||
|
map.put("error_message", "你无权更改别人的 bot");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot.getTitle().equals(title) && bot.getDescription().equals(description) && bot.getContent().equals(content)) {
|
||||||
|
map.put("error_message", "未作出修改");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date now = new Date();
|
||||||
|
Date createTime = bot.getCreatetime();
|
||||||
|
|
||||||
|
Bot newBot = new Bot(bot.getId(), user.getId(), title, description, content, createTime, now);
|
||||||
|
|
||||||
|
botMapper.updateById(newBot);
|
||||||
|
map.put("error_message", "success");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.kob.backend.service.impl.utils;
|
||||||
|
|
||||||
|
import com.kob.backend.pojo.User;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
//实现 UserDetails 接口:需要加上 lombok 的注解,来自动生成构造函数
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class UserDetailsImpl implements UserDetails {
|
||||||
|
|
||||||
|
// 定义一个 pojo 的 User 类对象
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 pojo -> User 类对象 user 的用户密码
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return user.getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 pojo -> User 类对象 user 的用户名
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return user.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户账号是否没有过期
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户是否没有被锁定
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户授权是否没有过期
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户是否被启用
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
// 匹配接口:配合 MatchingSystem 微服务进行使用
|
||||||
|
package com.kob.backend.service.pk;
|
||||||
|
|
||||||
|
public interface StartGameService {
|
||||||
|
// 参数是匹配的两位玩家的 id
|
||||||
|
String startGame(Integer aId, Integer bId);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.kob.backend.service.user.account;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
//根据令牌返回用户信息
|
||||||
|
public interface InfoService {
|
||||||
|
public Map<String,String> getInfo();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.kob.backend.service.user.account;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
//验证用户名密码,验证成功后返回jwt token(令牌)
|
||||||
|
public interface LoginService {
|
||||||
|
public Map<String,String> getToken(String username, String password);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.kob.backend.service.user.account;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
//注册账号
|
||||||
|
public interface RegisterService {
|
||||||
|
// confirmedPassword 密码确认
|
||||||
|
public Map<String,String> register(String username, String password, String confirmedPassword);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.kob.backend.service.user.bot;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface AddService {
|
||||||
|
public Map<String,String> add(Map<String,String> data);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.kob.backend.service.user.bot;
|
||||||
|
|
||||||
|
import com.kob.backend.pojo.Bot;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface GetListService {
|
||||||
|
List<Bot> getList(); // getList() 不需要传参数
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.kob.backend.service.user.bot;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
public interface RemoveService {
|
||||||
|
public Map<String,String> remove(Map<String,String> data);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.kob.backend.service.user.bot;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface UpdateService {
|
||||||
|
Map<String,String> update(Map<String,String> data);
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.kob.backend.utils;
|
||||||
|
|
||||||
|
//jwt工具类,用来创建、解析 jwt token
|
||||||
|
/*
|
||||||
|
主要作用:
|
||||||
|
1. 将字符串加上密钥,加上有效期,转换为加密后的字符串;
|
||||||
|
2. 给一个令牌,将其 userID 解析出来.
|
||||||
|
|
||||||
|
依赖:添加到 pom.xml 中,然后使用 Maven 重新加载依赖项
|
||||||
|
jjwt-apt;
|
||||||
|
jjwt-impl;
|
||||||
|
jjwt-jackson.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.JwtBuilder;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtUtil {
|
||||||
|
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天:单位-毫秒 ms
|
||||||
|
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232113afasdfad"; // 密钥:随机字符串-大小写英文字母+数字
|
||||||
|
|
||||||
|
public static String getUUID() {
|
||||||
|
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String createJWT(String subject) {
|
||||||
|
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
|
||||||
|
return builder.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
|
||||||
|
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
|
||||||
|
SecretKey secretKey = generalKey();
|
||||||
|
long nowMillis = System.currentTimeMillis();
|
||||||
|
Date now = new Date(nowMillis);
|
||||||
|
if (ttlMillis == null) {
|
||||||
|
ttlMillis = JwtUtil.JWT_TTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
long expMillis = nowMillis + ttlMillis;
|
||||||
|
Date expDate = new Date(expMillis);
|
||||||
|
return Jwts.builder()
|
||||||
|
.setId(uuid)
|
||||||
|
.setSubject(subject)
|
||||||
|
.setIssuer("sg")
|
||||||
|
.setIssuedAt(now)
|
||||||
|
.signWith(signatureAlgorithm, secretKey)
|
||||||
|
.setExpiration(expDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SecretKey generalKey() {
|
||||||
|
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
|
||||||
|
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Claims parseJWT(String jwt) throws Exception {
|
||||||
|
SecretKey secretKey = generalKey();
|
||||||
|
return Jwts.parserBuilder()
|
||||||
|
.setSigningKey(secretKey)
|
||||||
|
.build()
|
||||||
|
.parseClaimsJws(jwt)
|
||||||
|
.getBody();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#default server port
|
||||||
|
server.port=3000
|
||||||
|
|
||||||
|
#mysql database connect profile
|
||||||
|
spring.datasource.username=root
|
||||||
|
spring.datasource.password=mysqlPassword
|
||||||
|
spring.datasource.url=jdbc:mysql://localhost:3306/kob?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
|
||||||
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="text-align: center">
|
||||||
|
<img src="/image/img.png">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.kob.backend;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class BackendApplicationTests {
|
||||||
|
|
||||||
|
// 测试明文加密
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||||
|
System.out.println(passwordEncoder.encode("123"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.kob</groupId>
|
||||||
|
<artifactId>backendcloud</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.kob.matchingsystem</groupId>
|
||||||
|
<artifactId>matchingsystem</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 简化代码,可以帮助写一些构造函数, set() , get() 函数等-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.26</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 用于安全验证-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.kob.matchingsystem;
|
||||||
|
|
||||||
|
import com.kob.matchingsystem.service.impl.MatchingServiceImpl;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class MatchingSystemApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 启动 MatchingSystem 服务之前启动 MatchingPool 线程
|
||||||
|
MatchingServiceImpl.matchingPool.start(); // 启动匹配线程
|
||||||
|
|
||||||
|
SpringApplication.run(MatchingSystemApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.kob.matchingsystem.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RestTemplateConfig {
|
||||||
|
@Bean
|
||||||
|
public RestTemplate getRestTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 链接访问权限控制
|
||||||
|
package com.kob.matchingsystem.config;
|
||||||
|
|
||||||
|
/*
|
||||||
|
主要作用:放行登录、注册等接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
|
||||||
|
// TODO: 2023/2/19 WebSecurityConfigurerAdapter 已被弃用,注意替换
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
/*
|
||||||
|
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||||
|
用于配置公开链接
|
||||||
|
.hasIpAddress("127.0.0.1") 用于限制访问的IP地址, 127.0.0.1 意为只允许本地访问
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.csrf().disable()
|
||||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
.and()
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers("/player/add/", "/player/remove/").hasIpAddress("127.0.0.1")
|
||||||
|
.antMatchers(HttpMethod.OPTIONS).permitAll()
|
||||||
|
.anyRequest().authenticated();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.kob.matchingsystem.controller;
|
||||||
|
|
||||||
|
import com.kob.matchingsystem.service.MatchingService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class MatchingController {
|
||||||
|
// 注入接口
|
||||||
|
@Autowired
|
||||||
|
private MatchingService matchingService;
|
||||||
|
|
||||||
|
// 涉及到对数据的修改,使用 POST 请求
|
||||||
|
// MultiValueMap 每个关键字对应一个列表的值, Map 每个关键字只能对应一个单值
|
||||||
|
@PostMapping("/player/add/")
|
||||||
|
public String addPlayer(@RequestParam MultiValueMap<String, String> data) {
|
||||||
|
Integer userId = Integer.parseInt(Objects.requireNonNull(data.getFirst("user_id")));
|
||||||
|
Integer rating = Integer.parseInt(Objects.requireNonNull(data.getFirst("rating")));
|
||||||
|
return matchingService.addPlayer(userId, rating);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/player/remove/")
|
||||||
|
public String removePlayer(@RequestParam MultiValueMap<String, String> data) {
|
||||||
|
Integer userId = Integer.parseInt(Objects.requireNonNull(data.getFirst("user_id")));
|
||||||
|
return matchingService.removePlayer(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
// 定义接口
|
||||||
|
package com.kob.matchingsystem.service;
|
||||||
|
|
||||||
|
public interface MatchingService {
|
||||||
|
// 给匹配池添加一名玩家
|
||||||
|
String addPlayer(Integer userId, Integer rating);
|
||||||
|
|
||||||
|
// 从匹配池删除一名玩家
|
||||||
|
String removePlayer(Integer userId);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 实现接口
|
||||||
|
package com.kob.matchingsystem.service.impl;
|
||||||
|
|
||||||
|
import com.kob.matchingsystem.service.MatchingService;
|
||||||
|
import com.kob.matchingsystem.service.impl.utils.MatchingPool;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MatchingServiceImpl implements MatchingService {
|
||||||
|
// MatchingPool 全局只有一个线程,所以这里定义成静态
|
||||||
|
public final static MatchingPool matchingPool = new MatchingPool();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String addPlayer(Integer userId, Integer rating) {
|
||||||
|
System.out.println("add player: " + userId + " " + rating);
|
||||||
|
// 向匹配池添加一名玩家
|
||||||
|
matchingPool.addPlayer(userId, rating);
|
||||||
|
return "add player success";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String removePlayer(Integer userId) {
|
||||||
|
System.out.println("remove player: " + userId);
|
||||||
|
// 从匹配池移除一名玩家
|
||||||
|
matchingPool.removePlayer(userId);
|
||||||
|
return "remove player success";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
// 用来维护线程
|
||||||
|
package com.kob.matchingsystem.service.impl.utils;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MatchingPool extends Thread {
|
||||||
|
// 把所有用户存下来
|
||||||
|
private static List<Player> playerList = new ArrayList<>();
|
||||||
|
// 定义一个锁, Reentrant Lock 是可重入锁
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
// 定义微服务传值 URL
|
||||||
|
private static final String startGameUrl = "http://127.0.0.1:3000/pk/start/";
|
||||||
|
// 定义 RestTemplate 用来微服务数据通信
|
||||||
|
private static RestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private void setRestTemplate(RestTemplate restTemplate) {
|
||||||
|
MatchingPool.restTemplate = restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayer(Integer userId, Integer rating) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
// 一开始匹配等待时间是 0
|
||||||
|
playerList.add(new Player(userId, rating, 0));
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePlayer(Integer userId) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
// 先创建一个新列表,将没有删的用户先存下来
|
||||||
|
List<Player> newPlayerList = new ArrayList<>();
|
||||||
|
// 枚举所有的 player 并存入 newPlayerList
|
||||||
|
for (Player player : playerList) {
|
||||||
|
// 如果当前 player 的 id 不等于要删除的用户 id,则表示此用户保留,将要保留的用户信息存入新列表
|
||||||
|
// 否则不存入新列表
|
||||||
|
if (!player.getUserId().equals(userId))
|
||||||
|
newPlayerList.add(player);
|
||||||
|
}
|
||||||
|
// 将删除用户后的新 newPlayerList 赋值给 playerList
|
||||||
|
playerList = newPlayerList;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将所有玩家的等待时间 +1
|
||||||
|
private void increaseWaitingTime() {
|
||||||
|
for (Player player : playerList) {
|
||||||
|
player.setWaitingTime(player.getWaitingTime() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断两名玩家是否匹配
|
||||||
|
private boolean checkMatched(Player a, Player b) {
|
||||||
|
int ratingDelta = Math.abs(a.getRating() - b.getRating()); // 两名玩家的天梯积分之差
|
||||||
|
// a b 两名玩家的等待时间最小值
|
||||||
|
// min 是两方任意一方接受等待时间就可以匹配, max 是两方都接受的匹配
|
||||||
|
int minWaitingTime = Math.min(a.getWaitingTime(), b.getWaitingTime());
|
||||||
|
// 匹配结果可被 a 接受: a b 的分差小于等于 a和b 最小等待时间乘以 10
|
||||||
|
// 匹配结果可被 b 接受: a b 的分差小于等于 a和b 最小等待时间乘以 10
|
||||||
|
return ratingDelta <= minWaitingTime * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回 a 和 b 的匹配结果
|
||||||
|
private void sendResult(Player a, Player b) {
|
||||||
|
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
|
||||||
|
data.add("a_id", a.getUserId().toString());
|
||||||
|
data.add("b_id", b.getUserId().toString());
|
||||||
|
restTemplate.postForObject(startGameUrl, data, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试匹配所有玩家
|
||||||
|
private void matchPlayers() {
|
||||||
|
//TODO 后端调试
|
||||||
|
System.out.println("match players: " + playerList.toString());
|
||||||
|
// havaMatched 表示玩家已经匹配过了
|
||||||
|
boolean[] haveMatched = new boolean[playerList.size()];
|
||||||
|
for (int i = 0; i < playerList.size(); i++) {
|
||||||
|
if (haveMatched[i]) continue; // 如果当前枚举到的玩家已经匹配过了,则跳过改玩家
|
||||||
|
for (int j = i + 1; j < playerList.size(); j++) { // j 从 i+1 开始枚举
|
||||||
|
if (haveMatched[j]) continue;
|
||||||
|
// 如果 i 和 j 都没有匹配,则将 a 和 b 玩家取出来
|
||||||
|
Player a = playerList.get(i);
|
||||||
|
Player b = playerList.get(j);
|
||||||
|
// 判断 a 和 b 能否匹配:如果匹配,则将结果返回,并将 a 和 b 的位置置为 true
|
||||||
|
if (checkMatched(a, b)) {
|
||||||
|
haveMatched[i] = haveMatched[j] = true; // 置为已匹配
|
||||||
|
sendResult(a, b); // 返回匹配玩家结果
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配成功后,需要将匹配过的玩家从匹配池移除
|
||||||
|
List<Player> newPlayerList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < playerList.size(); i++) {
|
||||||
|
// 如果当前枚举到的玩家没有匹配,则将该玩家存入新的 list
|
||||||
|
if (!haveMatched[i])
|
||||||
|
newPlayerList.add(playerList.get(i));
|
||||||
|
}
|
||||||
|
// 将新的 newPlayerList 重新赋值 playerList
|
||||||
|
playerList = newPlayerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实现线程
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
// 每隔一秒钟调用“等待时间增加”函数
|
||||||
|
increaseWaitingTime();
|
||||||
|
// 尝试匹配玩家
|
||||||
|
matchPlayers();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// 如果捕获到异常,则输出异常并终止运行
|
||||||
|
e.printStackTrace();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// 存储玩家
|
||||||
|
package com.kob.matchingsystem.service.impl.utils;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Player {
|
||||||
|
private Integer userId;
|
||||||
|
private Integer rating;
|
||||||
|
private Integer waitingTime; // 等待时间
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
server.port=3001
|
|
@ -0,0 +1,316 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Maven Start Up Batch script
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# M2_HOME - location of maven2's installed home dir
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||||
|
. /usr/local/etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||||
|
else
|
||||||
|
export JAVA_HOME="/Library/Java/Home"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then
|
||||||
|
## resolve links - $0 may be a link to maven's home
|
||||||
|
PRG="$0"
|
||||||
|
|
||||||
|
# need this for relative symlinks
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG="`dirname "$PRG"`/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
saveddir=`pwd`
|
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/..
|
||||||
|
|
||||||
|
# make it fully qualified
|
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||||
|
|
||||||
|
cd "$saveddir"
|
||||||
|
# echo Using m2 at $M2_HOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`\\unset -f command; \\command -v java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=`cd "$wdir/.."; pwd`
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
echo "${basedir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
echo "$(tr -s '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
##########################################################################################
|
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||||
|
fi
|
||||||
|
if [ -n "$MVNW_REPOURL" ]; then
|
||||||
|
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
else
|
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
fi
|
||||||
|
while IFS="=" read key value; do
|
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||||
|
esac
|
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Downloading from: $jarUrl"
|
||||||
|
fi
|
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||||
|
if $cygwin; then
|
||||||
|
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v wget > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found wget ... using wget"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
else
|
||||||
|
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
fi
|
||||||
|
elif command -v curl > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found curl ... using curl"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
else
|
||||||
|
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Falling back to using Java to download"
|
||||||
|
fi
|
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||||
|
# For Cygwin, switch paths to Windows format before running javac
|
||||||
|
if $cygwin; then
|
||||||
|
javaClass=`cygpath --path --windows "$javaClass"`
|
||||||
|
fi
|
||||||
|
if [ -e "$javaClass" ]; then
|
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
# Compiling the Java class
|
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||||
|
fi
|
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
# Running the downloader
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Running MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
##########################################################################################
|
||||||
|
# End of extension
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo $MAVEN_PROJECTBASEDIR
|
||||||
|
fi
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
# work with both Windows and non-Windows executions.
|
||||||
|
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||||
|
export MAVEN_CMD_LINE_ARGS
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
$MAVEN_DEBUG_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.home=${M2_HOME}" \
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
|
@ -0,0 +1,188 @@
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Maven Start Up Batch script
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM M2_HOME - location of maven2's installed home dir
|
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
@REM e.g. to debug Maven itself, use
|
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM set title of command window
|
||||||
|
title %0
|
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@REM Execute a user defined script before this one
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||||
|
:skipRcPre
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||||
|
|
||||||
|
:endReadAdditionalConfig
|
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
|
||||||
|
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
if exist %WRAPPER_JAR% (
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Found %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
if not "%MVNW_REPOURL%" == "" (
|
||||||
|
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
)
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||||
|
echo Downloading from: %DOWNLOAD_URL%
|
||||||
|
)
|
||||||
|
|
||||||
|
powershell -Command "&{"^
|
||||||
|
"$webclient = new-object System.Net.WebClient;"^
|
||||||
|
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||||
|
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||||
|
"}"^
|
||||||
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||||
|
"}"
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Finished downloading %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@REM End of extension
|
||||||
|
|
||||||
|
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
@REM work with both Windows and non-Windows executions.
|
||||||
|
set MAVEN_CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% ^
|
||||||
|
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||||
|
%MAVEN_OPTS% ^
|
||||||
|
%MAVEN_DEBUG_OPTS% ^
|
||||||
|
-classpath %WRAPPER_JAR% ^
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||||
|
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||||
|
:skipRcPost
|
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||||
|
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||||
|
|
||||||
|
cmd /C exit /B %ERROR_CODE%
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.7.7</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.kob</groupId>
|
||||||
|
<artifactId>backendcloud</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>backendcloud</name>
|
||||||
|
<description>backendcloud</description>
|
||||||
|
<modules>
|
||||||
|
<module>matchingsystem</module>
|
||||||
|
<module>backend</module>
|
||||||
|
</modules>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<!-- springCloud 依赖-->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-dependencies</artifactId>
|
||||||
|
<version>2022.0.1</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
Loading…
Reference in New Issue