Compare commits
13 Commits
8e9089d574
...
a6ee2ac518
| Author | SHA1 | Date | |
|---|---|---|---|
| a6ee2ac518 | |||
| 377ca8cba3 | |||
| 80111e7141 | |||
|
|
61dded61ed | ||
|
|
e382d5f6f7 | ||
|
|
117f7d222c | ||
|
|
a64da463ff | ||
| a3622b0e72 | |||
| 820a3615be | |||
|
|
7d6bd52e37 | ||
|
|
9d8fa35aad | ||
|
|
4054862c70 | ||
|
|
024a9672d5 |
31
.github/workflows/maven-publish.yml
vendored
Normal file
31
.github/workflows/maven-publish.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
name: Maven Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
server-id: github
|
||||
settings-path: ${{ github.workspace }}
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
|
||||
- name: Publish to GitHub Packages Apache Maven
|
||||
run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml -DaltDeploymentRepository=github::default::https://maven.pkg.github.com/${{ github.repository }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -33,3 +33,4 @@ build/
|
|||
.vscode/
|
||||
|
||||
out/
|
||||
src/main/resources/bak.yml
|
||||
|
|
|
|||
44
pom.xml
44
pom.xml
|
|
@ -10,14 +10,14 @@
|
|||
</parent>
|
||||
<groupId>com.linearpast</groupId>
|
||||
<artifactId>MinecraftManager</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
<name>MinecraftManager</name>
|
||||
<description>MinecraftManager</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<!-- 跳过测试 -->
|
||||
<skipTests>true</skipTests>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
<skipTests>false</skipTests>
|
||||
<maven.test.skip>false</maven.test.skip>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
@ -25,7 +25,11 @@
|
|||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 移除了spring-boot-starter-test依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
|
@ -38,7 +42,7 @@
|
|||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<version>1.18.38</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
@ -109,10 +113,30 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- 配置maven-compiler-plugin跳过测试 -->
|
||||
<!-- 配置maven-compiler-plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.38</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- 配置maven-surefire-plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- 排除需要数据库的 @SpringBootTest -->
|
||||
<excludes>
|
||||
<exclude>**/MinecraftManagerApplicationTests.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
|
|
@ -124,5 +148,11 @@
|
|||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>github</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/LeisureTimeDock/MinecraftLTDWhitelistSystem</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</project>
|
||||
|
|
@ -202,7 +202,7 @@ public class PlayerController {
|
|||
question.setOptions(result.toString());
|
||||
} else if(type == 2){
|
||||
String options = question.getOptions();
|
||||
String processed = options.replaceAll("\\$\\{.*?}", "\\${}");
|
||||
String processed = options.replaceAll("\\$(\\d+)?\\{.*?}", "\\${}");
|
||||
String[] parts = processed.split("(?<=\\$\\{})|(?=\\$\\{})");
|
||||
JsonArray result = new Gson().toJsonTree(Arrays.asList(parts)).getAsJsonArray();
|
||||
question.setOptions(result.toString());
|
||||
|
|
@ -280,20 +280,46 @@ public class PlayerController {
|
|||
List<String> resultList = new ArrayList<>();
|
||||
JsonArray asJsonArray = JsonParser.parseString(answer).getAsJsonArray();
|
||||
for (JsonElement element : asJsonArray) {
|
||||
resultList.add(element.getAsString());
|
||||
resultList.add(element.getAsString().trim());
|
||||
}
|
||||
String regex = "\\$\\{(.+?)}";
|
||||
String regex = "\\$(\\d+)?\\{(.+?)}";
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(questions.getOptions());
|
||||
List<String> result = new ArrayList<>();
|
||||
int score = 0;
|
||||
int i = 0;
|
||||
int j = 0; //没有标记分数的答对题目数
|
||||
int k = 0; //没有标记分数的答案数量
|
||||
//后续计算之后,表示所有未标记分数的答案的总分
|
||||
int maxScore = questions.getScore();
|
||||
while (matcher.find()) {
|
||||
result.add(matcher.group(1));
|
||||
String scoreString = matcher.group(1);
|
||||
String answerString = matcher.group(2);
|
||||
int parseInt = 0;
|
||||
try {parseInt = Integer.parseInt(scoreString);}
|
||||
catch (NumberFormatException ignored) {}
|
||||
//若没有标记分数,k++
|
||||
//若标记了分数,最大分数减去,后续计算未标记分数的答案的均分
|
||||
if(parseInt == 0) k++;
|
||||
else maxScore -= parseInt;
|
||||
//或逻辑
|
||||
if (Arrays.stream(answerString.split("\\|")).toList().contains(resultList.get(i))) {
|
||||
score += parseInt;
|
||||
if(parseInt == 0) j++;
|
||||
}
|
||||
if(resultList.equals(result)){
|
||||
playerAnswers.setScore(questions.getScore());
|
||||
}else {
|
||||
playerAnswers.setScore(0);
|
||||
i++;
|
||||
}
|
||||
if(k > 0) {
|
||||
//未标记分数的答案的每题平均分
|
||||
float averageScore = (float)maxScore / k;
|
||||
//答对的未标记 * 未标记的平均分 = 未标记的答对总分
|
||||
int rightScore;
|
||||
//如果答对的未标记 == 总未标记,直接给未标记的满分:为了弥补四舍五入带来的误差
|
||||
//否则直接给 未标记平均分 * 未标记答对
|
||||
if(j == k) rightScore = maxScore;
|
||||
else rightScore = Math.round(averageScore * j);
|
||||
score += rightScore;
|
||||
}
|
||||
playerAnswers.setScore(score);
|
||||
}
|
||||
answers.add(playerAnswers);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import org.springframework.data.domain.PageRequest;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/questions")
|
||||
|
|
@ -25,6 +27,10 @@ public class QuestionController {
|
|||
public Result<?> saveQuestion(@RequestBody QuestionSaveDTO questionSaveDTO) {
|
||||
if (questionSaveDTO != null) {
|
||||
Questions questions = new Questions();
|
||||
if(questionSaveDTO.getId() != null) questions.setId(questionSaveDTO.getId());
|
||||
questions.setTitle(questionSaveDTO.getTitle());
|
||||
questions.setScore(questionSaveDTO.getScore());
|
||||
questions.setType(questionSaveDTO.getType());
|
||||
if(questionSaveDTO.getType() == (byte) 1){
|
||||
JsonArray inputArray = new JsonArray();
|
||||
JsonArray correct = new JsonArray();
|
||||
|
|
@ -45,11 +51,20 @@ public class QuestionController {
|
|||
questions.setOptions(resultArray.toString());
|
||||
} else if (questionSaveDTO.getType() == (byte) 2) {
|
||||
questions.setOptions(questionSaveDTO.getBlankContent());
|
||||
Integer originScore = questions.getScore();
|
||||
String regex = "\\$(\\d+)\\{(.+?)}";
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(questions.getOptions());
|
||||
int score = 0;
|
||||
while (matcher.find()) {
|
||||
String scoreString = matcher.group(1);
|
||||
int parseInt = 0;
|
||||
try {parseInt = Integer.parseInt(scoreString);}
|
||||
catch (NumberFormatException ignored) {}
|
||||
score += parseInt;
|
||||
}
|
||||
if(score > originScore) questions.setScore(score);
|
||||
}
|
||||
if(questionSaveDTO.getId() != null) questions.setId(questionSaveDTO.getId());
|
||||
questions.setTitle(questionSaveDTO.getTitle());
|
||||
questions.setScore(questionSaveDTO.getScore());
|
||||
questions.setType(questionSaveDTO.getType());
|
||||
Questions result = questionsService.saveQuestions(questions);
|
||||
return result == null ? Result.error("服务器错误") : Result.success();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
package com.linearpast.minecraftmanager.controller;
|
||||
|
||||
import com.linearpast.minecraftmanager.entity.Operators;
|
||||
import com.linearpast.minecraftmanager.entity.Players;
|
||||
import com.linearpast.minecraftmanager.entity.view.PlayerInfoView;
|
||||
import com.linearpast.minecraftmanager.service.inter.PlayersService;
|
||||
import com.linearpast.minecraftmanager.utils.Result;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/whitelist")
|
||||
public class WhitelistController {
|
||||
|
||||
@Autowired
|
||||
private PlayersService playersService;
|
||||
|
||||
/**
|
||||
* 获取白名单列表(已通过的玩家,status=1)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<?> getWhitelist(
|
||||
@RequestParam(required = false) String playerName,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size
|
||||
) {
|
||||
Page<PlayerInfoView> players = playersService.getPlayers(
|
||||
playerName, null, null, (byte) 1,
|
||||
null, null, PageRequest.of(page - 1, size)
|
||||
);
|
||||
return Result.successPage(players.getContent(), players.getTotalElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待审核列表(status=2)
|
||||
*/
|
||||
@GetMapping("/pending")
|
||||
public Result<?> getPending(
|
||||
@RequestParam(required = false) String playerName,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size
|
||||
) {
|
||||
Page<PlayerInfoView> players = playersService.getPlayers(
|
||||
playerName, null, null, (byte) 2,
|
||||
null, null, PageRequest.of(page - 1, size)
|
||||
);
|
||||
return Result.successPage(players.getContent(), players.getTotalElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被拒绝列表(status=0)
|
||||
*/
|
||||
@GetMapping("/rejected")
|
||||
public Result<?> getRejected(
|
||||
@RequestParam(required = false) String playerName,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size
|
||||
) {
|
||||
Page<PlayerInfoView> players = playersService.getPlayers(
|
||||
playerName, null, null, (byte) 0,
|
||||
null, null, PageRequest.of(page - 1, size)
|
||||
);
|
||||
return Result.successPage(players.getContent(), players.getTotalElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过审核(加入白名单)
|
||||
*/
|
||||
@PostMapping("/approve/{id}")
|
||||
public Result<?> approve(@PathVariable Integer id, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayerStatus(id, (byte) 1, operators);
|
||||
return code > 0 ? Result.success().msg("已加入白名单") : Result.error("操作失败,Rcon连接错误或玩家不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝申请
|
||||
*/
|
||||
@PostMapping("/reject/{id}")
|
||||
public Result<?> reject(@PathVariable Integer id, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayerStatus(id, (byte) 0, operators);
|
||||
return code > 0 ? Result.success().msg("已拒绝申请") : Result.error("操作失败,Rcon连接错误或玩家不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
* 从白名单移除
|
||||
*/
|
||||
@DeleteMapping("/remove/{id}")
|
||||
public Result<?> removeFromWhitelist(@PathVariable Integer id) {
|
||||
return playersService.deletePlayer(id) ? Result.success().msg("已从白名单移除") : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量通过
|
||||
*/
|
||||
@PostMapping("/batchApprove")
|
||||
public Result<?> batchApprove(@RequestBody List<Integer> ids, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayersStatus(ids, (byte) 1, operators);
|
||||
return code > 0 ? Result.success().msg("成功处理:" + code + "/" + ids.size()) : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量拒绝
|
||||
*/
|
||||
@PostMapping("/batchReject")
|
||||
public Result<?> batchReject(@RequestBody List<Integer> ids, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayersStatus(ids, (byte) 0, operators);
|
||||
return code > 0 ? Result.success().msg("成功处理:" + code + "/" + ids.size()) : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量移除白名单
|
||||
*/
|
||||
@DeleteMapping("/batchRemove")
|
||||
public Result<?> batchRemove(@RequestBody List<Integer> ids) {
|
||||
int code = playersService.deletePlayers(ids);
|
||||
return code > 0 ? Result.success().msg("成功移除:" + code + "/" + ids.size()) : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取白名单统计信息
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public Result<?> getStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("approved", playersService.getPlayersCountByStatus((byte) 1));
|
||||
stats.put("pending", playersService.getPlayersCountByStatus((byte) 2));
|
||||
stats.put("rejected", playersService.getPlayersCountByStatus((byte) 0));
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询玩家白名单状态
|
||||
*/
|
||||
@GetMapping("/check/{playerName}")
|
||||
public Result<?> checkPlayer(@PathVariable String playerName) {
|
||||
Players player = playersService.getPlayer(playerName);
|
||||
if (player == null) {
|
||||
return Result.success(Map.of("exists", false));
|
||||
}
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("exists", true);
|
||||
info.put("status", player.getStatus());
|
||||
info.put("playerName", player.getPlayerName());
|
||||
info.put("qq", player.getQq());
|
||||
info.put("uuid", player.getUuid());
|
||||
String statusText = switch (player.getStatus()) {
|
||||
case 1 -> "已通过";
|
||||
case 2 -> "待审核";
|
||||
default -> "已拒绝";
|
||||
};
|
||||
info.put("statusText", statusText);
|
||||
return Result.success(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家得分
|
||||
*/
|
||||
@GetMapping("/score/{id}")
|
||||
public Result<?> getPlayerScore(@PathVariable Integer id) {
|
||||
Integer score = playersService.getPlayerScoreById(id);
|
||||
if (score == null) {
|
||||
return Result.error("玩家不存在");
|
||||
}
|
||||
return Result.success(Map.of("playerId", id, "totalScore", score));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.linearpast.minecraftmanager.interceptor;
|
||||
|
||||
import com.linearpast.minecraftmanager.exception.UnauthorizedException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
@Component
|
||||
public class ApiKeyInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Value("${api.key}")
|
||||
private String apiKey;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 1. 优先检查 API Key(Header: X-API-Key 或 Query: apiKey)
|
||||
String reqApiKey = request.getHeader("X-API-Key");
|
||||
if (!StringUtils.hasText(reqApiKey)) {
|
||||
reqApiKey = request.getParameter("apiKey");
|
||||
}
|
||||
if (StringUtils.hasText(reqApiKey) && apiKey.equals(reqApiKey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 回退到 Session 鉴权(浏览器访问)
|
||||
HttpSession session = request.getSession();
|
||||
if (session != null
|
||||
&& session.getAttribute("isLoggedIn") != null
|
||||
&& (boolean) session.getAttribute("isLoggedIn")
|
||||
&& session.getAttribute("adminAccount") != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new UnauthorizedException("redirect:/admin/login?error=please login first");
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
private AdminInterceptor adminInterceptor;
|
||||
@Autowired
|
||||
private PlayerInterceptor playerInterceptor;
|
||||
@Autowired
|
||||
private ApiKeyInterceptor apiKeyInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
|
|
@ -23,8 +25,11 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
"/admin/login/**",
|
||||
"/api/answer/**",
|
||||
"/api/confirm",
|
||||
"/api/region/findRegion"
|
||||
"/api/region/findRegion",
|
||||
"/api/whitelist/**"
|
||||
);
|
||||
registry.addInterceptor(apiKeyInterceptor)
|
||||
.addPathPatterns("/api/whitelist/**");
|
||||
registry.addInterceptor(playerInterceptor)
|
||||
.addPathPatterns(
|
||||
"/player/**",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.springframework.core.env.PropertySource;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConfigLoader implements EnvironmentPostProcessor {
|
||||
public static final Map<String, String> config = new HashMap<>();
|
||||
|
|
@ -20,7 +21,7 @@ public class ConfigLoader implements EnvironmentPostProcessor {
|
|||
).findFirst().orElseThrow();
|
||||
if(source instanceof MapPropertySource mapPropertySource) {
|
||||
for (String key : mapPropertySource.getPropertyNames()) {
|
||||
config.put(key, mapPropertySource.getProperty(key).toString());
|
||||
config.put(key, Objects.requireNonNull(mapPropertySource.getProperty(key)).toString());
|
||||
System.out.println(key + "=" + mapPropertySource.getProperty(key));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,17 +33,6 @@ import java.util.Map;
|
|||
|
||||
public class HttpUtils {
|
||||
|
||||
/**
|
||||
* get
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param querys
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static HttpResponse doGet(String host, String path, String method,
|
||||
Map<String, String> headers,
|
||||
Map<String, String> querys)
|
||||
|
|
@ -58,18 +47,6 @@ public class HttpUtils {
|
|||
return httpClient.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* post form
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param querys
|
||||
* @param bodys
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static HttpResponse doPost(String host, String path, String method,
|
||||
Map<String, String> headers,
|
||||
Map<String, String> querys,
|
||||
|
|
@ -96,18 +73,6 @@ public class HttpUtils {
|
|||
return httpClient.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post String
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param querys
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static HttpResponse doPost(String host, String path, String method,
|
||||
Map<String, String> headers,
|
||||
Map<String, String> querys,
|
||||
|
|
@ -127,18 +92,6 @@ public class HttpUtils {
|
|||
return httpClient.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post stream
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param querys
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static HttpResponse doPost(String host, String path, String method,
|
||||
Map<String, String> headers,
|
||||
Map<String, String> querys,
|
||||
|
|
@ -158,17 +111,6 @@ public class HttpUtils {
|
|||
return httpClient.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put String
|
||||
* @param host
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param querys
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static HttpResponse doPut(String host, String path, String method,
|
||||
Map<String, String> headers,
|
||||
Map<String, String> querys,
|
||||
|
|
@ -188,17 +130,6 @@ public class HttpUtils {
|
|||
return httpClient.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put stream
|
||||
* @param host
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param querys
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static HttpResponse doPut(String host, String path, String method,
|
||||
Map<String, String> headers,
|
||||
Map<String, String> querys,
|
||||
|
|
@ -218,17 +149,6 @@ public class HttpUtils {
|
|||
return httpClient.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param querys
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static HttpResponse doDelete(String host, String path, String method,
|
||||
Map<String, String> headers,
|
||||
Map<String, String> querys)
|
||||
|
|
@ -252,7 +172,7 @@ public class HttpUtils {
|
|||
if (null != querys) {
|
||||
StringBuilder sbQuery = new StringBuilder();
|
||||
for (Map.Entry<String, String> query : querys.entrySet()) {
|
||||
if (0 < sbQuery.length()) {
|
||||
if (!sbQuery.isEmpty()) {
|
||||
sbQuery.append("&");
|
||||
}
|
||||
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
|
||||
|
|
@ -266,7 +186,7 @@ public class HttpUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (0 < sbQuery.length()) {
|
||||
if (!sbQuery.isEmpty()) {
|
||||
sbUrl.append("?").append(sbQuery);
|
||||
}
|
||||
}
|
||||
|
|
@ -303,9 +223,7 @@ public class HttpUtils {
|
|||
ClientConnectionManager ccm = httpClient.getConnectionManager();
|
||||
SchemeRegistry registry = ccm.getSchemeRegistry();
|
||||
registry.register(new Scheme("https", ssf, 443));
|
||||
} catch (KeyManagementException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
} catch (KeyManagementException | NoSuchAlgorithmException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ public class ConnectTask implements Callable<MinecraftClient> {
|
|||
try {
|
||||
log.debug("Pausing for {} ms", this.connectOptions.getTimeBetweenRetries().toMillis());
|
||||
Thread.sleep(this.connectOptions.getTimeBetweenRetries().toMillis());
|
||||
} catch (InterruptedException var2) {
|
||||
InterruptedException e = var2;
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.linearpast.minecraftmanager.utils.rcon;
|
|||
import com.linearpast.minecraftmanager.utils.WhitelistTarget;
|
||||
import com.linearpast.minecraftmanager.utils.config.SelfConfig;
|
||||
import io.graversen.minecraft.rcon.commands.base.ICommand;
|
||||
import io.graversen.minecraft.rcon.util.Target;
|
||||
import io.graversen.minecraft.rcon.util.WhiteListModes;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
|
|
@ -10,19 +11,25 @@ import org.apache.commons.text.StringSubstitutor;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public record LoginWhitelistCommand(WhitelistTarget whitelistTarget, WhiteListModes whiteListMode) implements ICommand {
|
||||
public LoginWhitelistCommand(WhitelistTarget whitelistTarget, WhiteListModes whiteListMode) {
|
||||
this.whitelistTarget = whitelistTarget;
|
||||
this.whiteListMode = Objects.requireNonNull(whiteListMode);
|
||||
}
|
||||
//public record LoginWhitelistCommand(WhitelistTarget whitelistTarget, WhiteListModes whiteListMode) implements ICommand {
|
||||
// public LoginWhitelistCommand(WhitelistTarget whitelistTarget, WhiteListModes whiteListMode) {
|
||||
// this.whitelistTarget = whitelistTarget;
|
||||
// this.whiteListMode = Objects.requireNonNull(whiteListMode);
|
||||
// }
|
||||
//
|
||||
// public String command() {
|
||||
// return switch (this.whiteListMode()) {
|
||||
// case ADD -> StringSubstitutor.replace(SelfConfig.addCommand + " ${name} ${uuid}", Map.of(
|
||||
// "name", this.whitelistTarget().name(),
|
||||
// "uuid", this.whitelistTarget().uuid())
|
||||
// );
|
||||
// case REMOVE, LIST, OFF, ON, RELOAD -> "";
|
||||
// };
|
||||
// }
|
||||
//}
|
||||
|
||||
public String command() {
|
||||
return switch (this.whiteListMode()) {
|
||||
case ADD -> StringSubstitutor.replace(SelfConfig.addCommand + " ${name} ${uuid}", Map.of(
|
||||
"name", this.whitelistTarget().name(),
|
||||
"uuid", this.whitelistTarget().uuid())
|
||||
);
|
||||
case REMOVE, LIST, OFF, ON, RELOAD -> "";
|
||||
};
|
||||
public class LoginWhitelistCommand extends SelfWhiteListCommand {
|
||||
public LoginWhitelistCommand(WhitelistTarget target, WhiteListModes whiteListMode) {
|
||||
super(Target.player(target.name()), whiteListMode);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import java.util.concurrent.*;
|
|||
|
||||
import io.graversen.minecraft.rcon.service.ConnectOptions;
|
||||
import io.graversen.minecraft.rcon.service.*;
|
||||
import lombok.Setter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -22,6 +23,7 @@ public class MinecraftRconUtils {
|
|||
private final ScheduledExecutorService executorService;
|
||||
private volatile IMinecraftClient minecraftClient;
|
||||
private volatile MinecraftRcon minecraftRcon;
|
||||
@Setter
|
||||
private volatile boolean isConnected;
|
||||
private volatile CountDownLatch connectionLatch;
|
||||
|
||||
|
|
@ -108,9 +110,6 @@ public class MinecraftRconUtils {
|
|||
if(this.minecraftClient != null) this.minecraftRcon = new MinecraftRcon(this.minecraftClient);
|
||||
else this.minecraftRcon = null;
|
||||
}
|
||||
public void setConnected(boolean connected) {
|
||||
this.isConnected = connected;
|
||||
}
|
||||
|
||||
private class TestConnect implements Runnable {
|
||||
private final RconDetails rconDetails;
|
||||
|
|
|
|||
|
|
@ -9,11 +9,8 @@ spring:
|
|||
name: MinecraftManager
|
||||
datasource:
|
||||
url: jdbc:mysql://127.0.0.1:3306/minecraft_manager_ltd?useSSL=false
|
||||
username: root
|
||||
password: 3235566389lzh
|
||||
# url: jdbc:mysql://127.0.0.1:3308/minecraft_manager_ltd?useSSL=false
|
||||
# username: leisuretimedock
|
||||
# password: ltd20250706
|
||||
username: admin
|
||||
password: 123456
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
jpa:
|
||||
show-sql: true
|
||||
|
|
@ -29,8 +26,8 @@ spring:
|
|||
auth: true
|
||||
ssl:
|
||||
enable: true
|
||||
password: 1111
|
||||
username: email@163.com
|
||||
username: admin@163.com
|
||||
password: 123456789
|
||||
port: 465
|
||||
host: smtp.163.com
|
||||
|
||||
|
|
@ -44,5 +41,7 @@ minecraft:
|
|||
heart-time: 600
|
||||
test-cmd: ping
|
||||
add-cmd: login whitelist
|
||||
api:
|
||||
key: ${API_KEY:changeme-3944realms-whitelist-key}
|
||||
email:
|
||||
enable: false
|
||||
|
|
|
|||
BIN
src/main/resources/static/pic/1-1.png
Normal file
BIN
src/main/resources/static/pic/1-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 MiB |
|
|
@ -107,11 +107,16 @@
|
|||
<div class="timeline-badge primary"></div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>最近</span>
|
||||
<h6 class="mb-0">服务器迈入机械动力时代,而且是1.20.1……<img alt=""
|
||||
<h6 class="mb-0">8周目合拍照<img alt=""
|
||||
class="img-fluid w-100"
|
||||
src="/pic/1-1.png"></h6>
|
||||
<p class="mb-0">来也匆匆,去也匆匆</p>
|
||||
<h6 class="mb-0">8周目刚开始时<img alt=""
|
||||
class="img-fluid w-100"
|
||||
src="/pic/1_1.png"></h6>
|
||||
<p class="mb-0">终于玩上机械动力了/(ㄒoㄒ)/~~</p>
|
||||
<p class="mb-0">机械动力,太卡了XwX</p>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge info"></div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,366 @@
|
|||
package com.linearpast.minecraftmanager.controller;
|
||||
|
||||
import com.linearpast.minecraftmanager.entity.Operators;
|
||||
import com.linearpast.minecraftmanager.entity.Players;
|
||||
import com.linearpast.minecraftmanager.entity.view.PlayerInfoView;
|
||||
import com.linearpast.minecraftmanager.service.inter.PlayersService;
|
||||
import com.linearpast.minecraftmanager.utils.Result;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WhitelistControllerTest {
|
||||
|
||||
@Mock
|
||||
private PlayersService playersService;
|
||||
|
||||
@InjectMocks
|
||||
private WhitelistController controller;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
private MockHttpSession adminSession;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
|
||||
Operators admin = new Operators();
|
||||
admin.setId(1);
|
||||
admin.setUsername("testAdmin");
|
||||
|
||||
adminSession = new MockHttpSession();
|
||||
adminSession.setAttribute("adminAccount", admin);
|
||||
adminSession.setAttribute("isLoggedIn", true);
|
||||
}
|
||||
|
||||
// ==================== list ====================
|
||||
|
||||
@Test
|
||||
void getWhitelist_shouldReturnPage() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 1),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/list")
|
||||
.param("page", "1").param("size", "10"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.count").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhitelist_withPlayerName_shouldFilter() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(eq("testPlayer"), isNull(), isNull(), eq((byte) 1),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/list").param("playerName", "testPlayer"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== pending ====================
|
||||
|
||||
@Test
|
||||
void getPending_shouldReturnPage() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 2),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/pending"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== rejected ====================
|
||||
|
||||
@Test
|
||||
void getRejected_shouldReturnPage() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 0),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/rejected"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== approve ====================
|
||||
|
||||
@Test
|
||||
void approve_success() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), any(Operators.class))).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/1").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("已加入白名单"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void approve_failure() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(99), eq((byte) 1), any(Operators.class))).thenReturn(0);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/99").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
@Test
|
||||
void approve_withoutSession_passNullOperator() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), isNull())).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== reject ====================
|
||||
|
||||
@Test
|
||||
void reject_success() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 0), any(Operators.class))).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/reject/1").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("已拒绝申请"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reject_failure() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(99), eq((byte) 0), any(Operators.class))).thenReturn(0);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/reject/99").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== remove ====================
|
||||
|
||||
@Test
|
||||
void removeFromWhitelist_success() throws Exception {
|
||||
when(playersService.deletePlayer(1)).thenReturn(true);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/remove/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("已从白名单移除"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeFromWhitelist_failure() throws Exception {
|
||||
when(playersService.deletePlayer(99)).thenReturn(false);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/remove/99"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== batchApprove ====================
|
||||
|
||||
@Test
|
||||
void batchApprove_success() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2, 3);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(3);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchApprove")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2,3]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("成功处理:3/3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchApprove_partialSuccess() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2, 3);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(2);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchApprove")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2,3]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.msg").value("成功处理:2/3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchApprove_allFailed() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(0);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchApprove")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== batchReject ====================
|
||||
|
||||
@Test
|
||||
void batchReject_success() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 0), any(Operators.class))).thenReturn(2);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchReject")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== batchRemove ====================
|
||||
|
||||
@Test
|
||||
void batchRemove_success() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2, 3);
|
||||
when(playersService.deletePlayers(ids)).thenReturn(3);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/batchRemove")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2,3]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("成功移除:3/3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchRemove_allFailed() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2);
|
||||
when(playersService.deletePlayers(ids)).thenReturn(0);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/batchRemove")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== stats ====================
|
||||
|
||||
@Test
|
||||
void getStats_shouldReturnCounts() throws Exception {
|
||||
when(playersService.getPlayersCountByStatus((byte) 1)).thenReturn(10);
|
||||
when(playersService.getPlayersCountByStatus((byte) 2)).thenReturn(5);
|
||||
when(playersService.getPlayersCountByStatus((byte) 0)).thenReturn(3);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/stats"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.approved").value(10))
|
||||
.andExpect(jsonPath("$.data.pending").value(5))
|
||||
.andExpect(jsonPath("$.data.rejected").value(3));
|
||||
}
|
||||
|
||||
// ==================== check ====================
|
||||
|
||||
@Test
|
||||
void checkPlayer_exists() throws Exception {
|
||||
Players player = new Players();
|
||||
player.setPlayerName("Steve");
|
||||
player.setQq("12345");
|
||||
player.setUuid("uuid-123");
|
||||
player.setStatus((byte) 1);
|
||||
|
||||
when(playersService.getPlayer("Steve")).thenReturn(player);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/Steve"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.exists").value(true))
|
||||
.andExpect(jsonPath("$.data.status").value(1))
|
||||
.andExpect(jsonPath("$.data.statusText").value("已通过"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkPlayer_notExists() throws Exception {
|
||||
when(playersService.getPlayer("Notch")).thenReturn(null);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/Notch"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.exists").value(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkPlayer_pendingStatus() throws Exception {
|
||||
Players player = new Players();
|
||||
player.setStatus((byte) 2);
|
||||
|
||||
when(playersService.getPlayer("Newbie")).thenReturn(player);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/Newbie"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.statusText").value("待审核"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkPlayer_rejectedStatus() throws Exception {
|
||||
Players player = new Players();
|
||||
player.setStatus((byte) 0);
|
||||
|
||||
when(playersService.getPlayer("RejectedGuy")).thenReturn(player);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/RejectedGuy"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.statusText").value("已拒绝"));
|
||||
}
|
||||
|
||||
// ==================== score ====================
|
||||
|
||||
@Test
|
||||
void getPlayerScore_success() throws Exception {
|
||||
when(playersService.getPlayerScoreById(1)).thenReturn(85);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/score/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.playerId").value(1))
|
||||
.andExpect(jsonPath("$.data.totalScore").value(85));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlayerScore_notFound() throws Exception {
|
||||
when(playersService.getPlayerScoreById(999)).thenReturn(null);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/score/999"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500))
|
||||
.andExpect(jsonPath("$.msg").value("玩家不存在"));
|
||||
}
|
||||
|
||||
// ==================== session: null operator for API key calls ====================
|
||||
|
||||
@Test
|
||||
void approve_apiKeyCall_shouldPassNullOperator() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), isNull())).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package com.linearpast.minecraftmanager.interceptor;
|
||||
|
||||
import com.linearpast.minecraftmanager.entity.Operators;
|
||||
import com.linearpast.minecraftmanager.exception.UnauthorizedException;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class ApiKeyInterceptorTest {
|
||||
|
||||
private static final String VALID_API_KEY = "test-api-key-123";
|
||||
|
||||
private ApiKeyInterceptor interceptor;
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
interceptor = new ApiKeyInterceptor();
|
||||
ReflectionTestUtils.setField(interceptor, "apiKey", VALID_API_KEY);
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
}
|
||||
|
||||
// ==================== API Key Header ====================
|
||||
|
||||
@Test
|
||||
void validApiKeyInHeader_shouldPass() {
|
||||
request.addHeader("X-API-Key", VALID_API_KEY);
|
||||
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidApiKeyInHeader_shouldThrow() {
|
||||
request.addHeader("X-API-Key", "wrong-key");
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyApiKeyInHeader_shouldFallbackToSession() {
|
||||
request.addHeader("X-API-Key", "");
|
||||
|
||||
// no session, should throw
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
// ==================== API Key Query Param ====================
|
||||
|
||||
@Test
|
||||
void validApiKeyInQueryParam_shouldPass() {
|
||||
request.setParameter("apiKey", VALID_API_KEY);
|
||||
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidApiKeyInQueryParam_shouldThrow() {
|
||||
request.setParameter("apiKey", "wrong-key");
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void headerTakesPriorityOverQueryParam() {
|
||||
request.addHeader("X-API-Key", VALID_API_KEY);
|
||||
request.setParameter("apiKey", "wrong-key");
|
||||
|
||||
// header should be used and pass
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
// ==================== Session Fallback ====================
|
||||
|
||||
@Test
|
||||
void validAdminSession_shouldPass() {
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("isLoggedIn", true);
|
||||
session.setAttribute("adminAccount", new Operators());
|
||||
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionNotLoggedIn_shouldThrow() {
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("isLoggedIn", false);
|
||||
session.setAttribute("adminAccount", new Operators());
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionNoAdminAccount_shouldThrow() {
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("isLoggedIn", true);
|
||||
// no adminAccount set
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noSession_noApiKey_shouldThrow() {
|
||||
// no session created, no API key set
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSession_noApiKey_shouldThrow() {
|
||||
// explicitly test null session behavior
|
||||
// MockHttpServletRequest.getSession() returns non-null by default,
|
||||
// but we test invalid session attributes above which covers this path
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user