创建cloud父组件,并将项目改为主服务+微服务MatchingSystem
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
+73
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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();
|
||||
}
|
||||
}
|
||||
+25
@@ -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);
|
||||
}
|
||||
}
|
||||
+72
@@ -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";
|
||||
}
|
||||
}
|
||||
+20
@@ -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();
|
||||
}
|
||||
}
|
||||
+27
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -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);
|
||||
}
|
||||
}
|
||||
+22
@@ -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();
|
||||
}
|
||||
}
|
||||
+20
@@ -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);
|
||||
}
|
||||
}
|
||||
+20
@@ -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;
|
||||
|
||||
}
|
||||
+35
@@ -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);
|
||||
}
|
||||
}
|
||||
+15
@@ -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";
|
||||
}
|
||||
}
|
||||
+34
@@ -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;
|
||||
}
|
||||
}
|
||||
+50
@@ -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;
|
||||
}
|
||||
}
|
||||
+85
@@ -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;
|
||||
}
|
||||
}
|
||||
+78
@@ -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;
|
||||
}
|
||||
}
|
||||
+33
@@ -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);
|
||||
}
|
||||
}
|
||||
+53
@@ -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;
|
||||
}
|
||||
}
|
||||
+90
@@ -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;
|
||||
}
|
||||
}
|
||||
+61
@@ -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);
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.kob.backend.service.user.account;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
//根据令牌返回用户信息
|
||||
public interface InfoService {
|
||||
public Map<String,String> getInfo();
|
||||
}
|
||||
+8
@@ -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);
|
||||
}
|
||||
+9
@@ -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);
|
||||
}
|
||||
+9
@@ -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>
|
||||
Reference in New Issue
Block a user