rename com.imyeyu.server to com.imyeyu.api

This commit is contained in:
Timi
2025-07-22 15:26:14 +08:00
parent e816b885b2
commit 323e038e86
340 changed files with 1174 additions and 1175 deletions
@@ -0,0 +1,158 @@
package com.imyeyu.api.modules.forevermc.bean;
import com.imyeyu.api.modules.forevermc.entity.Server;
import jakarta.validation.constraints.Min;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.List;
/**
* 服务器状态报告请求
*
* @author 夜雨
* @since 2021-12-02 19:56
*/
@Data
public class ServerStatus implements Cloneable {
private String id;
/** true 为维护中 */
private boolean isDebugging;
/** TPS */
@Min(0)
private double tps;
/** 报告时间 */
private long reportAt;
/** 静态基本信息 */
private BaseInfo baseInfo;
/** 主世界状态 */
private List<WorldStatus> worldStatusList = new ArrayList<>();
/** JVM 状态 */
private JVM jvm = new JVM();
/** 在线列表 */
private List<String> onlineList = new ArrayList<>();
@Override
protected ServerStatus clone() throws CloneNotSupportedException {
ServerStatus clone = (ServerStatus) super.clone();
clone.id = id;
clone.isDebugging = isDebugging;
clone.tps = tps;
clone.reportAt = reportAt;
clone.baseInfo = baseInfo;
return clone;
}
/**
* 静态基本信息
*
* @author 夜雨
* @since 2025-01-19 10:25
*/
@Data
@EqualsAndHashCode(callSuper = true)
public static class BaseInfo extends Server implements Cloneable {
/** 核心 */
private String core;
/** 启动时间 */
private Long bootAt;
/** 最大在线数量 */
private Integer maxOnline;
/** 图标 Base64 */
private String icon;
/** 游戏版本 */
private String version;
// @Override
// protected BaseInfo clone() throws CloneNotSupportedException {
// BaseInfo clone = (BaseInfo) super.clone();
// clone.core = core;
// clone.bootAt = bootAt;
// }
}
/**
* JVM 状态
*
* @author 夜雨
* @since 2022-11-11 14:52
*/
@Data
public static class JVM {
/** JVM 名称 */
private String name;
/** CPU 已使用 */
private double cpuUsed;
/** 内存状态 */
private Memory memory;
/**
* 内存状态
*
* @author 夜雨
* @since 2022-11-15 09:54
*/
@Data
public static class Memory {
/** 已使用 */
private long used;
/** 已申请 */
private long committed;
/** 最大内存 */
private long max;
}
}
/**
* 世界时间
*
* @author 夜雨
* @since 2024-10-29 23:59
*/
@Data
public static class WorldStatus {
/** 世界名称 */
private String name;
/** 总时刻 */
private long ticks;
/** 总天数 */
private int day;
/** 时 */
private int hour;
/** 分 */
private int minute;
/** true 为正在下雨 */
private boolean isRaining;
/** true 为正在雷雨 */
private boolean isThundering;
}
}
@@ -0,0 +1,115 @@
package com.imyeyu.api.modules.forevermc.controller;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.api.modules.forevermc.bean.ServerStatus;
import com.imyeyu.api.modules.forevermc.service.ServerService;
import com.imyeyu.api.modules.minecraft.annotation.RequiredFMCServerToken;
import com.imyeyu.api.modules.minecraft.vo.server.ReportRequest;
import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.utils.Decoder;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* 服务器接口
*
* @author 夜雨
* @since 2024-08-06 17:20
*/
@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/fmc/server")
public class ServerController {
private final ServerService service;
/**
* 服务器状态报告,由 FMCServer 服务端插件发送
*
* @param request 服务器状态
*/
@RequiredFMCServerToken
@PostMapping("/report")
public void report(@NotNull @RequestBody ReportRequest request) {
service.report(request);
}
@AOPLog
@RequestRateLimit
@IgnoreGlobalReturn
@GetMapping("/icon/{serverId}")
public void icon(@PathVariable String serverId, HttpServletResponse resp) {
try {
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", -1);
byte[] bytes = Decoder.base64(service.getIcon(serverId));
String mimeType = new Tika().detect(bytes);
if (TimiJava.isNotEmpty(mimeType)) {
resp.setContentType(mimeType);
}
resp.getOutputStream().write(bytes);
resp.getOutputStream().flush();
resp.getOutputStream().close();
} catch (TimiException e) {
if (e.getCode() == TimiCode.RESULT_NULL) {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
} else {
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
}
} catch (Exception e) {
log.error("read attachment error", e);
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
}
}
/**
* 获取服务器状态列表
*
* @return 服务器状态列表
*/
@RequestRateLimit
@GetMapping("/list")
public List<ServerStatus> listServer() {
List<ServerStatus> result = new ArrayList<>();
List<ServerStatus> sourceList = service.listAll();
for (int i = 0; i < sourceList.size(); i++) {
ServerStatus targetStatus = new ServerStatus();
BeanUtils.copyProperties(sourceList.get(i), targetStatus);
if (targetStatus.getBaseInfo() != null) {
ServerStatus.BaseInfo targetBaseInfo = new ServerStatus.BaseInfo();
BeanUtils.copyProperties(targetStatus.getBaseInfo(), targetBaseInfo);
targetBaseInfo.setIcon(null);
targetStatus.setBaseInfo(targetBaseInfo);
}
result.add(targetStatus);
}
result.sort(Comparator.comparingInt(o -> o.getBaseInfo().getOrder()));
return result;
}
}
@@ -0,0 +1,57 @@
package com.imyeyu.api.modules.forevermc.entity;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.Destroyable;
import com.imyeyu.spring.entity.UUIDEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 服务器
*
* @author 夜雨
* @since 2025-01-27 20:36
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Server extends UUIDEntity implements Destroyable {
/**
* 类型
*
* @author 夜雨
* @since 2025-03-03 10:06
*/
public enum Type {
/** 官方原版 */
ORIGINAL,
/** 模组 */
MOD
}
/** 上级服务器 ID */
protected String pid;
/** 标题 */
protected String title;
/** 说明 */
protected String description;
/** 地址 */
protected String host;
/** 类型 */
protected Type type;
/** 排序 */
protected int order;
/** 客户端列表 */
@Transient
protected List<ServerClient> clientList;
}
@@ -0,0 +1,40 @@
package com.imyeyu.api.modules.forevermc.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.Entity;
import java.util.List;
/**
* 服务器客户端
*
* @author 夜雨
* @since 2025-01-27 20:40
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServerClient extends Entity {
/** 服务器 ID,关联 {@link Server#getId()} */
private String serverId;
/** 文件名 */
private String fileName;
/** 客户端版本 */
private String version;
/** 默认配置 */
private String defOption;
/** 文件大小 */
private Long size;
/** true 为已过时 */
private boolean isDeprecated;
@Transient
private List<ServerClientSrc> srcList;
}
@@ -0,0 +1,52 @@
package com.imyeyu.api.modules.forevermc.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.imyeyu.spring.entity.Entity;
/**
* 服务器客户端下载源
*
* @author 夜雨
* @since 2025-01-27 20:41
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServerClientSrc extends Entity {
/**
* 下载源类型
*
* @author 夜雨
* @since 2025-01-27 20:43
*/
public enum BizType {
/** Timi MongoDB */
TIMI_MONGO,
/** Timi 对象储存(未实现) */
TIMI_COS,
/** 第三方 URL */
URL
}
/** 客户端 ID,关联 {@link ServerClient#getId()} */
private Long clientId;
/** 服务器 ID,关联 {@link Server#getId()} */
private String serverId;
/** 下载源类型 */
private BizType bizType;
/** 下载源类型值 */
private String bizValue;
/** 排序 */
private int order;
/** true 为默认 */
private boolean isDefault;
}
@@ -0,0 +1,17 @@
package com.imyeyu.api.modules.forevermc.mapper;
import com.imyeyu.api.modules.forevermc.entity.ServerClient;
import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author 夜雨
* @since 2025-02-07 17:03
*/
public interface ServerClientMapper extends BaseMapper<ServerClient, Long> {
@Select("SELECT * FROM server_client WHERE server_id = #{serverId}" + NOT_DELETE)
List<ServerClient> selectByServerId(String serverId);
}
@@ -0,0 +1,17 @@
package com.imyeyu.api.modules.forevermc.mapper;
import com.imyeyu.api.modules.forevermc.entity.ServerClientSrc;
import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author 夜雨
* @since 2025-02-07 17:20
*/
public interface ServerClientSrcMapper extends BaseMapper<ServerClientSrc, Long> {
@Select("SELECT * FROM server_client_src WHERE client_id = #{clientId}" + NOT_DELETE + " ORDER BY order ASC")
List<ServerClientSrc> selectByClientId(Long clientId);
}
@@ -0,0 +1,11 @@
package com.imyeyu.api.modules.forevermc.mapper;
import com.imyeyu.api.modules.forevermc.entity.Server;
import com.imyeyu.spring.mapper.BaseMapper;
/**
* @author 夜雨
* @since 2025-01-27 20:44
*/
public interface ServerMapper extends BaseMapper<Server, String> {
}
@@ -0,0 +1,35 @@
package com.imyeyu.api.modules.forevermc.service;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.api.modules.forevermc.bean.ServerStatus;
import com.imyeyu.api.modules.minecraft.vo.server.ReportRequest;
import java.util.List;
/**
* Minecraft 服务
*
* @author 夜雨
* @since 2021-12-02 09:53
*/
public interface ServerService extends TimiJava {
/**
* 缓存服务器状态
*
* @param request 服务器状态
* @throws TimiException 服务异常
*/
void report(ReportRequest request);
String getIcon(String serverId);
/**
* 获取所有服务器状态列表(此处移除与服务端的通信令牌)
*
* @return 所有服务器状态列表
* @throws TimiException 服务异常
*/
List<ServerStatus> listAll();
}
@@ -0,0 +1,90 @@
package com.imyeyu.api.modules.forevermc.service.implement;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.api.modules.forevermc.bean.ServerStatus;
import com.imyeyu.api.modules.forevermc.entity.Server;
import com.imyeyu.api.modules.forevermc.entity.ServerClient;
import com.imyeyu.api.modules.forevermc.mapper.ServerClientMapper;
import com.imyeyu.api.modules.forevermc.mapper.ServerClientSrcMapper;
import com.imyeyu.api.modules.forevermc.mapper.ServerMapper;
import com.imyeyu.api.modules.forevermc.service.ServerService;
import com.imyeyu.api.modules.minecraft.vo.server.ReportRequest;
import com.imyeyu.utils.Time;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Minecraft 服务实现
*
* @author 夜雨
* @version 2021-12-02 09:56
*/
@Service
@EnableScheduling
@RequiredArgsConstructor
public class ServerServiceImplement implements ServerService {
private final ServerMapper serverMapper;
private final ServerClientMapper serverClientMapper;
private final ServerClientSrcMapper serverClientSrcMapper;
private final Map<String, ServerStatus> serverMap = new HashMap<>();
/** 周期性填充数据库的基本信息 */
@Scheduled(fixedRate = Time.S * 30)
private void fillBaseInfo() {
serverMap.entrySet().removeIf(entry -> entry.getValue() == null);
for (Map.Entry<String, ServerStatus> item : serverMap.entrySet()) {
if (item.getValue().getBaseInfo() == null) {
continue;
}
Server server = serverMapper.select(item.getKey());
TimiException.required(server, "not found server");
List<ServerClient> clientList = serverClientMapper.selectByServerId(server.getId());
for (int i = 0; i < clientList.size(); i++) {
ServerClient client = clientList.get(i);
client.setSrcList(serverClientSrcMapper.selectByClientId(client.getId()));
}
server.setClientList(clientList);
BeanUtils.copyProperties(server, item.getValue().getBaseInfo());
}
}
@Override
public void report(ReportRequest request) {
ServerStatus status = serverMap.get(request.getId());
if (status != null) {
request.setBaseInfo(TimiJava.firstNotNull(request.getBaseInfo(), status.getBaseInfo()));
if (request.getBaseInfo() == null || TimiJava.isEmpty(request.getBaseInfo().getCore())) {
// 需要报告基本信息,与调用方约定返回代码为 IGNORE
throw new TimiException(TimiCode.IGNORE);
}
}
serverMap.put(request.getId(), request);
}
@Override
public String getIcon(String serverId) {
ServerStatus status = serverMap.get(serverId);
if (status == null || status.getBaseInfo() == null || TimiJava.isEmpty(status.getBaseInfo().getIcon())) {
throw new TimiException(TimiCode.RESULT_NULL);
}
return status.getBaseInfo().getIcon();
}
@Override
public List<ServerStatus> listAll() {
return new ArrayList<>(serverMap.values());
}
}