diff --git a/src/main/java/com/tailbet/common/constant/OpenImConstants.java b/src/main/java/com/tailbet/common/constant/OpenImConstants.java
new file mode 100644
index 0000000..1478e0d
--- /dev/null
+++ b/src/main/java/com/tailbet/common/constant/OpenImConstants.java
@@ -0,0 +1,284 @@
+package com.tailbet.common.constant;
+
+/**
+ * OpenIM 协议相关常量
+ *
+ * 统一管理 Webhook 回调命令、请求字段、响应字段、消息类型等协议魔法值,
+ * 避免散落在 Controller / Service 中。
+ *
+ *
+ * @author socialapp团队
+ * @since 1.0.7
+ */
+public final class OpenImConstants {
+
+ private OpenImConstants() {
+ }
+
+ // ==================== Webhook 回调命令 ====================
+
+ /** 单聊消息发送后回调 */
+ public static final String CALLBACK_AFTER_SEND_SINGLE_MSG = "callbackAfterSendSingleMsgCommand";
+
+ /** 群聊消息发送后回调 */
+ public static final String CALLBACK_AFTER_SEND_GROUP_MSG = "callbackAfterSendGroupMsgCommand";
+
+ /** 成员进群后回调 */
+ public static final String CALLBACK_AFTER_MEMBER_ENTER_GROUP = "callbackAfterMemberEnterGroupCommand";
+
+ /** 成员退群后回调 */
+ public static final String CALLBACK_AFTER_MEMBER_QUIT_GROUP = "callbackAfterMemberQuitGroupCommand";
+
+ /** 用户上线回调 */
+ public static final String CALLBACK_AFTER_USER_ONLINE = "callbackAfterUserOnlineCommand";
+
+ /** 用户下线回调 */
+ public static final String CALLBACK_AFTER_USER_OFFLINE = "callbackAfterUserOfflineCommand";
+
+ // ==================== 请求头 / JSON 字段名 ====================
+
+ /** OpenIM Webhook 鉴权请求头名 */
+ public static final String HEADER_TOKEN = "token";
+
+ /** 发送方用户 ID */
+ public static final String FIELD_SEND_ID = "sendID";
+
+ /** 接收方用户 ID */
+ public static final String FIELD_RECV_ID = "recvID";
+
+ /** 群组 ID(OpenIM 群 ID) */
+ public static final String FIELD_GROUP_ID = "groupID";
+
+ /** 用户 ID */
+ public static final String FIELD_USER_ID = "userID";
+
+ /** 平台 ID */
+ public static final String FIELD_PLATFORM_ID = "platformID";
+
+ /** 消息内容 */
+ public static final String FIELD_CONTENT = "content";
+
+ /** 消息内容类型 */
+ public static final String FIELD_CONTENT_TYPE = "contentType";
+
+ /** 客户端消息 ID */
+ public static final String FIELD_CLIENT_MSG_ID = "clientMsgID";
+
+ /** 服务端消息 ID */
+ public static final String FIELD_SERVER_MSG_ID = "serverMsgID";
+
+ // ==================== Webhook 成功响应 ====================
+
+ /** 响应字段:动作码 */
+ public static final String RESP_ACTION_CODE = "actionCode";
+
+ /** 响应字段:错误码 */
+ public static final String RESP_ERR_CODE = "errCode";
+
+ /** 响应字段:错误信息 */
+ public static final String RESP_ERR_MSG = "errMsg";
+
+ /** 响应字段:错误详情 */
+ public static final String RESP_ERR_DLT = "errDlt";
+
+ /** 响应字段:下一动作码 */
+ public static final String RESP_NEXT_CODE = "nextCode";
+
+ /** OpenIM 成功码(actionCode / errCode / nextCode) */
+ public static final int RESP_CODE_SUCCESS = 0;
+
+ /** OpenIM 成功响应空字符串字段值 */
+ public static final String RESP_EMPTY_MSG = "";
+
+ // ==================== Token / 鉴权错误码(OpenIM 官方) ====================
+
+ /** Token 过期 */
+ public static final int ERR_TOKEN_EXPIRED = 1501;
+
+ /** Token 无效 */
+ public static final int ERR_TOKEN_INVALID = 1502;
+
+ /** Token 格式错误 */
+ public static final int ERR_TOKEN_MALFORMED = 1503;
+
+ /** Token 不存在 */
+ public static final int ERR_TOKEN_NOT_EXIST = 1504;
+
+ /** Token 平台不一致 */
+ public static final int ERR_TOKEN_DIFFERENT_PLATFORM = 1505;
+
+ /** Token 被踢(多端/多实例刷新 adminToken、重复 get_user_token 等) */
+ public static final int ERR_TOKEN_KICKED = 1506;
+
+ /** Token 尚未生效 */
+ public static final int ERR_TOKEN_NOT_VALID_YET = 1507;
+
+ /**
+ * 服务端代操作使用的平台 ID(Web)。
+ * 与 App 端 iOS/Android 平台隔离,避免顶掉用户已登录 session。
+ */
+ public static final int PLATFORM_ID_SERVER_WEB = 5;
+
+ /** App 端 iOS 平台 ID */
+ public static final int PLATFORM_ID_IOS = 1;
+
+ /** App 端 Android 平台 ID */
+ public static final int PLATFORM_ID_ANDROID = 2;
+
+ // ==================== 会话摘要 / 未读 ====================
+
+ /** 会话 lastMessage 摘要最大长度 */
+ public static final int CONVERSATION_CONTENT_MAX_LEN = 100;
+
+ /** 未读数自增 SQL 片段 */
+ public static final String SQL_UNREAD_COUNT_INCREMENT = "unread_count = unread_count + 1";
+
+ /** 新建会话初始未读数(接收方) */
+ public static final int UNREAD_COUNT_INITIAL_RECEIVER = 1;
+
+ /** 新建会话初始未读数(发送方) */
+ public static final int UNREAD_COUNT_INITIAL_SENDER = 0;
+
+ // ==================== OpenIM contentType ====================
+
+ /** 文本 */
+ public static final int CONTENT_TYPE_TEXT = 101;
+
+ /** 图片 */
+ public static final int CONTENT_TYPE_IMAGE = 102;
+
+ /** 语音 */
+ public static final int CONTENT_TYPE_VOICE = 103;
+
+ /** 视频 */
+ public static final int CONTENT_TYPE_VIDEO = 104;
+
+ /** 文件 */
+ public static final int CONTENT_TYPE_FILE = 105;
+
+ /** 位置 */
+ public static final int CONTENT_TYPE_LOCATION = 106;
+
+ /** 名片 */
+ public static final int CONTENT_TYPE_CARD = 114;
+
+ /** 自定义消息 */
+ public static final int CONTENT_TYPE_CUSTOM = 110;
+
+ // ==================== OpenIM sessionType ====================
+
+ /** 单聊 */
+ public static final int SESSION_TYPE_SINGLE = 1;
+
+ /** 群聊 */
+ public static final int SESSION_TYPE_GROUP = 3;
+
+ // ==================== 离线推送 extras / 文案 ====================
+
+ /** 推送业务类型:IM 离线消息 */
+ public static final String PUSH_BIZ_TYPE_IM_MESSAGE = "IM_MESSAGE";
+
+ /** 推送标题 */
+ public static final String PUSH_TITLE_NEW_MESSAGE = "您有新消息";
+
+ /** 发送方昵称兜底 */
+ public static final String PUSH_DEFAULT_SENDER_NAME = "用户";
+
+ /** 系统/管理员代发时的发送方昵称兜底(如 OpenIM imAdmin) */
+ public static final String PUSH_DEFAULT_SYSTEM_SENDER_NAME = "系统";
+
+ /** 群名称兜底 */
+ public static final String PUSH_DEFAULT_GROUP_NAME = "群聊";
+
+ /** extras:会话 ID */
+ public static final String EXTRAS_CONVERSATION_ID = "conversationId";
+
+ /** extras:发送方 ID */
+ public static final String EXTRAS_SENDER_ID = "senderId";
+
+ /** extras:消息类型 */
+ public static final String EXTRAS_MESSAGE_TYPE = "messageType";
+
+ /** extras:会话类型 */
+ public static final String EXTRAS_SESSION_TYPE = "sessionType";
+
+ /** extras:本地群组 ID */
+ public static final String EXTRAS_GROUP_ID = "groupId";
+
+ /** 单聊 conversationId 前缀 */
+ public static final String CONVERSATION_ID_SINGLE_PREFIX = "si_";
+
+ /** 群聊 conversationId 前缀(业务推送 extras 约定,非 OpenIM 原生 ID) */
+ public static final String CONVERSATION_ID_GROUP_PREFIX = "group:";
+
+ /**
+ * OpenIM 原生群聊 conversationID 前缀(sessionType=3 ReadGroupChat)
+ * 形如 {@code sg_{openimGroupId}}
+ */
+ public static final String OPENIM_NATIVE_GROUP_CONVERSATION_PREFIX = "sg_";
+
+ /** 单聊 conversationId 分隔符 */
+ public static final String CONVERSATION_ID_SEPARATOR = "_";
+
+ /** 推送正文最大长度(对齐 PushSendReq) */
+ public static final int PUSH_CONTENT_MAX_LEN = 200;
+
+ /** 推送 bizId 最大长度(对齐 PushSendReq) */
+ public static final int PUSH_BIZ_ID_MAX_LEN = 64;
+
+ /** 推送摘要最大长度 */
+ public static final int PUSH_SUMMARY_MAX_LEN = 50;
+
+ /** 单次 Feign 推送批量用户数 */
+ public static final int PUSH_BATCH_SIZE = 500;
+
+ /** 离线推送幂等 TTL(分钟) */
+ public static final long PUSH_DEDUP_TTL_MINUTES = 10L;
+
+ /** Redis 幂等占位值 */
+ public static final String PUSH_DEDUP_PLACEHOLDER = "1";
+
+ /** 推送正文:昵称与摘要分隔符 */
+ public static final String PUSH_BODY_NAME_CONTENT_SEPARATOR = ":";
+
+ /** 推送正文:群名与发送者分隔符 */
+ public static final String PUSH_BODY_GROUP_SEPARATOR = " | ";
+
+ /** 摘要:图片 */
+ public static final String SUMMARY_IMAGE = "[图片]";
+
+ /** 摘要:语音 */
+ public static final String SUMMARY_VOICE = "[语音]";
+
+ /** 摘要:视频 */
+ public static final String SUMMARY_VIDEO = "[视频]";
+
+ /** 摘要:文件 */
+ public static final String SUMMARY_FILE = "[文件]";
+
+ /** 摘要:位置 */
+ public static final String SUMMARY_LOCATION = "[位置]";
+
+ /** 摘要:名片 */
+ public static final String SUMMARY_CARD = "[名片]";
+
+ /** 摘要:其他消息 */
+ public static final String SUMMARY_DEFAULT = "[消息]";
+
+ /** JSON 对象起始字符(用于判断 content 是否为 JSON 字符串) */
+ public static final char JSON_OBJECT_START = '{';
+
+ /** 自定义消息 content.data / extension 字段 */
+ public static final String FIELD_DATA = "data";
+
+ /** 自定义消息 extension 字段 */
+ public static final String FIELD_EXTENSION = "extension";
+
+ /** 业务自定义类型字段 */
+ public static final String FIELD_CUSTOM_TYPE = "customType";
+
+ /**
+ * 红包领取提醒:仅聊天内系统记录,禁止离线推送 / 系统通知
+ */
+ public static final String CUSTOM_TYPE_RED_ENVELOPE_CLAIM_NOTICE = "red_envelope_claim_notice";
+}
diff --git a/src/main/java/com/tailbet/common/enums/ErrorCodeEnum.java b/src/main/java/com/tailbet/common/enums/ErrorCodeEnum.java
new file mode 100644
index 0000000..0ee3801
--- /dev/null
+++ b/src/main/java/com/tailbet/common/enums/ErrorCodeEnum.java
@@ -0,0 +1,1120 @@
+package com.tailbet.common.enums;
+
+import lombok.Getter;
+
+/**
+ * 错误码枚举
+ *
+ * 统一定义系统错误码,便于前后端统一处理和国际化。
+ * 错误码分段规则:
+ * - 200:成功
+ * - 400xx:客户端参数错误
+ * - 401xx:认证相关错误
+ * - 403xx:权限相关错误
+ * - 404xx:资源不存在
+ * - 500xx:服务端内部错误
+ * - 600xx:业务逻辑错误
+ *
+ *
+ * message 字段存储国际化消息 key,格式为 {@code error.{code}},
+ * 由 GlobalExceptionHandler 通过 MessageSource 解析为对应语言的文本。
+ *
+ *
+ * @author socialapp团队
+ * @since 1.0.0
+ */
+@Getter
+public enum ErrorCodeEnum {
+
+ // ==================== 成功 ====================
+ /** 操作成功 */
+ SUCCESS(200, "error.200"),
+
+ // ==================== 客户端错误 (400xx) ====================
+ /** 请求参数错误 */
+ PARAM_ERROR(40001, "error.40001"),
+ /** 参数校验失败 */
+ PARAM_VALIDATE_ERROR(40002, "error.40002"),
+ /** 请求格式错误 */
+ PARAM_FORMAT_ERROR(40003, "error.40003"),
+ /** 请求过于频繁 */
+ REQUEST_TOO_FREQUENT(40004, "error.40004"),
+ /** 业务层限流触发 */
+ RATE_LIMIT_EXCEEDED(40005, "error.40005"),
+ /** 用户名已被使用 */
+ USERNAME_ALREADY_EXISTS(40009, "error.40009"),
+ /** targetIds 不能为空 */
+ PARAM_IDS_EMPTY(40010, "error.40010"),
+ /** 单次最多校验 50 条 */
+ PARAM_MAX_CHECK_LIMIT(40011, "error.40011"),
+ /** 视频时长必须在1秒到1小时之间 */
+ PARAM_VIDEO_DURATION_INVALID(40012, "error.40012"),
+ /** 密码和验证码不能同时为空 */
+ PARAM_PASSWORD_AND_CAPTCHA_EMPTY(40013, "error.40013"),
+ /** 两次输入的新密码不一致 */
+ PARAM_PASSWORDS_NOT_MATCH(40014, "error.40014"),
+ /** 新密码不能与原密码相同 */
+ PARAM_NEW_PASSWORD_SAME(40015, "error.40015"),
+ /** 原密码错误 */
+ PARAM_OLD_PASSWORD_WRONG(40016, "error.40016"),
+ /** 状态值无效,只能是0或1 */
+ PARAM_STATUS_INVALID(40017, "error.40017"),
+ /** 分享平台不能为空 */
+ PARAM_SHARE_PLATFORM_EMPTY(40018, "error.40018"),
+ /** autoDeleteEnabled 参数不能为空 */
+ PARAM_AUTO_DELETE_ENABLED_EMPTY(40019, "error.40019"),
+ /** 章节不属于指定的主题 */
+ PARAM_CHAPTER_NOT_BELONG_TO_TOPIC(40020, "error.40020"),
+ /** 不能为负数 */
+ PARAM_FIELD_NOT_NEGATIVE(40021, "error.40021"),
+ /** 无效的红包类型 */
+ REDPACKET_TYPE_INVALID(40022, "error.40022"),
+ /** userId 不能为空 */
+ PARAM_USER_ID_EMPTY(40023, "error.40023"),
+ /** asset 不能为空 */
+ PARAM_ASSET_EMPTY(40024, "error.40024"),
+ /** business 不能为空 */
+ PARAM_BUSINESS_EMPTY(40025, "error.40025"),
+ /** mainBusiness 不能为空 */
+ PARAM_MAIN_BUSINESS_EMPTY(40026, "error.40026"),
+ /** businessId 不能为空 */
+ PARAM_BUSINESS_ID_EMPTY(40027, "error.40027"),
+ /** detail 不能为空 */
+ PARAM_DETAIL_EMPTY(40028, "error.40028"),
+
+ // ==================== 认证错误 (401xx) ====================
+ /** 未登录或登录已过期 */
+ UNAUTHORIZED(40101, "error.40101"),
+ /** Token无效 */
+ TOKEN_INVALID(40102, "error.40102"),
+ /** Token已过期 */
+ TOKEN_EXPIRED(40103, "error.40103"),
+ /** 登录失败 */
+ LOGIN_FAILED(40104, "error.40104"),
+ /** 账号已被禁用 */
+ ACCOUNT_DISABLED(40105, "error.40105"),
+ /** 验证码错误 */
+ CAPTCHA_ERROR(40106, "error.40106"),
+ /** Token类型不匹配 */
+ TOKEN_TYPE_MISMATCH(40107, "error.40107"),
+
+ // ==================== 权限错误 (403xx) ====================
+ /** 无权限访问 */
+ FORBIDDEN(40301, "error.40301"),
+ /** 权限不足 */
+ PERMISSION_DENIED(40302, "error.40302"),
+ /** 资源访问被拒绝 */
+ RESOURCE_ACCESS_DENIED(40303, "error.40303"),
+ /** 不能删除自己的账号 */
+ OPERATION_CANNOT_DELETE_SELF(40304, "error.40304"),
+ /** 不能删除超级管理员 */
+ OPERATION_CANNOT_DELETE_SUPER_ADMIN(40305, "error.40305"),
+ /** 不能禁用自己的账号 */
+ OPERATION_CANNOT_DISABLE_SELF(40306, "error.40306"),
+ /** 不能禁用超级管理员 */
+ OPERATION_CANNOT_DISABLE_SUPER_ADMIN(40307, "error.40307"),
+ /** 不能将自己设为父菜单 */
+ OPERATION_CANNOT_SET_SELF_AS_PARENT(40308, "error.40308"),
+ /** 该菜单下存在子菜单,请先删除子菜单 */
+ OPERATION_HAS_CHILD_MENUS(40309, "error.40309"),
+ /** 不能修改超级管理员角色 */
+ OPERATION_CANNOT_MODIFY_SUPER_ADMIN_ROLE(40310, "error.40310"),
+ /** 不能删除超级管理员角色 */
+ OPERATION_CANNOT_DELETE_SUPER_ADMIN_ROLE(40311, "error.40311"),
+ /** 该角色下存在管理员,无法删除 */
+ OPERATION_ROLE_HAS_ADMINS(40312, "error.40312"),
+ /** 当前KYC状态不允许审核通过操作 */
+ OPERATION_KYC_STATUS_NOT_ALLOW_APPROVE(40313, "error.40313"),
+ /** 当前KYC状态不允许审核拒绝操作 */
+ OPERATION_KYC_STATUS_NOT_ALLOW_REJECT(40314, "error.40314"),
+ /** 不能重置自己的密码(请使用修改密码) */
+ OPERATION_CANNOT_RESET_OWN_PASSWORD(40315, "error.40315"),
+
+ // ==================== 资源不存在 (404xx) ====================
+ /** 资源不存在 */
+ NOT_FOUND(40401, "error.40401"),
+ /** 资源不存在(通用别名) */
+ RESOURCE_NOT_FOUND(40401, "error.40401"),
+ /** 用户不存在 */
+ USER_NOT_FOUND(40402, "error.40402"),
+ /** 数据不存在 */
+ DATA_NOT_FOUND(40403, "error.40403"),
+ /** 管理员不存在 */
+ ADMIN_NOT_FOUND(40404, "error.40404"),
+ /** 角色不存在 */
+ ROLE_NOT_FOUND(40405, "error.40405"),
+ /** 菜单不存在 */
+ MENU_NOT_FOUND(40406, "error.40406"),
+ /** 父菜单不存在 */
+ PARENT_MENU_NOT_FOUND(40407, "error.40407"),
+ /** 视频不存在 */
+ ACADEMY_VIDEO_NOT_FOUND(40408, "error.40408"),
+ /** 章节不存在 */
+ ACADEMY_CHAPTER_NOT_FOUND(40409, "error.40409"),
+ /** 主题不存在 */
+ ACADEMY_TOPIC_NOT_FOUND(40410, "error.40410"),
+ /** 商学院视频状态异常,无法分享 */
+ ACADEMY_VIDEO_STATUS_INVALID(40411, "error.40411"),
+ /** 短视频不存在 */
+ SHORT_VIDEO_NOT_FOUND(40412, "error.40412"),
+ /** 短视频状态异常,无法分享 */
+ SHORT_VIDEO_STATUS_INVALID(40413, "error.40413"),
+ /** 评论不存在 */
+ COMMENT_NOT_FOUND(40414, "error.40414"),
+ /** KYC认证记录不存在 */
+ KYC_RECORD_NOT_FOUND(40415, "error.40415"),
+ /** 分类不存在 */
+ CATEGORY_NOT_FOUND(40416, "error.40416"),
+ /** 资产配置不存在 */
+ ASSET_CONFIG_NOT_FOUND(40417, "error.40417"),
+ /** 发现页分类不存在 */
+ DISCOVER_CATEGORY_NOT_FOUND(40418, "error.40418"),
+ /** 发现页入口不存在 */
+ DISCOVER_ENTRY_NOT_FOUND(40419, "error.40419"),
+ /** 手续费配置不存在 */
+ FEE_CONFIG_NOT_FOUND(40420, "error.40420"),
+ /** 未找到匹配的冻结记录 */
+ FREEZE_RECORD_NOT_FOUND(40421, "error.40421"),
+ /** 白名单记录不存在 */
+ WITHDRAW_WHITELIST_NOT_FOUND(40422, "error.40422"),
+
+ // ==================== 服务端错误 (500xx) ====================
+ /** 系统内部错误 */
+ INTERNAL_ERROR(50001, "error.50001"),
+ /** 系统错误(通用别名) */
+ SYSTEM_ERROR(50001, "error.50001"),
+ /** 系统繁忙 */
+ SYSTEM_BUSY(50003, "error.50003"),
+ /** 数据库操作失败 */
+ DATABASE_ERROR(50002, "error.50002"),
+ /** 远程服务调用失败 */
+ RPC_ERROR(50003, "error.50003"),
+ /** 缓存操作失败 */
+ CACHE_ERROR(50004, "error.50004"),
+ /** 消息队列操作失败 */
+ MQ_ERROR(50005, "error.50005"),
+ /** 用户ID生成失败 */
+ ID_GENERATOR_FAILED(50006, "error.50006"),
+ /** 用户ID已耗尽 */
+ ID_EXHAUSTED(50007, "error.50007"),
+
+ // ==================== 业务错误 (600xx) ====================
+ /** 业务操作失败 */
+ BUSINESS_ERROR(60001, "error.60001"),
+ /** 数据已存在 */
+ DATA_ALREADY_EXISTS(60002, "error.60002"),
+ /** 数据状态异常 */
+ DATA_STATUS_ERROR(60003, "error.60003"),
+ /** 操作不允许 */
+ OPERATION_NOT_ALLOWED(60004, "error.60004"),
+ /** 文件上传失败 */
+ FILE_UPLOAD_ERROR(60005, "error.60005"),
+ /** 文件过大 */
+ FILE_TOO_LARGE(60006, "error.60006"),
+ /** 文件类型不支持 */
+ FILE_TYPE_NOT_SUPPORTED(60007, "error.60007"),
+
+ // ==================== 冲突错误 (409xx) ====================
+ /** 角色编码已存在 */
+ ROLE_CODE_ALREADY_EXISTS(40901, "error.40901"),
+ /** 用户名已存在 */
+ USERNAME_ALREADY_EXISTS_ADMIN(40902, "error.40902"),
+ /** 视频名称已存在 */
+ ACADEMY_VIDEO_NAME_EXISTS(40903, "error.40903"),
+ /** 主题名称已存在 */
+ ACADEMY_TOPIC_NAME_EXISTS(40904, "error.40904"),
+ /** 该分类名称已被使用,请更换名称 */
+ CATEGORY_NAME_EXISTS(40905, "error.40905"),
+ /** 该分类名称已被使用,请更换名称(发现页) */
+ DISCOVER_CATEGORY_NAME_EXISTS(40906, "error.40906"),
+ /** 该入口标题已被使用,请更换标题 */
+ DISCOVER_ENTRY_TITLE_EXISTS(40907, "error.40907"),
+ /** 用户名已被注册 */
+ USERNAME_ALREADY_REGISTERED(40908, "error.40908"),
+ /** 邮箱已被注册 */
+ EMAIL_ALREADY_REGISTERED(40909, "error.40909"),
+ /** 该邮箱已被其他账号绑定 */
+ EMAIL_ALREADY_BOUND(40910, "error.40910"),
+ /** 您已提交过博主申请,请勿重复申请 */
+ BLOGGER_APPLICATION_EXISTS(40911, "error.40911"),
+
+ // ==================== 业务错误(60041-60068) ====================
+ /** 登录失败次数过多 */
+ ACCOUNT_LOCKED_LOGIN(60041, "error.60041"),
+ /** 登录会话已过期,请重新登录 */
+ LOGIN_SESSION_EXPIRED(60042, "error.60042"),
+ /** Google验证码错误,请重新输入 */
+ GOOGLE_CAPTCHA_ERROR(60043, "error.60043"),
+ /** 绑定会话已过期,请重新登录 */
+ BIND_SESSION_EXPIRED(60044, "error.60044"),
+ /** Google验证码错误,请确认已正确扫码 */
+ GOOGLE_CAPTCHA_SCAN_ERROR(60045, "error.60045"),
+ /** 注册过于频繁,请1小时后再试 */
+ REGISTER_TOO_FREQUENT(60046, "error.60046"),
+ /** 今日注册次数已达上限,请明日再试 */
+ REGISTER_DAILY_LIMIT(60047, "error.60047"),
+ /** 图片中未检测到人脸,请重新拍摄 */
+ FACE_NO_FACE_DETECTED(60048, "error.60048"),
+ /** 人脸识别失败,请重试 */
+ FACE_RECOGNITION_FAILED(60049, "error.60049"),
+ /** 人脸注册失败,请重试 */
+ FACE_REGISTER_FAILED(60050, "error.60050"),
+ /** 活体检测未通过,请按照提示完成活体检测 */
+ FACE_LIVENESS_FAILED(60051, "error.60051"),
+ /** 人脸图片质量不符合要求,请重新拍摄 */
+ FACE_IMAGE_QUALITY_LOW(60052, "error.60052"),
+ /** 图片格式错误 */
+ FACE_IMAGE_FORMAT_ERROR(60053, "error.60053"),
+ /** 该人脸已注册过账号,请直接登录 */
+ FACE_ALREADY_REGISTERED_RPC(60054, "error.60054"),
+ /** 发送次数过多,请1小时后再试 */
+ EMAIL_SEND_TOO_FREQUENT(60055, "error.60055"),
+ /** 该邮箱今日发送次数已达上限,请明日再试 */
+ EMAIL_DAILY_LIMIT(60056, "error.60056"),
+ /** 验证码发送次数过多,请1小时后再尝试 */
+ CAPTCHA_SEND_TOO_FREQUENT(60057, "error.60057"),
+ /** 验证码不存在或已过期 */
+ CAPTCHA_NOT_EXISTS(60058, "error.60058"),
+ /** 验证码已失效,请重新获取 */
+ CAPTCHA_EXPIRED(60059, "error.60059"),
+ /** 验证码错误,请重新输入 */
+ CAPTCHA_WRONG(60060, "error.60060"),
+ /** 邮件发送失败,请稍后重试 */
+ EMAIL_SEND_FAILED(60061, "error.60061"),
+ /** KYC AES密钥未配置 */
+ KYC_AES_KEY_NOT_CONFIGURED(60062, "error.60062"),
+ /** 请先完成实名认证后再申请博主 */
+ BLOGGER_NOT_VERIFIED(60063, "error.60063"),
+ /** 冻结操作失败,已回滚 */
+ FUND_FREEZE_FAILED(60064, "error.60064"),
+ /** 封面图文件不能为空 */
+ COVER_IMAGE_EMPTY(60065, "error.60065"),
+ /** 封面图仅支持 JPG/PNG/SVG 格式 */
+ COVER_IMAGE_FORMAT_INVALID(60066, "error.60066"),
+ /** 封面图大小不能超过 1MB */
+ COVER_IMAGE_SIZE_EXCEEDED(60067, "error.60067"),
+ /** 文件大小不能超过1GB */
+ FILE_SIZE_EXCEED_1GB(60068, "error.60068"),
+ /** 不支持的币种操作 */
+ ASSET_NOT_SUPPORTED_OPERATION(60069, "error.60069"),
+ /** 获取手续费配置失败 */
+ FEE_CONFIG_FETCH_FAILED(60070, "error.60070"),
+ /** 冻结资金失败 */
+ FREEZE_FUND_FAILED(60071, "error.60071"),
+ /** 提币上链失败 */
+ WITHDRAW_SUBMIT_CHAIN_FAILED(60072, "error.60072"),
+ /** 清除冻结失败 */
+ CLEAR_LIFT_FAILED(60073, "error.60073"),
+ /** 查询余额失败 */
+ BALANCE_QUERY_FAILED(60074, "error.60074"),
+ /** 红包扣款失败 */
+ REDPACKET_DEDUCT_FAILED(60075, "error.60075"),
+ /** 红包入账失败 */
+ REDPACKET_CREDIT_FAILED(60076, "error.60076"),
+ /** 红包退款失败 */
+ REDPACKET_REFUND_FAILED(60077, "error.60077"),
+ /** 当前红包状态不允许强制过期 */
+ REDPACKET_STATUS_INVALID_FORCE_EXPIRE(60078, "error.60078"),
+ /** 余额查询失败(RPC) */
+ RPC_BALANCE_QUERY_FAILED(60079, "error.60079"),
+ /** 充值入账失败(RPC) */
+ RPC_DEPOSIT_CONFIRM_FAILED(60080, "error.60080"),
+ /** 账变历史查询失败(RPC) */
+ RPC_HISTORY_QUERY_FAILED(60081, "error.60081"),
+ /** increase和decrease不能同时大于0 */
+ PARAM_INCREASE_DECREASE_BOTH_POSITIVE(60082, "error.60082"),
+
+ // ==================== 会议相关错误 ====================
+ /** 当前已有进行中的会议 */
+ MEETING_ALREADY_ACTIVE(4004, "error.4004"),
+ /** 当前无进行中的会议 */
+ MEETING_NOT_ACTIVE(4005, "error.4005"),
+ /** 会议码无效或会议已结束 */
+ MEETING_CODE_INVALID(4006, "error.4006"),
+
+ // ==================== 人脸识别错误 ====================
+ /** 人脸未注册 */
+ FACE_NOT_REGISTERED(60010, "error.60010"),
+ /** 人脸已注册 */
+ FACE_ALREADY_REGISTERED(60011, "error.60011"),
+ /** 人脸不匹配 */
+ FACE_NOT_MATCH(60012, "error.60012"),
+ /** 活体检测失败 */
+ LIVENESS_FAILED(60013, "error.60013"),
+ /** 人脸识别功能未开启 */
+ FACE_FEATURE_DISABLED(60016, "error.60016"),
+ /** 账号已锁定 */
+ ACCOUNT_LOCKED(40108, "error.40108"),
+ /** 邮箱已注册 */
+ EMAIL_ALREADY_EXISTS(60014, "error.60014"),
+ /** 邮箱未注册 */
+ EMAIL_NOT_FOUND(60015, "error.60015"),
+ /** 不支持使用临时邮箱注册 */
+ EMAIL_DOMAIN_BLOCKED(60017, "error.60017"),
+
+ // ==================== 钱包服务错误 ====================
+ /** KYC认证已提交,请勿重复提交 */
+ KYC_ALREADY_SUBMITTED(60020, "error.60020"),
+ /** 请先完成KYC认证 */
+ KYC_NOT_APPROVED(60021, "error.60021"),
+ /** 请先设置支付密码 */
+ PAY_PASSWORD_NOT_SET(60022, "error.60022"),
+ /** 支付密码错误 */
+ PAY_PASSWORD_WRONG(60023, "error.60023"),
+ /** 支付密码已设置,请使用修改接口 */
+ PAY_PASSWORD_ALREADY_SET(60024, "error.60024"),
+ /** 新支付密码不能与旧密码相同 */
+ PAY_PASSWORD_SAME(60025, "error.60025"),
+ /** 该币种不支持 */
+ ASSET_NOT_SUPPORTED(60026, "error.60026"),
+ /** 该币种不支持充币 */
+ ASSET_NOT_SUPPORT_DEPOSIT(60027, "error.60027"),
+ /** 仅支持提币到AIA链地址 */
+ ASSET_NOT_SUPPORT_WITHDRAW(60028, "error.60028"),
+ /** 钱包余额不足 */
+ INSUFFICIENT_BALANCE(60029, "error.60029"),
+ /** 余额不足(通用别名) */
+ BALANCE_NOT_ENOUGH(60029, "error.60029"),
+ /** 提币金额小于最小限制 */
+ MIN_WITHDRAW_AMOUNT(60030, "error.60030"),
+ /** 划转金额小于最小限制 */
+ MIN_TRANSFER_AMOUNT(60031, "error.60031"),
+ /** 订单状态冲突,请稍后重试 */
+ ORDER_STATUS_CONFLICT(60032, "error.60032"),
+ /** 操作进行中,请勿重复提交 */
+ OPERATION_IN_PROGRESS(60033, "error.60033"),
+ /** 系统维护中,请稍后重试 */
+ SERVICE_UNAVAILABLE(60034, "error.60034"),
+ /** 风控审核未通过 */
+ RISK_REJECTED(60035, "error.60035"),
+ /** 操作需要延迟处理 */
+ RISK_DELAY_RESPONSE(60036, "error.60036"),
+ /** 风控服务异常,操作已拒绝 */
+ RISK_SERVICE_UNAVAILABLE(60037, "error.60037"),
+ /** 资产引擎响应超时 */
+ ENGINE_TIMEOUT(60038, "error.60038"),
+ /** 资产引擎业务异常 */
+ ENGINE_BUSINESS_ERROR(60039, "error.60039"),
+ /** 补偿操作失败,请联系客服 */
+ COMPENSATION_FAILED(60040, "error.60040"),
+ /** 支付密码已锁定 */
+ PAY_PASSWORD_LOCKED(60041, "error.60041"),
+ /** 超过今日提币上限 */
+ WITHDRAW_DAILY_LIMIT_EXCEEDED(60042, "error.60042"),
+
+ // ==================== 资产服务错误 (60100-60119) ====================
+ /** 请先设置支付密码 */
+ ASSET_PAY_PASSWORD_NOT_SET(60100, "error.60100"),
+ /** 支付密码错误 */
+ ASSET_PAY_PASSWORD_WRONG(60101, "error.60101"),
+ /** 新支付密码不能与旧密码相同 */
+ ASSET_PAY_PASSWORD_SAME(60102, "error.60102"),
+ /** 支付密码已锁定,请稍后再试 */
+ ASSET_PAY_PASSWORD_LOCKED(60103, "error.60103"),
+ /** 请先完成KYC认证 */
+ ASSET_KYC_NOT_APPROVED(60104, "error.60104"),
+ /** KYC认证已提交,请勿重复提交 */
+ ASSET_KYC_ALREADY_SUBMITTED(60105, "error.60105"),
+ /** 该币种不支持 */
+ FUND_ASSET_NOT_SUPPORTED(60106, "error.60106"),
+ /** 余额不足 */
+ FUND_INSUFFICIENT_BALANCE(60107, "error.60107"),
+ /** 低于最小提币金额 */
+ FUND_MIN_WITHDRAW_AMOUNT(60108, "error.60108"),
+ /** 超过今日提币上限 */
+ FUND_WITHDRAW_DAILY_LIMIT_EXCEEDED(60109, "error.60109"),
+ /** 低于最小转账金额 */
+ FUND_MIN_TRANSFER_AMOUNT(60110, "error.60110"),
+
+ // ==================== 订单服务错误 (60120-60139) ====================
+ /** 订单不存在 */
+ ORDER_NOT_FOUND(60120, "error.60120"),
+ /** 订单状态冲突,请稍后重试 */
+ ORDER_STATUS_CONFLICT_ERROR(60121, "error.60121"),
+ /** 当前状态不允许该操作 */
+ ORDER_OPERATION_NOT_ALLOWED(60122, "error.60122"),
+ /** 重复提交,请勿重复操作 */
+ ORDER_IDEMPOTENCY_KEY_DUPLICATE(60123, "error.60123"),
+ /** 红包不存在 */
+ REDPACKET_NOT_FOUND(60124, "error.60124"),
+ /** 红包已过期 */
+ REDPACKET_EXPIRED(60125, "error.60125"),
+ /** 红包已被抢完 */
+ REDPACKET_FINISHED(60126, "error.60126"),
+ /** 您已抢过该红包 */
+ REDPACKET_ALREADY_GRABBED(60127, "error.60127"),
+ /** 不能抢自己发的红包 */
+ REDPACKET_CANNOT_GRAB_OWN(60128, "error.60128"),
+ /** 该地址不在白名单中 */
+ WITHDRAW_ADDRESS_NOT_WHITELISTED(60129, "error.60129"),
+ /** 红包结算中,请稍后 */
+ REDPACKET_EXPIRING(60130, "error.60130"),
+ /** 当前红包状态不允许设置过期时间(需为进行中) */
+ REDPACKET_STATUS_INVALID_EXTEND_EXPIRE(60131, "error.60131"),
+ /** 红包新过期时间必须晚于当前时间 */
+ REDPACKET_NEW_EXPIRE_TIME_INVALID(60132, "error.60132"),
+
+
+ // ==================== 红包雨错误 (63201-63249) · V1.1.3 ====================
+ /** 红包雨功能关闭 */
+ RAIN_DISABLED(63201, "error.63201"),
+ /** 红包雨房间排队已满 */
+ RAIN_QUEUE_FULL(63202, "error.63202"),
+ /** 红包雨不存在 */
+ RAIN_NOT_FOUND(63203, "error.63203"),
+ /** 红包雨状态不可点击 */
+ RAIN_HIT_NOT_ALLOWED(63204, "error.63204"),
+ /** 红包雨重复点击幂等(可作为成功提示,一般不抛) */
+ RAIN_HIT_DUPLICATE(63205, "error.63205"),
+ /** 红包雨点击过于频繁 */
+ RAIN_HIT_TOO_FAST(63206, "error.63206"),
+ /** 非场控无权暂停/恢复红包雨 */
+ RAIN_NOT_CONTROLLER(63207, "error.63207"),
+ /** 房间已暂停无法开播红包雨 */
+ RAIN_ROOM_PAUSED(63208, "error.63208"),
+ /** 红包雨发送失败(扣款/发起失败,不重试) */
+ RAIN_DEDUCT_FAILED(63209, "error.63209"),
+ /** 红包雨结算未完成 */
+ RAIN_SETTLE_NOT_READY(63210, "error.63210"),
+ /** 红包雨系统繁忙(单雨限流) */
+ RAIN_BUSY(63211, "error.63211"),
+ /** 红包雨金额非法 */
+ RAIN_AMOUNT_INVALID(63212, "error.63212"),
+ /** 红包雨入账失败 */
+ RAIN_CREDIT_FAILED(63213, "error.63213"),
+ /** 红包雨退款失败 */
+ RAIN_REFUND_FAILED(63214, "error.63214"),
+
+ // ==================== 补偿任务错误 (60140-60149) ====================
+ /** 补偿任务执行中 */
+ COMPENSATION_EXECUTING(60140, "error.60140"),
+ /** 补偿操作失败,请联系客服 */
+ COMPENSATION_TASK_FAILED(60141, "error.60141"),
+
+ // ==================== 钱包服务错误 (60150-60159) ====================
+ /** 充值回调验签失败 */
+ DEPOSIT_CALLBACK_VERIFY_FAILED(60150, "error.60150"),
+ /** 充值地址未找到 */
+ DEPOSIT_ADDRESS_NOT_FOUND(60151, "error.60151"),
+ /** 提币上链失败 */
+ WITHDRAW_SUBMIT_FAILED(60152, "error.60152"),
+ /** 创建充值地址失败 */
+ WALLET_ADDRESS_CREATE_FAILED(60153, "error.60153"),
+ /** 充值入账哈希重复 */
+ DEPOSIT_TX_HASH_DUPLICATE(60154, "error.60154"),
+
+ // ==================== IM服务错误 (14001-14999) ====================
+ // ---- IM 会话 (14001-14099) ----
+ /** 会话不存在 */
+ IM_CONVERSATION_NOT_FOUND(14001, "error.14001"),
+ /** 不能给自己创建会话 */
+ IM_CANNOT_CREATE_CONVERSATION_WITH_SELF(14002, "error.14002"),
+ /** 会话已改由 OpenIM 管理,业务会话接口已废弃 */
+ IM_CONVERSATION_DEPRECATED(14003, "error.14003"),
+ /** 每页最多查询200条消息 */
+ IM_SYNC_LIMIT_EXCEEDED(14051, "error.14051"),
+ /** 批量检查会话数不能超过50 */
+ IM_SYNC_CHECK_LIMIT_EXCEEDED(14052, "error.14052"),
+
+ // ---- IM 好友 (14101-14199) ----
+ /** 不能添加自己为好友 */
+ IM_CANNOT_ADD_SELF_AS_FRIEND(14101, "error.14101"),
+ /** 已经是好友关系 */
+ IM_ALREADY_FRIENDS(14102, "error.14102"),
+ /** 已经发送过好友申请,请等待对方处理 */
+ IM_FRIEND_REQUEST_PENDING(14103, "error.14103"),
+ /** 好友申请不存在 */
+ IM_FRIEND_REQUEST_NOT_FOUND(14104, "error.14104"),
+ /** 无权处理该好友申请 */
+ IM_FRIEND_REQUEST_NOT_RECIPIENT(14105, "error.14105"),
+ /** 好友关系不存在 */
+ IM_FRIENDSHIP_NOT_FOUND(14106, "error.14106"),
+ /** 搜索关键词不能为空 */
+ IM_SEARCH_KEYWORD_EMPTY(14107, "error.14107"),
+ /** 该好友申请已处理 */
+ IM_FRIEND_REQUEST_ALREADY_PROCESSED(14108, "error.14108"),
+ /** 好友备注不合法 */
+ IM_FRIEND_REMARK_INVALID(14120, "error.14120"),
+
+ // ---- IM 群组 (14201-14299) ----
+ /** 您不在该群组中 */
+ IM_NOT_GROUP_MEMBER(14201, "error.14201"),
+ /** 群组不存在或已解散 */
+ IM_GROUP_NOT_FOUND(14202, "error.14202"),
+ /** 只有群主或管理员可以更新群组信息 */
+ IM_GROUP_NOT_MANAGER_UPDATE(14203, "error.14203"),
+ /** 只有群主可以解散群组 */
+ IM_GROUP_NOT_OWNER_DISSOLVE(14204, "error.14204"),
+ /** 只有群主或管理员可以添加成员 */
+ IM_GROUP_NOT_MANAGER_ADD(14205, "error.14205"),
+ /** 只有群主或管理员可以移除成员 */
+ IM_GROUP_NOT_MANAGER_REMOVE(14206, "error.14206"),
+ /** 只有群主可以移除管理员 */
+ IM_GROUP_NOT_OWNER_REMOVE_ADMIN(14207, "error.14207"),
+ /** 群组成员数量已达上限 */
+ IM_GROUP_MEMBER_LIMIT_REACHED(14208, "error.14208"),
+ /** 该用户已在群组中 */
+ IM_USER_ALREADY_IN_GROUP(14209, "error.14209"),
+ /** 不能移除群主 */
+ IM_CANNOT_REMOVE_OWNER(14210, "error.14210"),
+ /** 该成员不在群组中 */
+ IM_GROUP_MEMBER_NOT_FOUND(14211, "error.14211"),
+ /** 只有群主或管理员可以开启会议 */
+ IM_GROUP_NOT_MANAGER_START_MEETING(14212, "error.14212"),
+ /** 只有群主可以结束会议 */
+ IM_GROUP_NOT_OWNER_END_MEETING(14213, "error.14213"),
+ /** 您已被限制加入该会议 */
+ IM_MEETING_BLACKLISTED(14214, "error.14214"),
+ /** 只有群主可以修改成员角色 */
+ IM_GROUP_NOT_OWNER_UPDATE_ROLE(14215, "error.14215"),
+ /** 不能修改自己的角色 */
+ IM_CANNOT_UPDATE_OWN_ROLE(14216, "error.14216"),
+ /** 角色值只能为 admin 或 member */
+ IM_INVALID_ROLE_VALUE(14217, "error.14217"),
+ /** 只有群主可以转让群主 */
+ IM_GROUP_NOT_OWNER_TRANSFER(14218, "error.14218"),
+ /** 不能将群主转让给自己 */
+ IM_CANNOT_TRANSFER_OWNER_TO_SELF(14219, "error.14219"),
+ /** 您已在该群组中 */
+ IM_USER_ALREADY_IN_GROUP_JOIN(14220, "error.14220"),
+ /** 群主不可退出群组 */
+ IM_OWNER_CANNOT_LEAVE_GROUP(14221, "error.14221"),
+ /** 您已被拉黑,无法加入该群 */
+ IM_GROUP_USER_BLACKLISTED(14230, "error.14230"),
+ /** 仅群主可操作群黑名单 */
+ IM_GROUP_NOT_OWNER_BLACKLIST(14231, "error.14231"),
+ /** 用户不在群黑名单中 */
+ IM_GROUP_BLACKLIST_NOT_FOUND(14232, "error.14232"),
+ /** 群昵称不合法 */
+ IM_GROUP_NICKNAME_INVALID(14233, "error.14233"),
+ /** 不能拉黑自己 */
+ IM_CANNOT_BLACKLIST_SELF(14234, "error.14234"),
+ /** 不能拉黑群主 */
+ IM_CANNOT_BLACKLIST_OWNER(14235, "error.14235"),
+ /** 不能移除管理员 */
+ IM_CANNOT_REMOVE_ADMIN(14236, "error.14236"),
+ /** 不能拉黑管理员 */
+ IM_CANNOT_BLACKLIST_ADMIN(14237, "error.14237"),
+
+ // ---- IM 会议/连麦/媒体 (14301-14399) ----
+ /** 只有会议发起人可以审批媒体权限 */
+ IM_MEETING_NOT_OWNER_MEDIA_APPROVE(14301, "error.14301"),
+ /** 只有会议发起人可以撤销媒体权限 */
+ IM_MEETING_NOT_OWNER_MEDIA_REVOKE(14302, "error.14302"),
+ /** 只有会议发起人可以进行静音控制 */
+ IM_MEETING_NOT_OWNER_MUTE(14303, "error.14303"),
+ /** 未找到目标用户的匹配媒体轨道 */
+ IM_MEDIA_TRACK_NOT_FOUND(14304, "error.14304"),
+ /** 不能将自己从会议中踢出 */
+ IM_CANNOT_KICK_SELF(14305, "error.14305"),
+ /** 只有会议发起人可以将成员踢出会议 */
+ IM_MEETING_NOT_OWNER_KICK(14306, "error.14306"),
+ /** 发起人默认拥有连麦权限 */
+ IM_INITIATOR_NO_COHOST_NEEDED(14307, "error.14307"),
+ /** 您已提交过连麦申请,请等待审批 */
+ IM_COHOST_REQUEST_PENDING(14308, "error.14308"),
+ /** 您已在连麦中,无需重复申请 */
+ IM_COHOST_ALREADY_ACTIVE(14309, "error.14309"),
+ /** 连麦申请数量已达上限 */
+ IM_COHOST_REQUEST_LIMIT_REACHED(14310, "error.14310"),
+ /** 发送连麦申请通知失败 */
+ IM_COHOST_REQUEST_NOTIFY_FAILED(14311, "error.14311"),
+ /** 只有会议发起人可以查看连麦申请 */
+ IM_MEETING_NOT_OWNER_COHOST_VIEW(14312, "error.14312"),
+ /** 只有会议发起人可以审批连麦 */
+ IM_MEETING_NOT_OWNER_COHOST_APPROVE(14313, "error.14313"),
+ /** 未找到待审批的连麦申请 */
+ IM_COHOST_PENDING_REQUEST_NOT_FOUND(14314, "error.14314"),
+ /** 连麦人数已达上限 */
+ IM_COHOST_COUNT_LIMIT_REACHED(14315, "error.14315"),
+ /** 发送连麦审批通知失败 */
+ IM_COHOST_APPROVE_NOTIFY_FAILED(14316, "error.14316"),
+ /** 只有会议发起人可以取消连麦 */
+ IM_MEETING_NOT_OWNER_COHOST_CANCEL(14317, "error.14317"),
+ /** 未找到该用户的连麦记录 */
+ IM_COHOST_RECORD_NOT_FOUND(14318, "error.14318"),
+ /** 发送下麦通知失败 */
+ IM_COHOST_OFFLINE_NOTIFY_FAILED(14319, "error.14319"),
+ /** 未找到待撤回的连麦申请 */
+ IM_COHOST_CANCEL_REQUEST_NOT_FOUND(14320, "error.14320"),
+ /** 发送撤回申请通知失败 */
+ IM_COHOST_CANCEL_NOTIFY_FAILED(14321, "error.14321"),
+ /** 发送媒体权限请求失败 */
+ IM_MEDIA_REQUEST_NOTIFY_FAILED(14322, "error.14322"),
+ /** 发送媒体审批通知失败 */
+ IM_MEDIA_APPROVE_NOTIFY_FAILED(14323, "error.14323"),
+ /** 发送媒体撤销通知失败 */
+ IM_MEDIA_REVOKE_NOTIFY_FAILED(14324, "error.14324"),
+ /** 只有会议发起人可以控制弹幕 */
+ IM_MEETING_DANMAKU_NOT_OWNER(14325, "error.14325"),
+ /** 不能禁发自己的弹幕 */
+ IM_MEETING_DANMAKU_CANNOT_BAN_SELF(14326, "error.14326"),
+ /** 敏感词过滤服务异常 */
+ IM_SENSITIVE_FILTER_FAILED(14327, "error.14327"),
+
+ // ---- IM LiveKit 通话 (14401-14499) ----
+ /** 目标用户ID不能为空 */
+ IM_LIVEKIT_TARGET_USER_EMPTY(14401, "error.14401"),
+ /** 双方不是好友关系,无法发起通话 */
+ IM_LIVEKIT_NOT_FRIENDS(14402, "error.14402"),
+ /** 群组ID不能为空 */
+ IM_LIVEKIT_GROUP_ID_EMPTY(14403, "error.14403"),
+ /** 群组不存在 */
+ IM_LIVEKIT_GROUP_NOT_FOUND(14404, "error.14404"),
+ /** 多人通话房间名无效(须以 mpcall_ 开头) */
+ IM_LIVEKIT_MULTI_CALL_ROOM_INVALID(14405, "error.14405"),
+ /** 多人通话须提供邀请人或目标用户以校验好友关系 */
+ IM_LIVEKIT_MULTI_CALL_PEER_REQUIRED(14406, "error.14406"),
+
+ // ---- IM OpenIM 第三方集成 (14601-14699) ----
+ /** 调用 OpenIM 接口发生网络/HTTP异常 */
+ IM_OPENIM_HTTP_ERROR(14601, "error.14601"),
+ /** OpenIM 用户注册失败 */
+ IM_OPENIM_USER_REGISTER_FAILED(14602, "error.14602"),
+ /** 获取 OpenIM 用户 Token 失败 */
+ IM_OPENIM_GET_TOKEN_FAILED(14603, "error.14603"),
+ /** 获取 OpenIM 管理员 Token 失败 */
+ IM_OPENIM_GET_ADMIN_TOKEN_FAILED(14604, "error.14604"),
+ /** OpenIM 创建群组失败 */
+ IM_OPENIM_CREATE_GROUP_FAILED(14605, "error.14605"),
+ /** OpenIM 解散群组失败 */
+ IM_OPENIM_DISMISS_GROUP_FAILED(14606, "error.14606"),
+ /** OpenIM 邀请入群失败 */
+ IM_OPENIM_INVITE_GROUP_FAILED(14607, "error.14607"),
+ /** OpenIM 踢出群成员失败 */
+ IM_OPENIM_KICK_GROUP_FAILED(14608, "error.14608"),
+ /** OpenIM 添加好友失败 */
+ IM_OPENIM_ADD_FRIEND_FAILED(14609, "error.14609"),
+ /** OpenIM 删除好友失败 */
+ IM_OPENIM_DELETE_FRIEND_FAILED(14610, "error.14610"),
+ /** OpenIM 发送消息失败 */
+ IM_OPENIM_SEND_MSG_FAILED(14611, "error.14611"),
+ /** OpenIM 设置群成员角色失败 */
+ IM_OPENIM_SET_MEMBER_ROLE_FAILED(14612, "error.14612"),
+ /** OpenIM 拉黑失败 */
+ IM_OPENIM_ADD_BLACK_FAILED(14613, "error.14613"),
+ /** OpenIM 取消拉黑失败 */
+ IM_OPENIM_REMOVE_BLACK_FAILED(14614, "error.14614"),
+ /** OpenIM 处理好友申请(同意/拒绝)失败 */
+ IM_OPENIM_RESPOND_FRIEND_FAILED(14615, "error.14615"),
+ /** OpenIM 更新群资料失败 */
+ IM_OPENIM_SET_GROUP_INFO_FAILED(14616, "error.14616"),
+ /** OpenIM 转让群主失败 */
+ IM_OPENIM_TRANSFER_OWNER_FAILED(14617, "error.14617"),
+ /** OpenIM 退出群组失败 */
+ IM_OPENIM_QUIT_GROUP_FAILED(14618, "error.14618"),
+ /** OpenIM 设置好友备注失败 */
+ IM_OPENIM_SET_FRIEND_REMARK_FAILED(14620, "error.14620"),
+ /** OpenIM 设置群成员昵称失败 */
+ IM_OPENIM_SET_MEMBER_NICKNAME_FAILED(14621, "error.14621"),
+ /** OpenIM 同步官方标识失败 */
+ IM_OPENIM_SET_OFFICIAL_EX_FAILED(14622, "error.14622"),
+ /** OpenIM 强制用户下线失败 */
+ IM_OPENIM_FORCE_LOGOUT_FAILED(14623, "error.14623"),
+
+ // ---- IM 收藏 / 表情 (14701-14799) · V1.1.1 ----
+ /** 收藏不存在 */
+ IM_FAVORITE_NOT_FOUND(14701, "error.14701"),
+ /** 收藏内容类型不合法 */
+ IM_FAVORITE_TYPE_INVALID(14702, "error.14702"),
+ /** 收藏内容为空或非法 */
+ IM_FAVORITE_CONTENT_INVALID(14703, "error.14703"),
+ /** 收藏已达上限 */
+ IM_FAVORITE_LIMIT_REACHED(14704, "error.14704"),
+ /** 表情包不存在或已下架 */
+ IM_STICKER_PACK_NOT_FOUND(14711, "error.14711"),
+ /** 用户表情不存在 */
+ IM_USER_STICKER_NOT_FOUND(14712, "error.14712"),
+ /** 表情媒体类型不合法 */
+ IM_STICKER_MEDIA_TYPE_INVALID(14713, "error.14713"),
+ /** 视频表情时长非法(须 1~5000ms) */
+ IM_STICKER_DURATION_INVALID(14714, "error.14714"),
+ /** 用户表情数量已达上限 */
+ IM_USER_STICKER_LIMIT_REACHED(14715, "error.14715"),
+ /** 表情 URL 非法 */
+ IM_STICKER_URL_INVALID(14716, "error.14716"),
+ /** 批量撤回消息参数非法(空列表或超上限) */
+ IM_MSG_REVOKE_SEQS_INVALID(14720, "error.14720"),
+ /** 批量撤回消息全部失败 */
+ IM_MSG_REVOKE_ALL_FAILED(14721, "error.14721"),
+ /** 系统表情导入失败(EmojiHub 拉取/解析/入库) */
+ IM_EMOJI_IMPORT_FAILED(14730, "error.14730"),
+ /** 系统表情缓存刷新失败 */
+ IM_EMOJI_CACHE_FAILED(14731, "error.14731"),
+
+ // ==================== IM 批量转发错误 (63001-63013) · V1.1.3 ====================
+ /** 批量转发功能未开放 */
+ FORWARD_FEATURE_DISABLED(63001, "error.63001"),
+ /** 批量转发 scope 参数无效 */
+ FORWARD_SCOPE_INVALID(63002, "error.63002"),
+ /** 今日批量转发任务额度已用完 */
+ FORWARD_DAILY_QUOTA_EXCEEDED(63003, "error.63003"),
+ /** 批量转发目标超过上限 */
+ FORWARD_TARGET_LIMIT_EXCEEDED(63004, "error.63004"),
+ /** 批量转发消息数量超过上限 */
+ FORWARD_MESSAGE_LIMIT_EXCEEDED(63005, "error.63005"),
+ /** 批量转发消息类型不支持 */
+ FORWARD_MESSAGE_TYPE_UNSUPPORTED(63006, "error.63006"),
+ /** 批量转发媒体资源无效 */
+ FORWARD_MEDIA_INVALID(63007, "error.63007"),
+ /** 幂等键与原请求不一致 */
+ FORWARD_IDEMPOTENCY_CONFLICT(63008, "error.63008"),
+ /** 批量转发任务不存在或无权访问 */
+ FORWARD_JOB_NOT_FOUND(63009, "error.63009"),
+ /** 批量转发任务状态不允许当前操作 */
+ FORWARD_JOB_STATUS_INVALID(63010, "error.63010"),
+ /** estimateToken 无效或过期 */
+ FORWARD_ESTIMATE_TOKEN_INVALID(63011, "error.63011"),
+ /** 已有运行中的批量转发任务 */
+ FORWARD_JOB_ALREADY_RUNNING(63012, "error.63012"),
+ /** 源消息不存在或无权访问 */
+ FORWARD_SOURCE_INVALID(63013, "error.63013"),
+
+
+ // ==================== 游戏资金划转错误 (60160-60169) ====================
+
+ /** 游戏资金划转处理中,请勿重复提交(bizOrderNo 维度锁竞争) */
+ GAMEFUND_TRANSFER_BUSY(42901, "error.42901"),
+
+ /** 游戏资金划转重试中,请稍后再试(orderNo 维度锁竞争) */
+ GAMEFUND_RETRY_BUSY(42902, "error.42902"),
+
+ /** 业务订单号冲突(bizOrderNo 已存在但参数不一致) */
+ GAMEFUND_BIZ_ORDER_CONFLICT(40901, "error.40901"),
+
+ /** 游戏资金划转订单不存在 */
+ GAMEFUND_ORDER_NOT_FOUND(60160, "error.60160"),
+
+ /** 订单状态不允许重试(非 FAILED 状态) */
+ GAMEFUND_INVALID_RETRY_STATUS(60161, "error.60161"),
+
+ /** 资产引擎调用失败 */
+ GAMEFUND_LEDGER_CALL_FAILED(60162, "error.60162"),
+
+ /** 资产引擎返回非成功状态 */
+ GAMEFUND_LEDGER_RETURN_ERROR(60163, "error.60163"),
+
+ /** 用户余额不足(OUT 场景) */
+ GAMEFUND_INSUFFICIENT_BALANCE(60164, "error.60164"),
+
+ /** 平台→游戏划转已关闭(全局开关) */
+ GAMEFUND_OUT_DISABLED(60310, "error.60310"),
+
+ /** 游戏→平台划转已关闭(全局开关) */
+ GAMEFUND_IN_DISABLED(60311, "error.60311"),
+
+ // ==================== 直播服务错误 (14501-14599) ====================
+
+ /** 已有进行中的直播 */
+ LIVE_ALREADY_ACTIVE(14501, "error.14501"),
+ /** 直播场次不存在 */
+ LIVE_SESSION_NOT_FOUND(14502, "error.14502"),
+ /** 直播已结束 */
+ LIVE_SESSION_ENDED(14503, "error.14503"),
+ /** 仅主播可执行此操作 */
+ LIVE_NOT_ANCHOR(14504, "error.14504"),
+ /** 预约时间无效 */
+ LIVE_INVALID_SCHEDULE_TIME(14505, "error.14505"),
+ /** 当前状态不允许该操作 */
+ LIVE_INVALID_STATUS(14506, "error.14506"),
+ /** 已关注该主播 */
+ LIVE_ALREADY_FOLLOWED(14507, "error.14507"),
+ /** 未关注该主播 */
+ LIVE_NOT_FOLLOWED(14508, "error.14508"),
+ /** 已预约该场次 */
+ LIVE_ALREADY_RESERVED(14509, "error.14509"),
+ /** 未预约该场次 */
+ LIVE_NOT_RESERVED(14510, "error.14510"),
+ /** 弹幕发送过于频繁 */
+ LIVE_DANMAKU_TOO_FREQUENT(14511, "error.14511"),
+ /** 弹幕内容包含敏感词 */
+ LIVE_DANMAKU_SENSITIVE(14512, "error.14512"),
+ /** 主播用户不存在 */
+ LIVE_ANCHOR_NOT_FOUND(14513, "error.14513"),
+ /** 该场次已点赞 */
+ LIVE_DUPLICATE_LIKE(14514, "error.14514"),
+ /** 直播未开始或已结束,无法进入 */
+ LIVE_ROOM_NOT_LIVING(14515, "error.14515"),
+
+ // ==================== 直播主播申请错误 (14516-14520) ====================
+
+ /** 请先完成实名认证 */
+ LIVE_ANCHOR_NOT_VERIFIED(14517, "error.14517"),
+
+ /** 已提交过主播申请 */
+ LIVE_ANCHOR_ALREADY_APPLIED(14518, "error.14518"),
+
+ /** 申请记录不存在 */
+ LIVE_ANCHOR_APPLICATION_NOT_FOUND(14519, "error.14519"),
+
+ /** 未通过主播审核(开播时校验) */
+ LIVE_ANCHOR_NOT_APPROVED(14520, "error.14520"),
+
+ // ==================== 订单服务扩展错误 (60165-60209) ====================
+ /** 无效的转账类型 */
+ TRANSFER_TYPE_INVALID(60165, "error.60165"),
+ /** 用户间转账必须指定目标用户 */
+ TRANSFER_TARGET_USER_EMPTY(60166, "error.60166"),
+ /** 不能给自己转账 */
+ TRANSFER_CANNOT_SELF(60167, "error.60167"),
+ /** 扣款失败 */
+ TRANSFER_DEDUCT_FAILED(60168, "error.60168"),
+ /** 协议类型不能为空 */
+ AGREEMENT_TYPE_EMPTY(60169, "error.60169"),
+ /** 无效的协议类型 */
+ AGREEMENT_TYPE_INVALID(60170, "error.60170"),
+ /** 协议未发布 */
+ AGREEMENT_NOT_PUBLISHED(60171, "error.60171"),
+ /** 该协议类型下版本已存在 */
+ AGREEMENT_VERSION_EXISTS(60172, "error.60172"),
+ /** 协议不存在 */
+ AGREEMENT_NOT_FOUND(60173, "error.60173"),
+ /** 协议正在发布中,请稍后再试 */
+ AGREEMENT_PUBLISHING(60174, "error.60174"),
+ /** 产品不存在 */
+ PRODUCT_NOT_FOUND(60175, "error.60175"),
+ /** 产品已下架 */
+ PRODUCT_OFFLINE(60176, "error.60176"),
+ /** 产品已售罄 */
+ PRODUCT_SOLD_OUT(60177, "error.60177"),
+ /** 产品不存在或已下架 */
+ PRODUCT_NOT_FOUND_OR_OFFLINE(60178, "error.60178"),
+ /** 请先同意银行卡购买协议 */
+ AGREEMENT_NOT_ACCEPTED(60179, "error.60179"),
+ /** 邮箱验证码错误或已失效 */
+ EMAIL_CAPTCHA_ERROR(60180, "error.60180"),
+ /** 用户注册失败,请稍后重试 */
+ USER_REGISTER_FAILED(60181, "error.60181"),
+ /** 充值金额不足 */
+ RECHARGE_AMOUNT_INSUFFICIENT(60182, "error.60182"),
+ /** 订单正在处理中,请勿重复操作 */
+ ORDER_PROCESSING(60183, "error.60183"),
+ /** 已支付订单不能直接取消,请走退款流程 */
+ ORDER_PAID_CANNOT_CANCEL(60184, "error.60184"),
+ /** 订单状态已变更 */
+ ORDER_STATUS_CHANGED(60185, "error.60185"),
+ /** 支付密码不能为空 */
+ PAY_PASSWORD_EMPTY(60186, "error.60186"),
+ /** 卡记录不存在 */
+ CARD_RECORD_NOT_FOUND(60187, "error.60187"),
+ /** 无效的状态值 */
+ CARD_STATUS_INVALID(60188, "error.60188"),
+ /** 卡号生成失败,请重试 */
+ CARD_GENERATE_FAILED(60189, "error.60189"),
+ /** 银行卡价格必须大于0 */
+ BANKCARD_PRICE_INVALID(60190, "error.60190"),
+ /** 支付明细异常 */
+ PAYMENT_DETAIL_ERROR(60191, "error.60191"),
+ /** 分佣入账失败 */
+ COMMISSION_SETTLE_FAILED(60192, "error.60192"),
+ /** 规则列表不能为空 */
+ COMMISSION_RULE_LIST_EMPTY(60193, "error.60193"),
+ /** 层级必须在 1-20 之间 */
+ COMMISSION_LEVEL_INVALID(60194, "error.60194"),
+ /** 无效的佣金类型 */
+ COMMISSION_TYPE_INVALID(60195, "error.60195"),
+ /** 佣金值不能为负 */
+ COMMISSION_VALUE_NEGATIVE(60196, "error.60196"),
+ /** 产品编码已存在 */
+ BANKCARD_PRODUCT_CODE_EXISTS(60197, "error.60197"),
+ /** 售罄状态由系统自动维护 */
+ BANKCARD_STATUS_IMMUTABLE(60198, "error.60198"),
+ /** 产品已有销售记录,不能删除,请下架 */
+ BANKCARD_HAS_SALES_CANNOT_DELETE(60199, "error.60199"),
+ /** 无效的卡种 */
+ BANKCARD_CARD_TYPE_INVALID(60200, "error.60200"),
+ /** 支付配置不能为空 */
+ BANKCARD_PAYMENT_CONFIG_EMPTY(60201, "error.60201"),
+ /** 支付配置各币种比例之和必须等于100 */
+ BANKCARD_PAYMENT_RATIO_INVALID(60202, "error.60202"),
+ /** 订单状态异常 */
+ ORDER_STATUS_ERROR(60203, "error.60203"),
+ /** 无效的定价模式 */
+ BANKCARD_PRICING_MODE_INVALID(60204, "error.60204"),
+ /** 币本位支付数量无效 */
+ BANKCARD_PAYMENT_AMOUNT_INVALID(60205, "error.60205"),
+ /** 币本位含非USDT资产,站外余额不足时不支持USDT充值补差 */
+ BANKCARD_COIN_GUEST_RECHARGE_UNSUPPORTED(60206, "error.60206"),
+ /** 金本位总价不能为空 */
+ BANKCARD_TOTAL_PRICE_REQUIRED(60207, "error.60207"),
+ /** 币本位不可配置USDT总价 */
+ BANKCARD_COIN_TOTAL_PRICE_FORBIDDEN(60208, "error.60208"),
+
+ // ==================== 用户服务扩展错误 (2202, 2203, 60210-60219) ====================
+ /** 密码为空,需要设置密码 */
+ PASSWORD_EMPTY_NEED_SET(2202, "error.2202"),
+ /** App 版本过低,需要强制升级 */
+ APP_VERSION_TOO_LOW(2203, "error.2203"),
+ /** 跳转类型只能是 URL 或 APP */
+ DISCOVER_JUMP_TYPE_INVALID(60210, "error.60210"),
+ /** 审核状态参数非法,仅支持1(通过)或2(拒绝) */
+ KYC_AUDIT_STATUS_INVALID(60211, "error.60211"),
+ /** 请选择要上传的头像文件 */
+ FILE_AVATAR_EMPTY(60212, "error.60212"),
+ /** 文件不能为空 */
+ FILE_EMPTY(60213, "error.60213"),
+ /** 请选择要上传的图标文件 */
+ FILE_ICON_EMPTY(60214, "error.60214"),
+ /** 请选择要上传的图片文件 */
+ FILE_IMAGE_EMPTY(60215, "error.60215"),
+ /** 不支持的 fileType */
+ FILE_TYPE_INVALID(60216, "error.60216"),
+ /** 管理员未绑定 Google 验证器 */
+ ADMIN_TOTP_NOT_BOUND(60217, "error.60217"),
+
+ // ==================== 商学院服务扩展错误 (60220-60229) ====================
+ /** 该主题下章节名称已存在 */
+ CHAPTER_NAME_EXISTS_IN_TOPIC(60220, "error.60220"),
+
+ // ==================== 短视频服务扩展错误 (60230-60239) ====================
+ /** 视频标题已存在 */
+ SHORT_VIDEO_NAME_EXISTS(60230, "error.60230"),
+ /** 已关注该用户/博主 */
+ SHORT_VIDEO_ALREADY_FOLLOWED(60231, "error.60231"),
+ /** 未关注该用户/博主 */
+ SHORT_VIDEO_NOT_FOLLOWED(60232, "error.60232"),
+ /** 关注目标无效 */
+ SHORT_VIDEO_FOLLOW_TARGET_INVALID(60233, "error.60233"),
+ /** 视频当前状态不允许审核操作 */
+ SHORT_VIDEO_AUDIT_STATUS_INVALID(60234, "error.60234"),
+ /** 短视频文件下载失败 */
+ SHORT_VIDEO_DOWNLOAD_FAILED(60235, "error.60235"),
+ /** 重转码缺少原片地址 */
+ SHORT_VIDEO_RETRANSCODE_SOURCE_MISSING(60236, "error.60236"),
+ /** 当前状态不允许重转码 */
+ SHORT_VIDEO_RETRANSCODE_STATUS_INVALID(60237, "error.60237"),
+
+ // ==================== IM服务扩展错误 (60240-60259) ====================
+ /** 系统消息不存在 */
+ SYSTEM_MESSAGE_NOT_FOUND(60240, "error.60240"),
+ /** 仅已发布状态的消息可推送 */
+ SYSTEM_MESSAGE_NOT_PUBLISHED(60241, "error.60241"),
+ /** 公告不存在 */
+ ANNOUNCEMENT_NOT_FOUND(60242, "error.60242"),
+ /** 仅已发布状态的公告可推送 */
+ ANNOUNCEMENT_NOT_PUBLISHED(60243, "error.60243"),
+ /** 不能关注自己 */
+ CANNOT_FOLLOW_SELF(60244, "error.60244"),
+
+ // ==================== 通用服务扩展错误 (60260-60279) ====================
+ /** 获取行情价格失败 */
+ MARKET_PRICE_FETCH_FAILED(60260, "error.60260"),
+ /** 获取行情价格异常,价格为0 */
+ MARKET_PRICE_ZERO(60261, "error.60261"),
+ /** 分享短码不能为空 */
+ SHORT_CODE_EMPTY(60262, "error.60262"),
+ /** 分享事件类型不能为空 */
+ EVENT_TYPE_EMPTY(60263, "error.60263"),
+ /** 单次批量删除数量超限 */
+ BATCH_DELETE_MAX_EXCEEDED(60264, "error.60264"),
+ /** 群组已解散,无法分享 */
+ GROUP_DISSOLVED_CANNOT_SHARE(60265, "error.60265"),
+ /** LiveKit操作失败 */
+ LIVEKIT_OPERATION_FAILED(60266, "error.60266"),
+ /** 推送操作失败 */
+ PUSH_OPERATION_FAILED(60267, "error.60267"),
+ /** 扣款操作失败 */
+ DEDUCT_OPERATION_FAILED(60268, "error.60268"),
+ /** 文件大小超过限制 */
+ FILE_SIZE_EXCEED_LIMIT(60269, "error.60269"),
+
+ // ==================== S3文件服务错误 (60270-60279) ====================
+ /** 文件读取失败 */
+ FILE_READ_FAILED(60270, "error.60270"),
+ /** 文件上传至存储服务失败 */
+ FILE_UPLOAD_TO_STORAGE_FAILED(60271, "error.60271"),
+ /** 文件删除失败 */
+ FILE_DELETE_FAILED(60272, "error.60272"),
+ /** 初始化分片上传失败 */
+ MULTIPART_UPLOAD_INIT_FAILED(60273, "error.60273"),
+ /** 生成分片上传URL失败 */
+ MULTIPART_UPLOAD_URL_FAILED(60274, "error.60274"),
+ /** 查询已上传分片失败 */
+ MULTIPART_UPLOAD_PARTS_QUERY_FAILED(60275, "error.60275"),
+ /** 完成分片上传失败 */
+ MULTIPART_UPLOAD_COMPLETE_FAILED(60276, "error.60276"),
+ /** 取消分片上传失败 */
+ MULTIPART_UPLOAD_ABORT_FAILED(60277, "error.60277"),
+
+ // ==================== 加密/解密错误 (60278-60279) ====================
+ /** AES加密失败 */
+ AES_ENCRYPT_FAILED(60278, "error.60278"),
+ /** AES解密失败 */
+ AES_DECRYPT_FAILED(60279, "error.60279"),
+
+ // ==================== 视频处理错误 (60280-60289) ====================
+ /** 无法探测视频时长 */
+ VIDEO_DURATION_DETECT_FAILED(60280, "error.60280"),
+ /** 提取视频缩略图失败 */
+ VIDEO_THUMBNAIL_EXTRACT_FAILED(60281, "error.60281"),
+ /** 视频转码失败 */
+ VIDEO_TRANSCODE_FAILED(60282, "error.60282"),
+
+ // ==================== 订单服务扩展错误 (60283-60289) ====================
+ /** FIXED 类型佣金暂不支持,请使用 RATE 类型 */
+ COMMISSION_FIXED_TYPE_NOT_SUPPORTED(60283, "error.60283"),
+ /** 邮件发送冷却中,请稍后再试 */
+ EMAIL_SEND_COOLDOWN(60284, "error.60284"),
+
+ // ==================== App 版本管理错误 (60290-60299) ====================
+ /** 版本记录不存在 */
+ APP_VERSION_NOT_FOUND(60290, "error.60290"),
+ /** 版本状态不允许该操作 */
+ APP_VERSION_STATUS_INVALID(60291, "error.60291"),
+ /** 平台类型非法,仅支持 android 或 ios */
+ APP_VERSION_PLATFORM_INVALID(60292, "error.60292"),
+ /** 同平台同版本号已存在 */
+ APP_VERSION_CODE_DUPLICATE(60293, "error.60293"),
+
+ // ==================== 直播V2扩展错误 (14521-14529) ====================
+ /** 直播V2-主播未找到 */
+ LIVE_V2_ANCHOR_NOT_FOUND(14521, "error.14521"),
+
+ // ==================== 推送服务错误 (60300-60309) ====================
+ /** registrationId 不能为空 */
+ PUSH_DEVICE_REGISTRATION_ID_EMPTY(60300, "error.60300"),
+ /** 平台类型非法,仅支持 ANDROID 或 IOS */
+ PUSH_DEVICE_PLATFORM_INVALID(60301, "error.60301"),
+ /** 设备记录不存在 */
+ PUSH_DEVICE_NOT_FOUND(60302, "error.60302"),
+ /** 该设备已绑定当前用户 */
+ PUSH_DEVICE_ALREADY_BOUND(60303, "error.60303"),
+ /** 推送发送失败 */
+ PUSH_SEND_FAILED(60304, "error.60304"),
+ /** 推送配置未初始化 */
+ PUSH_CONFIG_NOT_INITIALIZED(60305, "error.60305"),
+ /** 通话类型非法,仅支持 CALL_VOICE 或 CALL_VIDEO */
+ PUSH_CALL_TYPE_INVALID(60306, "error.60306"),
+ /** 不能向自己发送来电推送 */
+ PUSH_CALL_CANNOT_SELF(60307, "error.60307");
+
+ /**
+ * 错误码
+ */
+ private final int code;
+
+ /**
+ * 国际化消息 key,格式为 {@code error.{code}},由 GlobalExceptionHandler 通过 MessageSource 解析
+ */
+ private final String message;
+
+ ErrorCodeEnum(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ /**
+ * 根据错误码查找枚举
+ *
+ * @param code 错误码
+ * @return 对应的ErrorCodeEnum枚举,未找到时返回INTERNAL_ERROR
+ */
+ public static ErrorCodeEnum of(int code) {
+ for (ErrorCodeEnum errorCode : values()) {
+ if (errorCode.code == code) {
+ return errorCode;
+ }
+ }
+ return INTERNAL_ERROR;
+ }
+}
diff --git a/src/main/java/com/tailbet/common/exception/BusinessException.java b/src/main/java/com/tailbet/common/exception/BusinessException.java
new file mode 100644
index 0000000..04c0a0d
--- /dev/null
+++ b/src/main/java/com/tailbet/common/exception/BusinessException.java
@@ -0,0 +1,113 @@
+package com.tailbet.common.exception;
+
+import com.tailbet.common.enums.ErrorCodeEnum;
+import lombok.Getter;
+
+/**
+ * 业务异常
+ *
+ * 封装业务逻辑层面的异常,区别于系统运行时异常。
+ * 业务异常通常有明确的错误码和提示信息,可直接返回给前端展示。
+ * 使用时优先使用预定义的错误码枚举,也支持自定义消息。
+ *
+ *
+ * @author socialapp团队
+ * @since 1.0.0
+ */
+@Getter
+public class BusinessException extends RuntimeException {
+
+ /**
+ * 错误码
+ */
+ private final int code;
+
+ /**
+ * 附加数据(如当前场次信息等)
+ */
+ private final Object data;
+
+ /**
+ * 基于错误码枚举构造业务异常
+ *
+ * @param errorCode 错误码枚举
+ */
+ public BusinessException(ErrorCodeEnum errorCode) {
+ super(errorCode.getMessage());
+ this.code = errorCode.getCode();
+ this.data = null;
+ }
+
+ /**
+ * 基于错误码枚举和自定义消息构造业务异常
+ *
+ * @param errorCode 错误码枚举
+ * @param message 自定义错误消息
+ */
+ public BusinessException(ErrorCodeEnum errorCode, String message) {
+ super(message);
+ this.code = errorCode.getCode();
+ this.data = null;
+ }
+
+ /**
+ * 基于错误码枚举、自定义消息和原因构造业务异常
+ *
+ * @param errorCode 错误码枚举
+ * @param message 自定义错误消息
+ * @param cause 原始异常
+ */
+ public BusinessException(ErrorCodeEnum errorCode, String message, Throwable cause) {
+ super(message, cause);
+ this.code = errorCode.getCode();
+ this.data = null;
+ }
+
+ /**
+ * 基于状态码和自定义消息构造业务异常
+ *
+ * @param code 状态码
+ * @param message 错误消息
+ */
+ public BusinessException(int code, String message) {
+ super(message);
+ this.code = code;
+ this.data = null;
+ }
+
+ /**
+ * 基于自定义消息构造业务异常(默认错误码 INTERNAL_ERROR)
+ *
+ * @param message 错误消息
+ */
+ public BusinessException(String message) {
+ super(message);
+ this.code = ErrorCodeEnum.INTERNAL_ERROR.getCode();
+ this.data = null;
+ }
+
+ /**
+ * 基于错误码枚举和附加数据构造业务异常
+ *
+ * 用于需要向前端返回额外数据的异常场景,
+ * 如 LIVE_ALREADY_ACTIVE 时携带已有场次信息。
+ *
+ *
+ * @param errorCode 错误码枚举
+ * @param data 附加数据
+ */
+ public BusinessException(ErrorCodeEnum errorCode, Object data) {
+ super(errorCode.getMessage());
+ this.code = errorCode.getCode();
+ this.data = data;
+ }
+
+ /**
+ * 获取对应的错误码枚举
+ *
+ * @return 错误码枚举
+ */
+ public ErrorCodeEnum getErrorCodeEnum() {
+ return ErrorCodeEnum.of(this.code);
+ }
+}
diff --git a/src/main/java/com/tailbet/config/GlobalExceptionHandler.java b/src/main/java/com/tailbet/config/GlobalExceptionHandler.java
index a94f6f6..faae675 100644
--- a/src/main/java/com/tailbet/config/GlobalExceptionHandler.java
+++ b/src/main/java/com/tailbet/config/GlobalExceptionHandler.java
@@ -1,26 +1,208 @@
package com.tailbet.config;
+
+import com.tailbet.common.enums.ErrorCodeEnum;
+import com.tailbet.common.exception.BusinessException;
import com.tailbet.model.vo.R;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.MessageSource;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+
+import java.util.Locale;
+import java.util.stream.Collectors;
/**
- * 全局异常处理
+ * 全局异常处理器
+ *
+ * 统一捕获并处理Controller层抛出的各类异常,将异常转换为统一的Result响应格式。
+ * 处理优先级:具体异常 > 通用异常 > 兜底异常。
+ * 支持国际化:根据请求头 Accept-Language 自动返回对应语言的错误信息。
+ *
+ *
+ * @author socialapp团队
+ * @since 1.0.0
*/
@Slf4j
@RestControllerAdvice
+@RequiredArgsConstructor
public class GlobalExceptionHandler {
- @ExceptionHandler(RuntimeException.class)
- public R> handleRuntimeException(RuntimeException e) {
- log.error("业务异常: {}", e.getMessage());
- return R.fail(e.getMessage());
+ /**
+ * 国际化消息源,用于将消息 key 解析为对应语言的文本
+ */
+ private final MessageSource messageSource;
+
+
+ /**
+ * 处理业务异常(最高优先级)
+ *
+ * @param e 业务异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(BusinessException.class)
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public R> handleBusinessException(BusinessException e, HttpServletRequest request) {
+ // 业务异常的 message 可能是 key,也可能是业务自定义文本,统一走 resolveMessage 处理
+ String message = e.getMessage();
+ log.warn("【业务异常】URI: {}, 错误码: {}, 消息: {}",
+ request.getRequestURI(), e.getCode(), message);
+ if (e.getData() != null) {
+ R r = R.fail(e.getCode(), message);
+ r.setData(e.getData());
+ return r;
+ }
+ return R.fail(e.getCode(), message);
}
+ /**
+ * 处理参数校验异常(@Valid / @RequestBody)
+ *
+ * @param e 方法参数校验异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public R handleMethodArgumentNotValid(MethodArgumentNotValidException e, HttpServletRequest request) {
+ String message = e.getBindingResult().getFieldErrors().stream()
+ .map(FieldError::getDefaultMessage)
+ .collect(Collectors.joining(", "));
+ log.warn("【参数校验失败】URI: {}, 错误: {}", request.getRequestURI(), message);
+ // 参数校验失败的 message 来自注解,直接使用,不走国际化 key 解析
+ return R.fail(ErrorCodeEnum.PARAM_VALIDATE_ERROR, message);
+ }
+
+ /**
+ * 处理参数绑定异常(@ModelAttribute)
+ *
+ * @param e 参数绑定异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(BindException.class)
+ public R handleBindException(BindException e, HttpServletRequest request) {
+ String message = e.getFieldErrors().stream()
+ .map(error -> error.getField() + ": " + error.getDefaultMessage())
+ .collect(Collectors.joining(", "));
+ log.warn("【参数绑定失败】URI: {}, 错误: {}", request.getRequestURI(), message);
+ return R.fail(ErrorCodeEnum.PARAM_VALIDATE_ERROR, message);
+ }
+
+ /**
+ * 处理约束校验异常(@Validated 方法级别)
+ *
+ * @param e 约束校验异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(ConstraintViolationException.class)
+ public R handleConstraintViolation(ConstraintViolationException e, HttpServletRequest request) {
+ String message = e.getConstraintViolations().stream()
+ .map(ConstraintViolation::getMessage)
+ .collect(Collectors.joining(", "));
+ log.warn("【约束校验失败】URI: {}, 错误: {}", request.getRequestURI(), message);
+ return R.fail(ErrorCodeEnum.PARAM_VALIDATE_ERROR, message);
+ }
+
+ /**
+ * 处理缺少请求参数异常
+ *
+ * @param e 缺少参数异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(MissingServletRequestParameterException.class)
+ public R handleMissingParam(MissingServletRequestParameterException e, HttpServletRequest request) {
+ // 缺少参数的提示信息拼接参数名,不走国际化 key
+ String message = e.getMessage()
+ + ": " + e.getParameterName();
+ log.warn("【缺少参数】URI: {}, 错误: {}", request.getRequestURI(), message);
+ return R.fail(ErrorCodeEnum.PARAM_ERROR, message);
+ }
+
+ /**
+ * 处理请求体不可读异常(请求体缺失、格式错误、缺少必填字段等)
+ *
+ * @param e 请求体不可读异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(HttpMessageNotReadableException.class)
+ public R handleHttpMessageNotReadable(HttpMessageNotReadableException e, HttpServletRequest request) {
+ // 请求体格式错误,使用国际化后的错误码消息
+ String message = e.getMessage();
+ log.warn("【请求体不可读】URI: {}, 错误: {}", request.getRequestURI(), e.getMessage());
+ return R.fail(ErrorCodeEnum.PARAM_FORMAT_ERROR, message);
+ }
+
+ /**
+ * 处理参数类型不匹配异常
+ *
+ * @param e 参数类型不匹配异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(MethodArgumentTypeMismatchException.class)
+ public R handleTypeMismatch(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
+ String message = String.format("参数[%s]类型错误,期望类型: %s",
+ e.getName(), e.getRequiredType() != null ? e.getRequiredType().getSimpleName() : "未知");
+ log.warn("【参数类型错误】URI: {}, 错误: {}", request.getRequestURI(), message);
+ return R.fail(ErrorCodeEnum.PARAM_FORMAT_ERROR, message);
+ }
+
+ /**
+ * 处理非法参数异常
+ *
+ * @param e 非法参数异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(IllegalArgumentException.class)
+ public R handleIllegalArgument(IllegalArgumentException e, HttpServletRequest request) {
+ log.warn("【非法参数】URI: {}, 错误: {}", request.getRequestURI(), e.getMessage());
+ return R.fail(ErrorCodeEnum.PARAM_ERROR, e.getMessage());
+ }
+
+ /**
+ * 处理非法状态异常
+ *
+ * @param e 非法状态异常
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
+ @ExceptionHandler(IllegalStateException.class)
+ public R handleIllegalState(IllegalStateException e, HttpServletRequest request) {
+ log.warn("【非法状态】URI: {}, 错误: {}", request.getRequestURI(), e.getMessage());
+ return R.fail(ErrorCodeEnum.OPERATION_NOT_ALLOWED, e.getMessage());
+ }
+
+ /**
+ * 兜底异常处理器(最低优先级)
+ *
+ * 捕获所有未被前面处理器捕获的异常,避免异常信息直接暴露给客户端。
+ *
+ *
+ * @param e 异常对象
+ * @param request HTTP请求
+ * @return 统一错误响应
+ */
@ExceptionHandler(Exception.class)
- public R> handleException(Exception e) {
- log.error("系统异常: {}", e.getMessage(), e);
- return R.fail("系统错误: " + e.getMessage());
+ public R handleException(Exception e, HttpServletRequest request) {
+ log.error("【系统异常】URI: {}, 错误类型: {}, 消息: {}",
+ request.getRequestURI(), e.getClass().getName(), e.getMessage(), e);
+ // 兜底异常使用国际化后的系统繁忙提示
+ return R.fail(ErrorCodeEnum.INTERNAL_ERROR.getCode(), e.getMessage());
}
}
diff --git a/src/main/java/com/tailbet/config/OpenIMConfig.java b/src/main/java/com/tailbet/config/OpenIMConfig.java
deleted file mode 100644
index 589c301..0000000
--- a/src/main/java/com/tailbet/config/OpenIMConfig.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.tailbet.config;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-/**
- * OpenIM配置
- */
-@Data
-@Component
-@ConfigurationProperties(prefix = "openim")
-public class OpenIMConfig {
-
- /**
- * OpenIM API地址
- */
- private String apiUrl;
-
- /**
- * OpenIM WebSocket地址
- */
- private String wsUrl;
-
- /**
- * 管理员账号
- */
- private String adminUser;
-
- /**
- * 管理员Token
- */
- private String adminToken;
-}
diff --git a/src/main/java/com/tailbet/config/OpenIMProperties.java b/src/main/java/com/tailbet/config/OpenIMProperties.java
new file mode 100644
index 0000000..267576a
--- /dev/null
+++ b/src/main/java/com/tailbet/config/OpenIMProperties.java
@@ -0,0 +1,144 @@
+package com.tailbet.config;
+
+import cn.hutool.core.util.StrUtil;
+import jakarta.annotation.PostConstruct;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * OpenIM 集成配置属性类
+ *
+ * 从 Nacos/application.yml 中读取 OpenIM 第三方即时通讯服务相关配置属性,
+ * 对应配置前缀为 {@code openim}。OpenIM 是独立部署的外部第三方服务(非 Spring 微服务),
+ * 不参与 Nacos 服务发现,因此配置项中直接使用完整的 HTTP/WS 地址。
+ *
+ *
+ * @author socialapp团队
+ * @since 1.0.0
+ */
+@Slf4j
+@Data
+@Component
+@ConfigurationProperties(prefix = "openim")
+public class OpenIMProperties {
+
+ /**
+ * OpenIM REST API 外网地址,下发给客户端 {@code httpUrl}(如 https://im-api-pre.aimchat.im)
+ *
+ * 历史配置中亦可能同时作为服务端调用地址;配置了 {@link #internalApiUrl} 后仅用于下发。
+ *
+ */
+ private String apiUrl;
+
+ /**
+ * OpenIM REST API 内网地址,服务端 {@code OpenIMApiClient} 调用(如 http://10.x.x.x:10002)
+ *
+ * 未配置时回落 {@link #apiUrl},兼容旧 Nacos。
+ *
+ */
+ private String internalApiUrl;
+
+ /**
+ * OpenIM WebSocket 外网地址,下发给客户端用于 IM SDK 初始化(如 wss://im-ws-pre.aimchat.im)
+ */
+ private String wsUrl;
+
+ /**
+ * OpenIM 管理员密钥,用于调用管理接口换取 adminToken
+ * 生产环境通过 Nacos 敏感配置注入,禁止硬编码
+ */
+ private String secret;
+
+ /**
+ * OpenIM 管理员用户 ID,默认 imAdmin
+ */
+ private String adminUserId = "imAdmin";
+
+ /**
+ * 应用标识,用于 operationID 生成前缀
+ */
+ private String appId = "socialapp";
+
+ /**
+ * adminToken 有效期(秒),用于定时刷新任务计算刷新周期,默认 82800 秒(23小时)
+ */
+ private int tokenExpireSeconds = 82800;
+
+ /**
+ * 调用 OpenIM 接口的连接超时时间(毫秒),默认 5000
+ */
+ private int connectTimeoutMs = 5000;
+
+ /**
+ * 调用 OpenIM 接口的读取超时时间(毫秒),默认 10000
+ */
+ private int readTimeoutMs = 10000;
+
+ /**
+ * Webhook 签名密钥,用于校验 OpenIM 服务端回调请求的合法性(对应请求头 X-Webhook-Secret)
+ * 为空时跳过校验并输出警告日志
+ */
+ private String webhookSecret;
+
+ /**
+ * 配置加载后的校验方法
+ * 检查必填配置是否已从 Nacos 注入,未配置则输出警告日志
+ */
+ @PostConstruct
+ public void validate() {
+ if (StrUtil.isBlank(apiUrl)) {
+ log.warn("【OpenIM配置】apiUrl(外网下发 httpUrl)未配置,OpenIM 相关功能将不可用");
+ }
+ if (StrUtil.isBlank(internalApiUrl)) {
+ log.warn("【OpenIM配置】internalApiUrl(内网调用)未配置,服务端将回落 apiUrl");
+ }
+ if (StrUtil.isBlank(secret)) {
+ log.warn("【OpenIM配置】secret 未配置,管理员Token获取将失败");
+ }
+ if (StrUtil.isBlank(wsUrl)) {
+ log.warn("【OpenIM配置】wsUrl(外网下发)未配置,客户端 SDK 可能无法初始化");
+ }
+ if (StrUtil.isNotBlank(apiUrl)) {
+ log.info("【OpenIM配置】加载完成,外网apiUrl: {}, 内网internalApiUrl: {}, wsUrl: {}, adminUserId: {}, secret: {}",
+ apiUrl,
+ StrUtil.blankToDefault(internalApiUrl, "(回落 api-url)"),
+ wsUrl,
+ adminUserId,
+ maskString(secret));
+ }
+ }
+
+ /**
+ * 解析服务端调用 OpenIM REST 基址(优先内网)
+ *
+ * @return HTTP(S) 基址,末尾无斜杠约定由调用方拼接 path
+ */
+ public String resolveServerApiUrl() {
+ return StrUtil.blankToDefault(internalApiUrl, apiUrl);
+ }
+
+ /**
+ * 解析下发给客户端的 OpenIM HTTP API 地址(外网)
+ *
+ * @return 外网 api-url
+ */
+ public String resolvePublicApiUrl() {
+ return apiUrl;
+ }
+
+ /**
+ * 对敏感字符串进行脱敏处理
+ * 只显示前3位和后3位,中间用星号代替
+ *
+ * @param str 原始字符串
+ * @return 脱敏后的字符串
+ */
+ private String maskString(String str) {
+ if (str == null || str.length() <= 6) {
+ return "***";
+ }
+ return str.substring(0, 3) + "****" + str.substring(str.length() - 3);
+ }
+}
diff --git a/src/main/java/com/tailbet/model/vo/R.java b/src/main/java/com/tailbet/model/vo/R.java
index 1978560..2ba0243 100644
--- a/src/main/java/com/tailbet/model/vo/R.java
+++ b/src/main/java/com/tailbet/model/vo/R.java
@@ -1,5 +1,6 @@
package com.tailbet.model.vo;
+import com.tailbet.common.enums.ErrorCodeEnum;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@@ -49,6 +50,9 @@ public class R implements Serializable {
public static R fail(int code, String msg) {
return new R<>(code, msg, null);
}
+ public static R fail(ErrorCodeEnum errorCode, String msg) {
+ return new R<>(errorCode.getCode(), msg, null);
+ }
public boolean isSuccess() {
return code == SUCCESS;
diff --git a/src/main/java/com/tailbet/openim/OpenIMAdminTokenManager.java b/src/main/java/com/tailbet/openim/OpenIMAdminTokenManager.java
new file mode 100644
index 0000000..128548e
--- /dev/null
+++ b/src/main/java/com/tailbet/openim/OpenIMAdminTokenManager.java
@@ -0,0 +1,170 @@
+package com.tailbet.openim;
+
+
+import com.tailbet.common.enums.ErrorCodeEnum;
+import com.tailbet.common.exception.BusinessException;
+import com.tailbet.config.OpenIMProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * OpenIM adminToken 管理器
+ *
+ * 负责 OpenIM 管理员 Token 的获取与定期刷新:服务启动时通过 OpenIM
+ * {@code /auth/get_admin_token} 接口获取一次,之后每 23 小时自动刷新一次
+ * (OpenIM 管理员 Token 默认 24 小时过期,提前 1 小时刷新以规避临界失效)。
+ *
+ *
+ * 本类不依赖 {@link OpenIMApiClient},避免出现
+ * OpenIMApiClient → OpenIMAdminTokenManager → OpenIMApiClient 的循环依赖,
+ * 内部直接使用专用 RestTemplate 调用 OpenIM 接口。
+ *
+ *
+ * @author socialapp团队
+ * @since 1.0.0
+ */
+@RequiredArgsConstructor
+@Slf4j
+@Component
+public class OpenIMAdminTokenManager {
+
+ /**
+ * 定时刷新周期:23 小时(毫秒)
+ */
+ private static final long REFRESH_INTERVAL_MS = 23 * 60 * 60 * 1000L;
+
+ private final RestTemplate restTemplate;
+
+ /**
+ * OpenIM 集成配置属性
+ */
+ private final OpenIMProperties openIMProperties;
+
+ /**
+ * 当前有效的 adminToken,使用原子引用保证多线程读取的可见性与线程安全
+ */
+ private final AtomicReference tokenRef = new AtomicReference<>("");
+
+ /**
+ * 服务启动时初始化 adminToken
+ *
+ * 若启动时获取失败,仅记录错误日志,不阻塞服务启动;
+ * 后续定时任务会持续尝试刷新,业务调用方在 adminToken 为空时会因鉴权失败而收到明确异常。
+ *
+ */
+ @PostConstruct
+ public void init() {
+ try {
+ refreshToken();
+ } catch (Exception e) {
+ log.error("【OpenIM】服务启动时获取 adminToken 失败: {}", e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 每 23 小时自动刷新 adminToken(避免 24 小时过期)
+ */
+ @Scheduled(fixedDelay = REFRESH_INTERVAL_MS)
+ public void scheduledRefresh() {
+ try {
+ refreshToken();
+ } catch (Exception e) {
+ log.error("【OpenIM】定时刷新 adminToken 失败: {}", e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 获取当前有效的 adminToken
+ *
+ * 若 token 尚未初始化(null 或空),则尝试立即刷新一次,防止 NPE 及空 token 导致的鉴权失败。
+ *
+ *
+ * @return adminToken 字符串,刷新失败时返回空字符串
+ */
+ public String getAdminToken() {
+ String token = tokenRef.get();
+ if (token == null || token.isEmpty()) {
+ log.warn("【OpenIM】adminToken 尚未初始化,尝试立即刷新");
+ refreshToken();
+ token = tokenRef.get();
+ }
+ return token != null ? token : "";
+ }
+
+ /**
+ * 调用 OpenIM {@code /auth/get_admin_token} 接口刷新 adminToken
+ *
+ * 获取 adminToken 属于鉴权前置操作,请求本身不能携带 adminToken 请求头。
+ * 方法加锁,避免同进程内并发刷新互相顶掉(OpenIM 同 user 新 token 会使旧 token 变为 TokenKicked)。
+ *
+ */
+ public synchronized void refreshToken() {
+ String url = openIMProperties.resolveServerApiUrl() + "/auth/get_admin_token";
+
+ Map body = new HashMap<>();
+ body.put("secret", openIMProperties.getSecret());
+ body.put("userID", openIMProperties.getAdminUserId());
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("operationID", UUID.randomUUID().toString());
+ HttpEntity