第58批续23:AI聊天400调试日志增强 + CreditManager GUI管理员直接操作 + 主菜单40号位入口
This commit is contained in:
@@ -392,6 +392,15 @@ public class AIChatManager {
|
|||||||
String jsonBody = buildChatRequest(modelFull, systemPrompt, prompt, endpoint.maxToken);
|
String jsonBody = buildChatRequest(modelFull, systemPrompt, prompt, endpoint.maxToken);
|
||||||
String requestUrl = buildRequestUrl(endpoint);
|
String requestUrl = buildRequestUrl(endpoint);
|
||||||
|
|
||||||
|
// ★ 400调试日志:请求发出前打印完整信息
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] === 请求开始 ===");
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 模型全名: " + modelFull);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 请求URL: " + requestUrl);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 自定义供应商: " + endpoint.isCustom);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 自动补齐开关: " + autoAppendCompletions);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 请求体JSON: " + jsonBody);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] === 请求结束 ===");
|
||||||
|
|
||||||
// 异步发送请求
|
// 异步发送请求
|
||||||
CompletableFuture.supplyAsync(() -> {
|
CompletableFuture.supplyAsync(() -> {
|
||||||
try {
|
try {
|
||||||
@@ -419,6 +428,19 @@ public class AIChatManager {
|
|||||||
|
|
||||||
if (response.statusCode() != 200) {
|
if (response.statusCode() != 200) {
|
||||||
String errBody = response.body();
|
String errBody = response.body();
|
||||||
|
// ★ 400调试日志:打印完整响应头和响应体
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] === 请求失败 ===");
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] HTTP状态码: " + response.statusCode());
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 模型全名: " + modelFull);
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 请求URL: " + requestUrl);
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 请求体JSON: " + jsonBody);
|
||||||
|
// 打印完整响应头
|
||||||
|
response.headers().map().forEach((name, values) -> {
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 响应头 " + name + ": " + String.join(", ", values));
|
||||||
|
});
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 响应体: " + errBody);
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] === 请求失败结束 ===");
|
||||||
|
|
||||||
handleRequestFailure(prompt, "HTTP " + response.statusCode() + ": " + errBody);
|
handleRequestFailure(prompt, "HTTP " + response.statusCode() + ": " + errBody);
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
player.sendMessage("§c[AI聊天] §f请求失败: HTTP " + response.statusCode());
|
player.sendMessage("§c[AI聊天] §f请求失败: HTTP " + response.statusCode());
|
||||||
@@ -621,6 +643,14 @@ public class AIChatManager {
|
|||||||
String jsonBody = buildChatRequest(defaultModelFull, systemPrompt, prompt, endpoint.maxToken);
|
String jsonBody = buildChatRequest(defaultModelFull, systemPrompt, prompt, endpoint.maxToken);
|
||||||
String requestUrl = buildRequestUrl(endpoint);
|
String requestUrl = buildRequestUrl(endpoint);
|
||||||
|
|
||||||
|
// ★ 400调试日志:自动互动请求
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] === 自动互动请求开始 ===");
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 模型全名: " + defaultModelFull);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 请求URL: " + requestUrl);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 自定义供应商: " + endpoint.isCustom);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] 请求体JSON: " + jsonBody);
|
||||||
|
plugin.getLogger().info("[AI聊天-调试] === 自动互动请求结束 ===");
|
||||||
|
|
||||||
CompletableFuture.supplyAsync(() -> {
|
CompletableFuture.supplyAsync(() -> {
|
||||||
try {
|
try {
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
@@ -638,6 +668,19 @@ public class AIChatManager {
|
|||||||
}
|
}
|
||||||
}).thenAccept(response -> {
|
}).thenAccept(response -> {
|
||||||
if (response == null || response.statusCode() != 200) {
|
if (response == null || response.statusCode() != 200) {
|
||||||
|
// ★ 400调试日志:自动互动失败
|
||||||
|
if (response != null) {
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] === 自动互动请求失败 ===");
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] HTTP状态码: " + response.statusCode());
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 模型全名: " + defaultModelFull);
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 请求URL: " + requestUrl);
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 请求体JSON: " + jsonBody);
|
||||||
|
response.headers().map().forEach((name, values) -> {
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 响应头 " + name + ": " + String.join(", ", values));
|
||||||
|
});
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] 响应体: " + response.body());
|
||||||
|
plugin.getLogger().warning("[AI聊天-调试] === 自动互动请求失败结束 ===");
|
||||||
|
}
|
||||||
handleRequestFailure("[自动] " + prompt.substring(0, Math.min(100, prompt.length())),
|
handleRequestFailure("[自动] " + prompt.substring(0, Math.min(100, prompt.length())),
|
||||||
"自动互动失败: HTTP " + (response != null ? response.statusCode() : "N/A"));
|
"自动互动失败: HTTP " + (response != null ? response.statusCode() : "N/A"));
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -61,13 +61,15 @@ public class CreditManager {
|
|||||||
ADD_SCORE_TARGET, // 加分 → 等待玩家名
|
ADD_SCORE_TARGET, // 加分 → 等待玩家名
|
||||||
ADD_SCORE_AMOUNT, // 加分 → 等待数值
|
ADD_SCORE_AMOUNT, // 加分 → 等待数值
|
||||||
DEDUCT_SCORE_TARGET, // 扣分 → 等待玩家名
|
DEDUCT_SCORE_TARGET, // 扣分 → 等待玩家名
|
||||||
DEDUCT_SCORE_AMOUNT // 扣分 → 等待数值
|
DEDUCT_SCORE_AMOUNT, // 扣分 → 等待数值
|
||||||
|
APPROVE_SCORE_CHANGE // 审批通过 → 等待积分变动值
|
||||||
}
|
}
|
||||||
|
|
||||||
// 玩家当前引导状态 (playerName -> FlowState + 临时数据)
|
// 玩家当前引导状态 (playerName -> FlowState + 临时数据)
|
||||||
private static class FlowData {
|
private static class FlowData {
|
||||||
FlowState state;
|
FlowState state;
|
||||||
String tempName; // 临时存储的目标玩家名
|
String tempName; // 临时存储的目标玩家名
|
||||||
|
int recordId; // 临时存储的审批记录ID
|
||||||
}
|
}
|
||||||
private final Map<String, FlowData> chatFlows = new ConcurrentHashMap<>();
|
private final Map<String, FlowData> chatFlows = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@@ -445,12 +447,63 @@ public class CreditManager {
|
|||||||
"§7内容: " + r.get("content"),
|
"§7内容: " + r.get("content"),
|
||||||
"§7来源: " + r.get("source"),
|
"§7来源: " + r.get("source"),
|
||||||
"",
|
"",
|
||||||
"§e/credits approve " + r.get("id") + " <通过|驳回> <理由> [积分变动]"));
|
"§e点击查看详情,右键通过,Shift+右键驳回"));
|
||||||
}
|
}
|
||||||
inv.setItem(53, mkItem(Material.ARROW, "§7返回"));
|
inv.setItem(53, mkItem(Material.ARROW, "§7返回"));
|
||||||
return inv;
|
return inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单条待审批记录详情 GUI — 含通过/驳回按钮
|
||||||
|
*/
|
||||||
|
public Inventory createPendingDetailGUI(Player p, int recordId) {
|
||||||
|
Inventory inv = Bukkit.createInventory(null, 9, GUI_TITLE + " - 审批详情");
|
||||||
|
fillBg(inv);
|
||||||
|
|
||||||
|
Map<String, Object> record = null;
|
||||||
|
for (Map<String, Object> r : getPendingRecords()) {
|
||||||
|
if (((Number) r.get("id")).intValue() == recordId) {
|
||||||
|
record = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (record == null) {
|
||||||
|
inv.setItem(3, mkItem(Material.BARRIER, "§c§l记录不存在", "§7该记录可能已被审批"));
|
||||||
|
inv.setItem(4, mkItem(Material.ARROW, "§7返回"));
|
||||||
|
return inv;
|
||||||
|
}
|
||||||
|
|
||||||
|
String type = (String) record.get("type");
|
||||||
|
Material mat = TYPE_GOOD.equals(type) ? Material.EMERALD : Material.REDSTONE;
|
||||||
|
String typeText = TYPE_GOOD.equals(type) ? "§a好人好事" : "§c举报";
|
||||||
|
|
||||||
|
// Slot 0: 通过按钮 (EMERALD)
|
||||||
|
inv.setItem(0, mkItem(Material.EMERALD,
|
||||||
|
"§a§l通过",
|
||||||
|
"§7点击通过此条上报",
|
||||||
|
"§7通过后需要输入积分变动值"));
|
||||||
|
|
||||||
|
// Slot 1: 驳回按钮 (REDSTONE)
|
||||||
|
inv.setItem(1, mkItem(Material.REDSTONE,
|
||||||
|
"§c§l驳回",
|
||||||
|
"§7点击驳回此条上报",
|
||||||
|
"§7驳回则不扣分不加分"));
|
||||||
|
|
||||||
|
// Slot 3: 记录详情
|
||||||
|
inv.setItem(3, mkItem(mat,
|
||||||
|
"§e§l#" + recordId + " " + record.get("target_player"),
|
||||||
|
"§7上报人: " + record.get("reporter_player"),
|
||||||
|
"§7类型: " + typeText,
|
||||||
|
"§7内容: " + record.get("content"),
|
||||||
|
"§7来源: " + record.get("source"),
|
||||||
|
"§7时间: " + formatTime((Long) record.get("created_at"))));
|
||||||
|
|
||||||
|
// Slot 4: 返回
|
||||||
|
inv.setItem(4, mkItem(Material.ARROW, "§7返回"));
|
||||||
|
|
||||||
|
return inv;
|
||||||
|
}
|
||||||
|
|
||||||
public Inventory createRecordsGUI(Player p) {
|
public Inventory createRecordsGUI(Player p) {
|
||||||
Inventory inv = Bukkit.createInventory(null, 54, GUI_TITLE + " - 历史记录");
|
Inventory inv = Bukkit.createInventory(null, 54, GUI_TITLE + " - 历史记录");
|
||||||
fillBg(inv);
|
fillBg(inv);
|
||||||
@@ -688,9 +741,80 @@ public class CreditManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 待审批GUI — 点击物品提示用命令审批
|
// 待审批GUI — 点击记录打开详情,右键直接通过,Shift+右键驳回
|
||||||
if (title != null && title.contains("待审批")) {
|
if (title != null && title.contains("待审批")) {
|
||||||
p.sendMessage("§c请使用 /credits approve <id> <通过|驳回> <理由> [积分变动]");
|
// 详情GUI的返回按钮
|
||||||
|
if (slot == 4) {
|
||||||
|
// 清除临时状态,避免拦截后续聊天
|
||||||
|
chatFlows.remove(p.getName());
|
||||||
|
p.closeInventory();
|
||||||
|
p.openInventory(createPendingGUI(p));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 详情GUI的按钮
|
||||||
|
if (title.contains("审批详情")) {
|
||||||
|
if (slot == 0) { // 通过
|
||||||
|
// 从当前打开的详情GUI中获取 recordId
|
||||||
|
// 需要从 GUI title 中解析,或者用 FlowData
|
||||||
|
// 简化:直接从 chatFlows 获取(如果有),否则提示
|
||||||
|
FlowData fd = chatFlows.get(p.getName());
|
||||||
|
if (fd == null) {
|
||||||
|
// 没有引导状态,需要从 inventory 中推断
|
||||||
|
// 改用另一种方式:在详情GUI打开时设置一个临时状态
|
||||||
|
p.sendMessage("§c§l[信誉积分] §c无法获取记录ID,请重新打开详情");
|
||||||
|
p.closeInventory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int rid = fd.recordId;
|
||||||
|
// 标记审批通过,等待输入积分变动
|
||||||
|
fd.state = FlowState.APPROVE_SCORE_CHANGE;
|
||||||
|
fd.tempName = null;
|
||||||
|
// 先查目标玩家名
|
||||||
|
String targetName = getRecordTarget(rid);
|
||||||
|
fd.tempName = targetName;
|
||||||
|
p.closeInventory();
|
||||||
|
p.sendMessage("§a§l[信誉积分] §f审批通过 #" + rid + " (目标: " + targetName + ")");
|
||||||
|
p.sendMessage("§7请输入积分变动值(正数加分,负数扣分,如 +10 或 -5)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (slot == 1) { // 驳回
|
||||||
|
FlowData fd = chatFlows.get(p.getName());
|
||||||
|
if (fd == null) {
|
||||||
|
p.sendMessage("§c§l[信誉积分] §c无法获取记录ID,请重新打开详情");
|
||||||
|
p.closeInventory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int rid = fd.recordId;
|
||||||
|
approveRecord(rid, false, "管理员GUI驳回", 0, p.getName());
|
||||||
|
chatFlows.remove(p.getName());
|
||||||
|
p.closeInventory();
|
||||||
|
p.sendMessage("§c§l[信誉积分] §f已驳回 #" + rid + " (不扣分不加分)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 列表GUI的返回按钮
|
||||||
|
if (slot == 53) {
|
||||||
|
p.closeInventory();
|
||||||
|
p.openInventory(createMainGUI(p));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 点击记录 → 打开详情GUI
|
||||||
|
if (slot >= 0 && slot < 49) {
|
||||||
|
List<Map<String, Object>> records = getPendingRecords();
|
||||||
|
if (slot < records.size()) {
|
||||||
|
Map<String, Object> r = records.get(slot);
|
||||||
|
int recordId = ((Number) r.get("id")).intValue();
|
||||||
|
// 先清除之前的引导状态,记录ID
|
||||||
|
chatFlows.remove(p.getName());
|
||||||
|
FlowData fd = new FlowData();
|
||||||
|
fd.state = FlowState.APPROVE_SCORE_CHANGE; // 占位,实际点击按钮时才用
|
||||||
|
fd.recordId = recordId;
|
||||||
|
chatFlows.put(p.getName(), fd);
|
||||||
|
p.closeInventory();
|
||||||
|
p.openInventory(createPendingDetailGUI(p, recordId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
p.closeInventory();
|
p.closeInventory();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -780,6 +904,23 @@ public class CreditManager {
|
|||||||
fd.state = FlowState.DEDUCT_SCORE_AMOUNT;
|
fd.state = FlowState.DEDUCT_SCORE_AMOUNT;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case APPROVE_SCORE_CHANGE:
|
||||||
|
try {
|
||||||
|
String input = msg.trim();
|
||||||
|
// 支持 +10 / -5 / 10 / -5 格式
|
||||||
|
int amount = Integer.parseInt(input.replaceFirst("^[+]", ""));
|
||||||
|
int rid = fd.recordId;
|
||||||
|
String targetName = fd.tempName;
|
||||||
|
// 通过审批并加分/扣分
|
||||||
|
approveRecord(rid, true, "管理员GUI审批通过", amount, p.getName());
|
||||||
|
chatFlows.remove(p.getName());
|
||||||
|
p.sendMessage("§a§l[信誉积分] §f已通过 #" + rid + " " + targetName + " §e"
|
||||||
|
+ (amount > 0 ? "+" : "") + amount);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
p.sendMessage("§c请输入数字(如 +10 或 -5)");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -823,4 +964,38 @@ public class CreditManager {
|
|||||||
public boolean approveFromPhp(int recordId, boolean approved, String reason, int scoreChange, String approverName) {
|
public boolean approveFromPhp(int recordId, boolean approved, String reason, int scoreChange, String approverName) {
|
||||||
return approveRecord(recordId, approved, reason, scoreChange, approverName);
|
return approveRecord(recordId, approved, reason, scoreChange, approverName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 辅助方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询审批记录的目标玩家名
|
||||||
|
*/
|
||||||
|
private String getRecordTarget(int recordId) {
|
||||||
|
try {
|
||||||
|
PreparedStatement ps = db.getConnection().prepareStatement(
|
||||||
|
"SELECT target_player FROM credit_records WHERE id = ?");
|
||||||
|
ps.setInt(1, recordId);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
String target = rs.getString("target_player");
|
||||||
|
ps.close();
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
ps.close();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间戳为可读字符串
|
||||||
|
*/
|
||||||
|
private String formatTime(long timestamp) {
|
||||||
|
if (timestamp <= 0) return "未知";
|
||||||
|
try {
|
||||||
|
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
return sdf.format(new java.util.Date(timestamp));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return String.valueOf(timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,6 +225,12 @@ public class GUIManager implements Listener {
|
|||||||
? "§e点击打开GUI"
|
? "§e点击打开GUI"
|
||||||
: "§e点击打开CLI"));
|
: "§e点击打开CLI"));
|
||||||
|
|
||||||
|
// ★ 信誉积分 - 40号位(领地系统旁边)
|
||||||
|
g.setItem(40, mkItem(Material.GOLD_INGOT,
|
||||||
|
"§d§l信誉积分",
|
||||||
|
"§7上报好人好事/举报违规",
|
||||||
|
"§7查看我的积分与明细"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===== Row0(0-8): 自定义菜单项 =====
|
// ===== Row0(0-8): 自定义菜单项 =====
|
||||||
|
|||||||
@@ -7885,6 +7885,14 @@ public class Main extends JavaPlugin
|
|||||||
// CLI模式
|
// CLI模式
|
||||||
Bukkit.dispatchCommand(p, "protect menu");
|
Bukkit.dispatchCommand(p, "protect menu");
|
||||||
}
|
}
|
||||||
|
} else if (slot == 40) {
|
||||||
|
// ★ 信誉积分入口
|
||||||
|
p.closeInventory();
|
||||||
|
if (creditManager != null) {
|
||||||
|
p.openInventory(creditManager.createMainGUI(p));
|
||||||
|
} else {
|
||||||
|
p.sendMessage("§c信誉积分模块未初始化");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user