rename com.imyeyu.server to com.imyeyu.api
This commit is contained in:
@@ -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();
|
||||
}
|
||||
+90
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user