# MC 插件对接指南(外部 API) 本文面向 Bukkit/Paper/Spigot 插件开发者,演示如何用 Java 对接 MC Report 外部 API(提交举报、同步封禁、查询进度)。完整接口定义见 [EXTERNAL-API.md](EXTERNAL-API.md)。 > 仓库不再内置插件代码 —— 按本指南即可自行实现;凭据(ID+Secret)在站点后台「外部API」页创建。 --- ## 一、鉴权流程(Java 实现) 外部 API 用 **ID + Secret 换取 SESSION**,之后所有请求带 `Authorization: Bearer `。 ```java public class McReportApi { private final String baseUrl; // 如 https://mc.example.com/api/external private final String clientId; // 后台创建: 16 位数字 private final String clientSecret; // 后台创建: 32 位随机串 private String sessionToken; private long sessionExpiresAt; // 到期时间戳(ms) public McReportApi(String baseUrl, String clientId, String clientSecret) { this.baseUrl = baseUrl; this.clientId = clientId; this.clientSecret = clientSecret; } /** 获取有效 SESSION, 过期则自动换新 */ public synchronized String session() throws Exception { if (sessionToken != null && System.currentTimeMillis() < sessionExpiresAt) { return sessionToken; } JSONObject body = new JSONObject(); HttpURLConnection conn = open("POST", "/auth/session", null); conn.setRequestProperty("x-api-client-id", clientId); conn.setRequestProperty("x-api-secret", clientSecret); conn.setDoOutput(true); conn.getOutputStream().write(body.toString().getBytes(StandardCharsets.UTF_8)); JSONObject resp = readJson(conn); sessionToken = resp.getString("session_token"); sessionExpiresAt = System.currentTimeMillis() + resp.getLong("expires_in") * 1000L - 60_000L; // 提前1分钟过期 return sessionToken; } private HttpURLConnection open(String method, String path, String token) throws Exception { HttpURLConnection conn = (HttpURLConnection) new URL(baseUrl + path).openConnection(); conn.setRequestMethod(method); conn.setConnectTimeout(5000); conn.setReadTimeout(10000); conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("User-Agent", "MCReportPlugin/1.0"); if (token != null) conn.setRequestProperty("Authorization", "Bearer " + token); return conn; } private JSONObject readJson(HttpURLConnection conn) throws Exception { int code = conn.getResponseCode(); InputStream in = code >= 200 && code < 300 ? conn.getInputStream() : conn.getErrorStream(); String text = new String(in.readAllBytes(), StandardCharsets.UTF_8); JSONObject json = new JSONObject(text); if (code >= 400) throw new RuntimeException(json.optString("error", "HTTP " + code)); return json; } } ``` ## 二、提交举报工单 ```java /** 玩家在游戏内执行 /report 命令时调用 */ public JSONObject submitReport(String reporterName, String targetName, String reason, String serverName, String description) throws Exception { String token = session(); JSONObject body = new JSONObject(); body.put("type", "report"); body.put("title", "游戏内举报: " + targetName); body.put("reporter_game_name", reporterName); body.put("target_game_name", targetName); body.put("reason", reason); body.put("description", description); body.put("server", serverName); // 子服别名或「分组/子服」 HttpURLConnection conn = open("POST", "/tickets", token); conn.setDoOutput(true); conn.getOutputStream().write(body.toString().getBytes(StandardCharsets.UTF_8)); JSONObject resp = readJson(conn); // resp: {"id": 123, "tracking_token": "..."} return resp; } ``` **返回的 `tracking_token` 请保存**(例如写进玩家持久数据),玩家可用它查询处理进度: ```java public JSONObject trackTicket(String trackingToken) throws Exception { return readJson(open("GET", "/tickets/track?token=" + URLEncoder.encode(trackingToken, "UTF-8"), session())); } ``` ## 三、拉取/同步封禁 ```java /** 定时任务: 每 5 分钟同步一次本服生效封禁 */ public JSONArray fetchActiveBans(String serverName) throws Exception { String token = session(); String path = "/bans?status=active&limit=500" + (serverName != null && !serverName.isEmpty() ? "&server=" + URLEncoder.encode(serverName, "UTF-8") : ""); return readJson(open("GET", path, token)).getJSONArray("data"); } /** 服务器内封禁玩家后同步到系统 */ public JSONObject createBan(String playerName, String reason, String type, String duration, String serverName) throws Exception { JSONObject body = new JSONObject(); body.put("player_name", playerName); body.put("reason", reason); body.put("type", type); // ban / mute / warn body.put("duration", duration); // 如 "7d", 空=永久 body.put("server", serverName); return readJson(open("POST", "/bans", session())); // 需带 body } ``` > 注意:`POST /bans` 需设置 `setDoOutput(true)` 并写入 body(与 submitReport 相同写法)。 ## 四、配置 `plugins/<你的插件>/config.yml`: ```yaml # MC Report 外部 API 配置 api_url: "https://mc.example.com/api/external" client_id: "1829473056482917" # 后台「外部API」创建 client_secret: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" server_name: "survival" # 本服别名(后台服务器管理配置) ``` --- ## 常见问题 | 问题 | 处理 | |------|------| | `401` | ID/Secret 错误或客户端被停用;检查后台「外部API」 | | `401 SESSION 无效或已过期` | 重新调用 `/auth/session` 换取(示例代码已自动处理) | | `400 待处理工单已达上限` | 同一玩家待处理工单最多 5 个 | | 换服/改名 | 用 `server` 参数按子服隔离数据;玩家身份以 `game_name` 为准 |