2026-03-13 09:38:18 +08:00
|
|
|
|
package errors
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// AppError 统一的应用错误类型
|
|
|
|
|
|
type AppError struct {
|
|
|
|
|
|
Code string
|
|
|
|
|
|
Message string
|
|
|
|
|
|
Cause error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (e *AppError) Error() string {
|
|
|
|
|
|
if e.Cause != nil {
|
|
|
|
|
|
return fmt.Sprintf("[%s] %s: %v", e.Code, e.Message, e.Cause)
|
|
|
|
|
|
}
|
|
|
|
|
|
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (e *AppError) Unwrap() error {
|
|
|
|
|
|
return e.Cause
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 预定义错误
|
|
|
|
|
|
var (
|
|
|
|
|
|
// 通用错误
|
|
|
|
|
|
ErrNotFound = &AppError{Code: "NOT_FOUND", Message: "资源不存在"}
|
|
|
|
|
|
ErrUnauthorized = &AppError{Code: "UNAUTHORIZED", Message: "未授权"}
|
|
|
|
|
|
ErrForbidden = &AppError{Code: "FORBIDDEN", Message: "禁止访问"}
|
|
|
|
|
|
ErrBadRequest = &AppError{Code: "BAD_REQUEST", Message: "请求参数错误"}
|
|
|
|
|
|
ErrInternal = &AppError{Code: "INTERNAL_ERROR", Message: "内部服务器错误"}
|
|
|
|
|
|
|
|
|
|
|
|
// 用户相关错误
|
|
|
|
|
|
ErrUserNotFound = &AppError{Code: "USER_NOT_FOUND", Message: "用户不存在"}
|
|
|
|
|
|
ErrUserAlreadyExists = &AppError{Code: "USER_ALREADY_EXISTS", Message: "用户已存在"}
|
|
|
|
|
|
ErrInvalidCredentials = &AppError{Code: "INVALID_CREDENTIALS", Message: "用户名或密码错误"}
|
|
|
|
|
|
ErrInvalidUsername = &AppError{Code: "INVALID_USERNAME", Message: "无效的用户名"}
|
|
|
|
|
|
ErrInvalidEmail = &AppError{Code: "INVALID_EMAIL", Message: "无效的邮箱"}
|
|
|
|
|
|
ErrInvalidPhone = &AppError{Code: "INVALID_PHONE", Message: "无效的手机号"}
|
|
|
|
|
|
ErrWeakPassword = &AppError{Code: "WEAK_PASSWORD", Message: "密码强度不足"}
|
|
|
|
|
|
ErrUsernameExists = &AppError{Code: "USERNAME_EXISTS", Message: "用户名已存在"}
|
|
|
|
|
|
ErrEmailExists = &AppError{Code: "EMAIL_EXISTS", Message: "邮箱已存在"}
|
|
|
|
|
|
ErrPhoneExists = &AppError{Code: "PHONE_EXISTS", Message: "手机号已存在"}
|
|
|
|
|
|
ErrUserBanned = &AppError{Code: "USER_BANNED", Message: "用户已被封禁"}
|
|
|
|
|
|
ErrUserBlocked = &AppError{Code: "USER_BLOCKED", Message: "存在屏蔽关系"}
|
|
|
|
|
|
ErrInvalidOperation = &AppError{Code: "INVALID_OPERATION", Message: "无效操作"}
|
|
|
|
|
|
ErrEmailServiceUnavailable = &AppError{Code: "EMAIL_SERVICE_UNAVAILABLE", Message: "邮件服务不可用"}
|
|
|
|
|
|
ErrVerificationCodeTooFrequent = &AppError{Code: "VERIFICATION_CODE_TOO_FREQUENT", Message: "验证码发送过于频繁"}
|
|
|
|
|
|
ErrVerificationCodeInvalid = &AppError{Code: "VERIFICATION_CODE_INVALID", Message: "验证码无效"}
|
|
|
|
|
|
ErrVerificationCodeExpired = &AppError{Code: "VERIFICATION_CODE_EXPIRED", Message: "验证码已过期"}
|
|
|
|
|
|
ErrVerificationCodeUnavailable = &AppError{Code: "VERIFICATION_CODE_UNAVAILABLE", Message: "验证码存储不可用"}
|
|
|
|
|
|
ErrEmailAlreadyVerified = &AppError{Code: "EMAIL_ALREADY_VERIFIED", Message: "邮箱已验证"}
|
|
|
|
|
|
ErrEmailNotBound = &AppError{Code: "EMAIL_NOT_BOUND", Message: "邮箱未绑定"}
|
|
|
|
|
|
|
|
|
|
|
|
// 帖子相关错误
|
|
|
|
|
|
ErrPostNotFound = &AppError{Code: "POST_NOT_FOUND", Message: "帖子不存在"}
|
|
|
|
|
|
ErrPostDeleted = &AppError{Code: "POST_DELETED", Message: "帖子已删除"}
|
|
|
|
|
|
|
|
|
|
|
|
// 群组相关错误
|
|
|
|
|
|
ErrGroupNotFound = &AppError{Code: "GROUP_NOT_FOUND", Message: "群组不存在"}
|
|
|
|
|
|
ErrNotGroupMember = &AppError{Code: "NOT_GROUP_MEMBER", Message: "不是群组成员"}
|
|
|
|
|
|
ErrAlreadyGroupMember = &AppError{Code: "ALREADY_GROUP_MEMBER", Message: "已是群组成员"}
|
|
|
|
|
|
ErrNotGroupAdmin = &AppError{Code: "NOT_GROUP_ADMIN", Message: "不是群管理员"}
|
|
|
|
|
|
ErrNotGroupOwner = &AppError{Code: "NOT_GROUP_OWNER", Message: "不是群主"}
|
|
|
|
|
|
ErrGroupFull = &AppError{Code: "GROUP_FULL", Message: "群已满"}
|
|
|
|
|
|
ErrCannotRemoveOwner = &AppError{Code: "CANNOT_REMOVE_OWNER", Message: "不能移除群主"}
|
|
|
|
|
|
ErrCannotMuteOwner = &AppError{Code: "CANNOT_MUTE_OWNER", Message: "不能禁言群主"}
|
|
|
|
|
|
ErrMuted = &AppError{Code: "MUTED", Message: "你已被禁言"}
|
|
|
|
|
|
ErrMuteAllEnabled = &AppError{Code: "MUTE_ALL_ENABLED", Message: "全员禁言中"}
|
|
|
|
|
|
ErrCannotJoin = &AppError{Code: "CANNOT_JOIN", Message: "该群不允许加入"}
|
|
|
|
|
|
ErrJoinRequestPending = &AppError{Code: "JOIN_REQUEST_PENDING", Message: "加群申请已提交"}
|
|
|
|
|
|
ErrGroupRequestNotFound = &AppError{Code: "GROUP_REQUEST_NOT_FOUND", Message: "加群请求不存在"}
|
|
|
|
|
|
ErrGroupRequestHandled = &AppError{Code: "GROUP_REQUEST_HANDLED", Message: "该加群请求已处理"}
|
|
|
|
|
|
ErrNotRequestTarget = &AppError{Code: "NOT_REQUEST_TARGET", Message: "不是邀请目标用户"}
|
|
|
|
|
|
ErrNoEligibleInvitee = &AppError{Code: "NO_ELIGIBLE_INVITEE", Message: "没有可邀请的用户"}
|
|
|
|
|
|
ErrNotMutualFollow = &AppError{Code: "NOT_MUTUAL_FOLLOW", Message: "仅支持邀请互相关注用户"}
|
|
|
|
|
|
ErrInviteNotAccepted = &AppError{Code: "INVITE_NOT_ACCEPTED", Message: "被邀请人尚未同意邀请,无法审批"}
|
|
|
|
|
|
|
|
|
|
|
|
// 表情包相关错误
|
|
|
|
|
|
ErrStickerAlreadyExists = &AppError{Code: "STICKER_ALREADY_EXISTS", Message: "表情包已存在"}
|
|
|
|
|
|
ErrInvalidStickerURL = &AppError{Code: "INVALID_STICKER_URL", Message: "无效的表情包URL"}
|
|
|
|
|
|
|
|
|
|
|
|
// 日程相关错误
|
|
|
|
|
|
ErrInvalidSchedulePayload = &AppError{Code: "INVALID_SCHEDULE_PAYLOAD", Message: "无效的日程数据"}
|
|
|
|
|
|
ErrScheduleCourseNotFound = &AppError{Code: "SCHEDULE_COURSE_NOT_FOUND", Message: "日程课程不存在"}
|
|
|
|
|
|
ErrScheduleForbidden = &AppError{Code: "SCHEDULE_FORBIDDEN", Message: "禁止的日程操作"}
|
|
|
|
|
|
ErrScheduleColorDuplicated = &AppError{Code: "SCHEDULE_COLOR_DUPLICATED", Message: "课程颜色已被使用"}
|
|
|
|
|
|
|
|
|
|
|
|
// 通知相关错误
|
|
|
|
|
|
ErrUnauthorizedNotification = &AppError{Code: "UNAUTHORIZED_NOTIFICATION", Message: "无权删除此通知"}
|
2026-03-14 02:09:38 +08:00
|
|
|
|
|
|
|
|
|
|
// Casbin 相关错误
|
|
|
|
|
|
ErrPermissionDenied = &AppError{Code: "PERMISSION_DENIED", Message: "权限不足"}
|
|
|
|
|
|
ErrRoleNotFound = &AppError{Code: "ROLE_NOT_FOUND", Message: "角色不存在"}
|
|
|
|
|
|
ErrRoleAlreadyAssigned = &AppError{Code: "ROLE_ALREADY_ASSIGNED", Message: "用户已拥有该角色"}
|
|
|
|
|
|
ErrRoleNotAssigned = &AppError{Code: "ROLE_NOT_ASSIGNED", Message: "用户未拥有该角色"}
|
|
|
|
|
|
ErrCannotModifyOwnRole = &AppError{Code: "CANNOT_MODIFY_OWN_ROLE", Message: "不能修改自己的角色"}
|
|
|
|
|
|
ErrCannotModifySuperAdmin = &AppError{Code: "CANNOT_MODIFY_SUPER_ADMIN", Message: "不能修改超级管理员角色"}
|
|
|
|
|
|
ErrCasbinInternal = &AppError{Code: "CASBIN_INTERNAL_ERROR", Message: "权限系统内部错误"}
|
2026-03-28 07:03:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 通话相关错误
|
|
|
|
|
|
ErrCallNotFound = &AppError{Code: "CALL_NOT_FOUND", Message: "通话不存在"}
|
|
|
|
|
|
ErrCallNotActive = &AppError{Code: "CALL_NOT_ACTIVE", Message: "通话未激活"}
|
|
|
|
|
|
ErrCallInProgress = &AppError{Code: "CALL_IN_PROGRESS", Message: "用户之间已有通话"}
|
|
|
|
|
|
ErrNotCallParticipant = &AppError{Code: "NOT_CALL_PARTICIPANT", Message: "不是通话参与者"}
|
|
|
|
|
|
ErrInvalidCallState = &AppError{Code: "INVALID_CALL_STATE", Message: "通话状态无效"}
|
|
|
|
|
|
ErrCalleeOffline = &AppError{Code: "CALLEE_OFFLINE", Message: "对方不在线"}
|
|
|
|
|
|
ErrCallAlreadyAnswered = &AppError{Code: "CALL_ALREADY_ANSWERED", Message: "通话已在其他设备应答"}
|
2026-06-07 00:11:47 +08:00
|
|
|
|
ErrGroupCallNotSupported = &AppError{Code: "GROUP_CALL_NOT_SUPPORTED", Message: "群组通话不支持"}
|
|
|
|
|
|
ErrMaxParticipantsReached = &AppError{Code: "MAX_PARTICIPANTS_REACHED", Message: "通话人数已达上限"}
|
2026-03-28 07:03:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 消息相关错误
|
2026-04-05 20:27:03 +08:00
|
|
|
|
ErrConversationNotFound = &AppError{Code: "CONVERSATION_NOT_FOUND", Message: "会话不存在或无权限"}
|
|
|
|
|
|
ErrNotConversationMember = &AppError{Code: "NOT_CONVERSATION_MEMBER", Message: "不是会话参与者"}
|
|
|
|
|
|
ErrMessageNotFound = &AppError{Code: "MESSAGE_NOT_FOUND", Message: "消息不存在"}
|
|
|
|
|
|
ErrMessageAlreadyRecalled = &AppError{Code: "MESSAGE_ALREADY_RECALLED", Message: "消息已撤回"}
|
|
|
|
|
|
ErrMessageRecallTimeout = &AppError{Code: "MESSAGE_RECALL_TIMEOUT", Message: "消息撤回超时(2分钟)"}
|
|
|
|
|
|
ErrCannotSendMessage = &AppError{Code: "CANNOT_SEND_MESSAGE", Message: "无法发送消息"}
|
|
|
|
|
|
ErrCannotSendImage = &AppError{Code: "CANNOT_SEND_IMAGE", Message: "对方未关注你,暂不支持发送图片"}
|
|
|
|
|
|
ErrMessageLimitReached = &AppError{Code: "MESSAGE_LIMIT_REACHED", Message: "对方未关注你前,仅允许发送一条消息"}
|
|
|
|
|
|
ErrQRCodeSessionExpired = &AppError{Code: "QRCODE_SESSION_EXPIRED", Message: "二维码已过期"}
|
|
|
|
|
|
ErrQRCodeAlreadyScanned = &AppError{Code: "QRCODE_ALREADY_SCANNED", Message: "二维码已被扫描"}
|
|
|
|
|
|
ErrQRCodeInvalidStatus = &AppError{Code: "QRCODE_INVALID_STATUS", Message: "二维码状态无效"}
|
|
|
|
|
|
ErrPushNotImplemented = &AppError{Code: "PUSH_NOT_IMPLEMENTED", Message: "推送服务未实现"}
|
|
|
|
|
|
ErrPushQueueFull = &AppError{Code: "PUSH_QUEUE_FULL", Message: "推送队列已满"}
|
|
|
|
|
|
ErrUserOffline = &AppError{Code: "USER_OFFLINE", Message: "用户不在线"}
|
2026-03-28 07:03:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 学习资料相关错误
|
|
|
|
|
|
ErrSubjectHasMaterials = &AppError{Code: "SUBJECT_HAS_MATERIALS", Message: "学科下存在资料,无法删除"}
|
2026-04-05 20:27:03 +08:00
|
|
|
|
|
2026-06-26 17:00:38 +08:00
|
|
|
|
// 帖子创建幂等性:同一 client_request_id 的请求仍在处理中(占位未回填真实结果)
|
|
|
|
|
|
ErrDuplicatePostRequest = &AppError{Code: "DUPLICATE_POST_REQUEST", Message: "正在发布中,请稍候"}
|
|
|
|
|
|
|
2026-04-05 20:27:03 +08:00
|
|
|
|
// 身份认证相关错误
|
|
|
|
|
|
ErrVerificationPending = &AppError{Code: "VERIFICATION_PENDING", Message: "已有待审核的认证申请,请等待审核"}
|
|
|
|
|
|
ErrVerificationNotFound = &AppError{Code: "VERIFICATION_NOT_FOUND", Message: "认证记录不存在"}
|
|
|
|
|
|
ErrInvalidIdentity = &AppError{Code: "INVALID_IDENTITY", Message: "无效的身份类型"}
|
|
|
|
|
|
ErrAlreadyVerified = &AppError{Code: "ALREADY_VERIFIED", Message: "已完成身份认证,无需重复申请"}
|
|
|
|
|
|
ErrVerificationAlreadyHandled = &AppError{Code: "VERIFICATION_ALREADY_HANDLED", Message: "该认证申请已被处理"}
|
|
|
|
|
|
ErrVerificationRequired = &AppError{Code: "VERIFICATION_REQUIRED", Message: "需要完成身份认证"}
|
2026-04-24 15:14:44 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化相关错误
|
|
|
|
|
|
ErrSetupAlreadyCompleted = &AppError{Code: "SETUP_ALREADY_COMPLETED", Message: "超级管理员已初始化,无法重复设置"}
|
|
|
|
|
|
ErrInvalidSetupSecret = &AppError{Code: "INVALID_SETUP_SECRET", Message: "初始化密钥无效"}
|
|
|
|
|
|
ErrSetupSecretNotConfigured = &AppError{Code: "SETUP_SECRET_NOT_CONFIGURED", Message: "未配置初始化密钥,无法使用此接口"}
|
2026-03-13 09:38:18 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Wrap 包装错误
|
|
|
|
|
|
func Wrap(err error, appErr *AppError) *AppError {
|
|
|
|
|
|
return &AppError{
|
|
|
|
|
|
Code: appErr.Code,
|
|
|
|
|
|
Message: appErr.Message,
|
|
|
|
|
|
Cause: err,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// As 将错误转换为 AppError
|
|
|
|
|
|
func As(err error) (*AppError, bool) {
|
|
|
|
|
|
var appErr *AppError
|
|
|
|
|
|
if errors.As(err, &appErr) {
|
|
|
|
|
|
return appErr, true
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil, false
|
|
|
|
|
|
}
|