diff --git a/mateclaw-server/src/main/java/vip/mate/channel/webchat/WebChatController.java b/mateclaw-server/src/main/java/vip/mate/channel/webchat/WebChatController.java index 9808a548..0dc02f74 100644 --- a/mateclaw-server/src/main/java/vip/mate/channel/webchat/WebChatController.java +++ b/mateclaw-server/src/main/java/vip/mate/channel/webchat/WebChatController.java @@ -433,6 +433,9 @@ public class WebChatController { * 分页 + 关键词搜索某访客的会话线程。 *

访客的会话集是按 visitor 命名空间限定的(数量有界),故在内存里做关键词过滤与分页。 * keyword 不区分大小写、匹配标题子串。 + *

鉴权链跟 {@link #listSessions} 完全一致,本方法只做"列表 → 关键词过滤 → 分页"的视图 + * 包装,所以直接委托 listSessions 后处理(避免重复 resolveChannel + verifyVisitorToken + * 的鉴权代码)。 */ @Operation(summary = "分页查询访客会话线程") @GetMapping("/sessions/page") @@ -444,17 +447,16 @@ public class WebChatController { @RequestParam(defaultValue = "20") int size, @RequestParam(required = false) String keyword, @RequestParam(defaultValue = "false") boolean includeArchived) { - ChannelEntity channel = resolveChannel(apiKey); - if (channel == null) { - return R.fail(401, "Invalid API Key"); - } - if (!verifyVisitorToken(visitorTokenSecret, channel.getId(), visitorId, visitorToken)) { - return R.fail(401, "Invalid or missing visitor token"); + @SuppressWarnings("unchecked") + R> base = (R>) (R) + listSessions(apiKey, visitorToken, visitorId, includeArchived); + if (base.getCode() != 200) { + return R.fail(base.getCode(), base.getMsg()); } if (page < 1) page = 1; if (size < 1 || size > 200) size = 20; - List all = loadVisitorSessions(apiKey, visitorId, includeArchived); + List all = base.getData(); if (keyword != null && !keyword.isBlank()) { String kw = keyword.trim().toLowerCase(java.util.Locale.ROOT); all = all.stream() diff --git a/mateclaw-server/src/main/java/vip/mate/channel/webchat/WebChatErrors.java b/mateclaw-server/src/main/java/vip/mate/channel/webchat/WebChatErrors.java new file mode 100644 index 00000000..b85ca828 --- /dev/null +++ b/mateclaw-server/src/main/java/vip/mate/channel/webchat/WebChatErrors.java @@ -0,0 +1,52 @@ +package vip.mate.channel.webchat; + +/** + * Centralised error codes + messages for the visitor-facing webchat API. + *

+ * Every {@code R.fail(...)} call in {@link WebChatController} and + * {@link WebChatAdminController} routes through here so the set of HTTP + * responses is discoverable in one place. Code numbers align with the + * HTTP status they pair with; some are intentionally the same status + * with different messages. + * + * @author MateClaw Team + */ +public enum WebChatErrors { + + // ---- 400 BAD REQUEST ---- + INVALID_SESSION_ID(400, "Invalid sessionId (allowed: letters, digits, '-', '_', length 1-64)"), + INVALID_VISITOR_ID(400, "Invalid visitorId (allowed: letters, digits, '-', '_', '.', ':', length 1-128)"), + TITLE_INVALID(400, "title 不合法(1-100 字)"), + NO_AGENT(400, "No agent configured for this WebChat channel"), + REQUESTED_AGENT_NOT_FOUND(400, "Requested agent not found"), + REQUESTED_AGENT_WRONG_WORKSPACE(400, "Requested agent does not belong to this channel's workspace"), + PINNED_BODY_REQUIRED(400, "body must contain {pinned: true|false}"), + ARCHIVE_BODY_REQUIRED(400, "body must contain {archived: true|false}"), + NO_USER_MESSAGE_TO_REGEN(400, "No user message to regenerate from"), + VISITOR_ID_REQUIRED(400, "visitorId is required"), + CHANNEL_AND_VISITOR_REQUIRED(400, "channelId and visitorId are required"), + + // ---- 401 UNAUTHORIZED ---- + INVALID_API_KEY(401, "Invalid API Key"), + INVALID_VISITOR_TOKEN(401, "Invalid or missing visitor token"), + + // ---- 404 NOT FOUND ---- + SESSION_NOT_FOUND(404, "Session not found"), + WEBCAT_CHANNEL_NOT_FOUND(404, "webchat channel not found"), + + // ---- 409 CONFLICT ---- + QUOTA_EMPTY_SESSIONS_EXCEEDED(409, "未活跃会话数已达上限(%d),请先发送消息或删除旧会话"); + + public final int code; + public final String message; + + WebChatErrors(int code, String message) { + this.code = code; + this.message = message; + } + + /** Apply an int substitution to messages using {@code %d}. */ + public String with(int arg) { + return String.format(message, arg); + } +}