diff --git a/viewsh-module-system/viewsh-module-system-api/src/main/java/com/viewsh/module/system/enums/ErrorCodeConstants.java b/viewsh-module-system/viewsh-module-system-api/src/main/java/com/viewsh/module/system/enums/ErrorCodeConstants.java index badcd4e..f820f9e 100644 --- a/viewsh-module-system/viewsh-module-system-api/src/main/java/com/viewsh/module/system/enums/ErrorCodeConstants.java +++ b/viewsh-module-system/viewsh-module-system-api/src/main/java/com/viewsh/module/system/enums/ErrorCodeConstants.java @@ -1,171 +1,174 @@ -package com.viewsh.module.system.enums; - -import com.viewsh.framework.common.exception.ErrorCode; - -/** - * System 错误码枚举类 - * - * system 系统,使用 1-002-000-000 段 - */ -public interface ErrorCodeConstants { - - // ========== AUTH 模块 1-002-000-000 ========== - ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确"); - ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用"); - ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}"); - ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定"); - ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在"); - ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "验证码不正确,原因:{}"); - - // ========== 菜单模块 1-002-001-000 ========== - ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单"); - ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1_002_001_001, "父菜单不存在"); - ErrorCode MENU_PARENT_ERROR = new ErrorCode(1_002_001_002, "不能设置自己为父菜单"); - ErrorCode MENU_NOT_EXISTS = new ErrorCode(1_002_001_003, "菜单不存在"); - ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除"); - ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单"); - ErrorCode MENU_COMPONENT_NAME_DUPLICATE = new ErrorCode(1_002_001_006, "已经存在该组件名的菜单"); - - // ========== 角色模块 1-002-002-000 ========== - ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在"); - ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色"); - ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在标识为【{}】的角色"); - ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色"); - ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用"); - ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用"); - - // ========== 用户模块 1-002-003-000 ========== - ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1_002_003_000, "用户账号已经存在"); - ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1_002_003_001, "手机号已经存在"); - ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1_002_003_002, "邮箱已经存在"); - ErrorCode USER_NOT_EXISTS = new ErrorCode(1_002_003_003, "用户不存在"); - ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_002_003_004, "导入用户数据不能为空!"); - ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败"); - ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); - ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); - ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); - ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); - ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭"); - - // ========== 部门模块 1-002-004-000 ========== - ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); - ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在"); - ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在"); - ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除"); - ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门"); - ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); - ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); - - // ========== 岗位模块 1-002-005-000 ========== - ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在"); - ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择"); - ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位"); - ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位"); - - // ========== 字典类型 1-002-006-000 ========== - ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在"); - ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1_002_006_002, "字典类型不处于开启状态,不允许选择"); - ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1_002_006_003, "已经存在该名字的字典类型"); - ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1_002_006_004, "已经存在该类型的字典类型"); - ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1_002_006_005, "无法删除,该字典类型还有字典数据"); - - // ========== 字典数据 1-002-007-000 ========== - ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1_002_007_001, "当前字典数据不存在"); - ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1_002_007_002, "字典数据({})不处于开启状态,不允许选择"); - ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1_002_007_003, "已经存在该值的字典数据"); - - // ========== 通知公告 1-002-008-000 ========== - ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在"); - - // ========== 短信渠道 1-002-011-000 ========== - ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1_002_011_000, "短信渠道不存在"); - ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1_002_011_001, "短信渠道不处于开启状态,不允许选择"); - ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1_002_011_002, "无法删除,该短信渠道还有短信模板"); - - // ========== 短信模板 1-002-012-000 ========== - ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在"); - ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板"); - ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}"); - ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中"); - ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}"); - ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在"); - - // ========== 短信发送 1-002-013-000 ========== - ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在"); - ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_013_001, "模板参数({})缺失"); - ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_013_002, "短信模板不存在"); - - // ========== 短信验证码 1-002-014-000 ========== - ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在"); - ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期"); - ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用"); - ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量"); - ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁"); - - // ========== 租户信息 1-002-015-000 ========== - ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在"); - ErrorCode TENANT_DISABLE = new ErrorCode(1_002_015_001, "名字为【{}】的租户已被禁用"); - ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期"); - ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!"); - ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在"); - ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在"); - - // ========== 租户套餐 1-002-016-000 ========== - ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在"); - ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); - ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用"); - ErrorCode TENANT_PACKAGE_NAME_DUPLICATE = new ErrorCode(1_002_016_003, "已经存在该名字的租户套餐"); - - // ========== 社交用户 1-002-018-000 ========== - ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1_002_018_000, "社交授权失败,原因是:{}"); - ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_001, "社交授权失败,找不到对应的用户"); - - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_200, "获得手机号失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR = new ErrorCode(1_002_018_204, "上传微信小程序发货信息失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR = new ErrorCode(1_002_018_205, "上传微信小程序订单收货信息失败"); - ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在"); - ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置"); - - // ========== OAuth2 客户端 1-002-020-000 ========= - ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); - ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在"); - ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1_002_020_002, "OAuth2 客户端已禁用"); - ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1_002_020_003, "不支持该授权类型"); - ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1_002_020_004, "授权范围过大"); - ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1_002_020_005, "无效 redirect_uri: {}"); - ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1_002_020_006, "无效 client_secret: {}"); - - // ========== OAuth2 授权 1-002-021-000 ========= - ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配"); - ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配"); - ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配"); - - // ========== OAuth2 授权 1-002-022-000 ========= - ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在"); - ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期"); - - // ========== 邮箱账号 1-002-023-000 ========== - ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1_002_023_000, "邮箱账号不存在"); - ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1_002_023_001, "无法删除,该邮箱账号还有邮件模板"); - - // ========== 邮件模版 1-002-024-000 ========== - ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_024_000, "邮件模版不存在"); - ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1_002_024_001, "邮件模版 code({}) 已存在"); - - // ========== 邮件发送 1-002-025-000 ========== - ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_025_000, "模板参数({})缺失"); - ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1_002_025_001, "邮箱不存在"); - - // ========== 站内信模版 1-002-026-000 ========== - ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在"); - ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_026_001, "已经存在编码为【{}】的站内信模板"); - - // ========== 站内信模版 1-002-027-000 ========== - - // ========== 站内信发送 1-002-028-000 ========== - ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); - -} +package com.viewsh.module.system.enums; + +import com.viewsh.framework.common.exception.ErrorCode; + +/** + * System 错误码枚举类 + * + * system 系统,使用 1-002-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== AUTH 模块 1-002-000-000 ========== + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确"); + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用"); + ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在"); + ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "验证码不正确,原因:{}"); + ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_NOT_FOUND = new ErrorCode(1_002_000_009, "手机号未关联管理员账号"); + ErrorCode AUTH_WEIXIN_MINI_APP_BINDTO_OTHER_WECHAT = new ErrorCode(1_002_000_010, "该账号已绑定其他微信,请先解绑"); + ErrorCode AUTH_WEIXIN_MINI_APP_WECHAT_BINDTO_OTHER = new ErrorCode(1_002_000_011, "该微信已绑定其他账号"); + + // ========== 菜单模块 1-002-001-000 ========== + ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单"); + ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1_002_001_001, "父菜单不存在"); + ErrorCode MENU_PARENT_ERROR = new ErrorCode(1_002_001_002, "不能设置自己为父菜单"); + ErrorCode MENU_NOT_EXISTS = new ErrorCode(1_002_001_003, "菜单不存在"); + ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除"); + ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单"); + ErrorCode MENU_COMPONENT_NAME_DUPLICATE = new ErrorCode(1_002_001_006, "已经存在该组件名的菜单"); + + // ========== 角色模块 1-002-002-000 ========== + ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在"); + ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色"); + ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在标识为【{}】的角色"); + ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色"); + ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用"); + ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用"); + + // ========== 用户模块 1-002-003-000 ========== + ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1_002_003_000, "用户账号已经存在"); + ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1_002_003_001, "手机号已经存在"); + ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1_002_003_002, "邮箱已经存在"); + ErrorCode USER_NOT_EXISTS = new ErrorCode(1_002_003_003, "用户不存在"); + ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_002_003_004, "导入用户数据不能为空!"); + ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败"); + ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); + ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); + ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); + ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); + ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭"); + + // ========== 部门模块 1-002-004-000 ========== + ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); + ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在"); + ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在"); + ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除"); + ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门"); + ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); + ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); + + // ========== 岗位模块 1-002-005-000 ========== + ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在"); + ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择"); + ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位"); + ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位"); + + // ========== 字典类型 1-002-006-000 ========== + ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在"); + ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1_002_006_002, "字典类型不处于开启状态,不允许选择"); + ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1_002_006_003, "已经存在该名字的字典类型"); + ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1_002_006_004, "已经存在该类型的字典类型"); + ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1_002_006_005, "无法删除,该字典类型还有字典数据"); + + // ========== 字典数据 1-002-007-000 ========== + ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1_002_007_001, "当前字典数据不存在"); + ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1_002_007_002, "字典数据({})不处于开启状态,不允许选择"); + ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1_002_007_003, "已经存在该值的字典数据"); + + // ========== 通知公告 1-002-008-000 ========== + ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在"); + + // ========== 短信渠道 1-002-011-000 ========== + ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1_002_011_000, "短信渠道不存在"); + ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1_002_011_001, "短信渠道不处于开启状态,不允许选择"); + ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1_002_011_002, "无法删除,该短信渠道还有短信模板"); + + // ========== 短信模板 1-002-012-000 ========== + ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在"); + ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板"); + ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}"); + ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中"); + ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}"); + ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在"); + + // ========== 短信发送 1-002-013-000 ========== + ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在"); + ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_013_001, "模板参数({})缺失"); + ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_013_002, "短信模板不存在"); + + // ========== 短信验证码 1-002-014-000 ========== + ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在"); + ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期"); + ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用"); + ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量"); + ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁"); + + // ========== 租户信息 1-002-015-000 ========== + ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在"); + ErrorCode TENANT_DISABLE = new ErrorCode(1_002_015_001, "名字为【{}】的租户已被禁用"); + ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期"); + ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!"); + ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在"); + ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在"); + + // ========== 租户套餐 1-002-016-000 ========== + ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在"); + ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); + ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用"); + ErrorCode TENANT_PACKAGE_NAME_DUPLICATE = new ErrorCode(1_002_016_003, "已经存在该名字的租户套餐"); + + // ========== 社交用户 1-002-018-000 ========== + ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1_002_018_000, "社交授权失败,原因是:{}"); + ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_001, "社交授权失败,找不到对应的用户"); + + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_200, "获得手机号失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR = new ErrorCode(1_002_018_204, "上传微信小程序发货信息失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR = new ErrorCode(1_002_018_205, "上传微信小程序订单收货信息失败"); + ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在"); + ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置"); + + // ========== OAuth2 客户端 1-002-020-000 ========= + ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); + ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在"); + ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1_002_020_002, "OAuth2 客户端已禁用"); + ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1_002_020_003, "不支持该授权类型"); + ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1_002_020_004, "授权范围过大"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1_002_020_005, "无效 redirect_uri: {}"); + ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1_002_020_006, "无效 client_secret: {}"); + + // ========== OAuth2 授权 1-002-021-000 ========= + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配"); + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配"); + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配"); + + // ========== OAuth2 授权 1-002-022-000 ========= + ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在"); + ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期"); + + // ========== 邮箱账号 1-002-023-000 ========== + ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1_002_023_000, "邮箱账号不存在"); + ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1_002_023_001, "无法删除,该邮箱账号还有邮件模板"); + + // ========== 邮件模版 1-002-024-000 ========== + ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_024_000, "邮件模版不存在"); + ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1_002_024_001, "邮件模版 code({}) 已存在"); + + // ========== 邮件发送 1-002-025-000 ========== + ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_025_000, "模板参数({})缺失"); + ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1_002_025_001, "邮箱不存在"); + + // ========== 站内信模版 1-002-026-000 ========== + ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在"); + ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_026_001, "已经存在编码为【{}】的站内信模板"); + + // ========== 站内信模版 1-002-027-000 ========== + + // ========== 站内信发送 1-002-028-000 ========== + ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); + +} diff --git a/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/controller/admin/auth/vo/AuthWeixinMiniAppLoginReqVO.java b/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/controller/admin/auth/vo/AuthWeixinMiniAppLoginReqVO.java new file mode 100644 index 0000000..442d19c --- /dev/null +++ b/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/controller/admin/auth/vo/AuthWeixinMiniAppLoginReqVO.java @@ -0,0 +1,31 @@ +package com.viewsh.module.system.controller.admin.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 微信小程序手机登录 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthWeixinMiniAppLoginReqVO { + + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", + requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxx") + @NotEmpty(message = "手机 code 不能为空") + private String phoneCode; + + @Schema(description = "登录 code,小程序通过 wx.login 方法获得", + requiredMode = Schema.RequiredMode.REQUIRED, example = "yyyy") + @NotEmpty(message = "登录 code 不能为空") + private String loginCode; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, + example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + @NotEmpty(message = "state 不能为空") + private String state; +} diff --git a/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthService.java b/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthService.java index fa3d811..1e4ad39 100644 --- a/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthService.java +++ b/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthService.java @@ -1,87 +1,97 @@ -package com.viewsh.module.system.service.auth; - -import com.viewsh.module.system.controller.admin.auth.vo.*; -import com.viewsh.module.system.dal.dataobject.user.AdminUserDO; -import jakarta.validation.Valid; - -/** - * 管理后台的认证 Service 接口 - * - * 提供用户的登录、登出的能力 - * - * @author 芋道源码 - */ -public interface AdminAuthService { - - /** - * 验证账号 + 密码。如果通过,则返回用户 - * - * @param username 账号 - * @param password 密码 - * @return 用户 - */ - AdminUserDO authenticate(String username, String password); - - /** - * 账号登录 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO login(@Valid AuthLoginReqVO reqVO); - - /** - * 基于 token 退出登录 - * - * @param token token - * @param logType 登出类型 - */ - void logout(String token, Integer logType); - - /** - * 短信验证码发送 - * - * @param reqVO 发送请求 - */ - void sendSmsCode(AuthSmsSendReqVO reqVO); - - /** - * 短信登录 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO); - - /** - * 社交快捷登录,使用 code 授权码 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO); - - /** - * 刷新访问令牌 - * - * @param refreshToken 刷新令牌 - * @return 登录结果 - */ - AuthLoginRespVO refreshToken(String refreshToken); - - /** - * 用户注册 - * - * @param createReqVO 注册用户 - * @return 注册结果 - */ - AuthLoginRespVO register(AuthRegisterReqVO createReqVO); - - /** - * 重置密码 - * - * @param reqVO 验证码信息 - */ - void resetPassword(AuthResetPasswordReqVO reqVO); - -} +package com.viewsh.module.system.service.auth; + +import com.viewsh.module.system.controller.admin.auth.vo.*; +import com.viewsh.module.system.dal.dataobject.user.AdminUserDO; +import jakarta.validation.Valid; + +/** + * 管理后台的认证 Service 接口 + * + * 提供用户的登录、登出的能力 + * + * @author 芋道源码 + */ +public interface AdminAuthService { + + /** + * 验证账号 + 密码。如果通过,则返回用户 + * + * @param username 账号 + * @param password 密码 + * @return 用户 + */ + AdminUserDO authenticate(String username, String password); + + /** + * 账号登录 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AuthLoginRespVO login(@Valid AuthLoginReqVO reqVO); + + /** + * 基于 token 退出登录 + * + * @param token token + * @param logType 登出类型 + */ + void logout(String token, Integer logType); + + /** + * 短信验证码发送 + * + * @param reqVO 发送请求 + */ + void sendSmsCode(AuthSmsSendReqVO reqVO); + + /** + * 短信登录 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO); + + /** + * 社交快捷登录,使用 code 授权码 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO); + + /** + * 刷新访问令牌 + * + * @param refreshToken 刷新令牌 + * @return 登录结果 + */ + AuthLoginRespVO refreshToken(String refreshToken); + + /** + * 用户注册 + * + * @param createReqVO 注册用户 + * @return 注册结果 + */ + AuthLoginRespVO register(AuthRegisterReqVO createReqVO); + + /** + * 重置密码 + * + * @param reqVO 验证码信息 + */ + void resetPassword(AuthResetPasswordReqVO reqVO); + + /** + * 微信小程序一键登录(管理后台) + * + * 通过手机号匹配管理员账号,并绑定微信社交账号 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AuthLoginRespVO weixinMiniAppLogin(@Valid AuthWeixinMiniAppLoginReqVO reqVO); + +} diff --git a/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthServiceImpl.java b/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthServiceImpl.java index cf250f0..521ae30 100644 --- a/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthServiceImpl.java +++ b/viewsh-module-system/viewsh-module-system-server/src/main/java/com/viewsh/module/system/service/auth/AdminAuthServiceImpl.java @@ -1,306 +1,361 @@ -package com.viewsh.module.system.service.auth; - -import cn.hutool.core.util.ObjectUtil; -import com.viewsh.framework.common.enums.CommonStatusEnum; -import com.viewsh.framework.common.enums.UserTypeEnum; -import com.viewsh.framework.common.util.monitor.TracerUtils; -import com.viewsh.framework.common.util.object.BeanUtils; -import com.viewsh.framework.common.util.servlet.ServletUtils; -import com.viewsh.framework.common.util.validation.ValidationUtils; -import com.viewsh.framework.datapermission.core.annotation.DataPermission; -import com.viewsh.module.system.api.logger.dto.LoginLogCreateReqDTO; -import com.viewsh.module.system.api.sms.SmsCodeApi; -import com.viewsh.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import com.viewsh.module.system.api.social.dto.SocialUserBindReqDTO; -import com.viewsh.module.system.api.social.dto.SocialUserRespDTO; -import com.viewsh.module.system.controller.admin.auth.vo.*; -import com.viewsh.module.system.convert.auth.AuthConvert; -import com.viewsh.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import com.viewsh.module.system.dal.dataobject.user.AdminUserDO; -import com.viewsh.module.system.enums.logger.LoginLogTypeEnum; -import com.viewsh.module.system.enums.logger.LoginResultEnum; -import com.viewsh.module.system.enums.oauth2.OAuth2ClientConstants; -import com.viewsh.module.system.enums.sms.SmsSceneEnum; -import com.viewsh.module.system.service.logger.LoginLogService; -import com.viewsh.module.system.service.member.MemberService; -import com.viewsh.module.system.service.oauth2.OAuth2TokenService; -import com.viewsh.module.system.service.social.SocialUserService; -import com.viewsh.module.system.service.user.AdminUserService; -import com.anji.captcha.model.common.ResponseModel; -import com.anji.captcha.model.vo.CaptchaVO; -import com.anji.captcha.service.CaptchaService; -import com.google.common.annotations.VisibleForTesting; -import jakarta.annotation.Resource; -import jakarta.validation.Validator; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Objects; - -import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.viewsh.framework.common.util.servlet.ServletUtils.getClientIP; -import static com.viewsh.module.system.enums.ErrorCodeConstants.*; - -/** - * Auth Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class AdminAuthServiceImpl implements AdminAuthService { - - @Resource - private AdminUserService userService; - @Resource - private LoginLogService loginLogService; - @Resource - private OAuth2TokenService oauth2TokenService; - @Resource - private SocialUserService socialUserService; - @Resource - private MemberService memberService; - @Resource - private Validator validator; - @Resource - private CaptchaService captchaService; - @Resource - private SmsCodeApi smsCodeApi; - - /** - * 验证码的开关,默认为 true - */ - @Value("${viewsh.captcha.enable:true}") - @Setter // 为了单测:开启或者关闭验证码 - private Boolean captchaEnable; - - @Override - public AdminUserDO authenticate(String username, String password) { - final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; - // 校验账号是否存在 - AdminUserDO user = userService.getUserByUsername(username); - if (user == null) { - createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); - throw exception(AUTH_LOGIN_BAD_CREDENTIALS); - } - if (!userService.isPasswordMatch(password, user.getPassword())) { - createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); - throw exception(AUTH_LOGIN_BAD_CREDENTIALS); - } - // 校验是否禁用 - if (CommonStatusEnum.isDisable(user.getStatus())) { - createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); - throw exception(AUTH_LOGIN_USER_DISABLED); - } - return user; - } - - @Override - @DataPermission(enable = false) - public AuthLoginRespVO login(AuthLoginReqVO reqVO) { - // 校验验证码 - validateCaptcha(reqVO); - - // 使用账号密码,进行登录 - AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); - - // 如果 socialType 非空,说明需要绑定社交用户 - if (reqVO.getSocialType() != null) { - socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), - reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); - } - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); - } - - @Override - public void sendSmsCode(AuthSmsSendReqVO reqVO) { - // 如果是重置密码场景,需要校验图形验证码是否正确 - if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) { - ResponseModel response = doValidateCaptcha(reqVO); - if (!response.isSuccess()) { - throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); - } - } - - // 登录场景,验证是否存在 - if (userService.getUserByMobile(reqVO.getMobile()) == null) { - throw exception(AUTH_MOBILE_NOT_EXISTS); - } - // 发送验证码 - smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); - } - - @Override - public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) { - // 校验验证码 - smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())).checkError(); - - // 获得用户信息 - AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); - } - - private void createLoginLog(Long userId, String username, - LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { - // 插入登录日志 - LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); - reqDTO.setLogType(logTypeEnum.getType()); - reqDTO.setTraceId(TracerUtils.getTraceId()); - reqDTO.setUserId(userId); - reqDTO.setUserType(getUserType().getValue()); - reqDTO.setUsername(username); - reqDTO.setUserAgent(ServletUtils.getUserAgent()); - reqDTO.setUserIp(ServletUtils.getClientIP()); - reqDTO.setResult(loginResult.getResult()); - loginLogService.createLoginLog(reqDTO); - // 更新最后登录时间 - if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { - userService.updateUserLogin(userId, ServletUtils.getClientIP()); - } - } - - @Override - public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { - // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 - SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), - reqVO.getCode(), reqVO.getState()); - if (socialUser == null || socialUser.getUserId() == null) { - throw exception(AUTH_THIRD_LOGIN_NOT_BIND); - } - - // 获得用户 - AdminUserDO user = userService.getUser(socialUser.getUserId()); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); - } - - @VisibleForTesting - void validateCaptcha(AuthLoginReqVO reqVO) { - ResponseModel response = doValidateCaptcha(reqVO); - // 校验验证码 - if (!response.isSuccess()) { - // 创建登录失败日志(验证码不正确) - createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR); - throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg()); - } - } - - private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) { - // 如果验证码关闭,则不进行校验 - if (!captchaEnable) { - return ResponseModel.success(); - } - ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class); - CaptchaVO captchaVO = new CaptchaVO(); - captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification()); - return captchaService.verification(captchaVO); - } - - private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { - // 插入登陆日志 - createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); - // 创建访问令牌 - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(), - OAuth2ClientConstants.CLIENT_ID_DEFAULT, null); - // 构建返回结果 - return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class); - } - - @Override - public AuthLoginRespVO refreshToken(String refreshToken) { - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT); - return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class); - } - - @Override - public void logout(String token, Integer logType) { - // 删除访问令牌 - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); - if (accessTokenDO == null) { - return; - } - // 删除成功,则记录登出日志 - createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType); - } - - private void createLogoutLog(Long userId, Integer userType, Integer logType) { - LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); - reqDTO.setLogType(logType); - reqDTO.setTraceId(TracerUtils.getTraceId()); - reqDTO.setUserId(userId); - reqDTO.setUserType(userType); - if (ObjectUtil.equal(getUserType().getValue(), userType)) { - reqDTO.setUsername(getUsername(userId)); - } else { - reqDTO.setUsername(memberService.getMemberUserMobile(userId)); - } - reqDTO.setUserAgent(ServletUtils.getUserAgent()); - reqDTO.setUserIp(ServletUtils.getClientIP()); - reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); - loginLogService.createLoginLog(reqDTO); - } - - private String getUsername(Long userId) { - if (userId == null) { - return null; - } - AdminUserDO user = userService.getUser(userId); - return user != null ? user.getUsername() : null; - } - - private UserTypeEnum getUserType() { - return UserTypeEnum.ADMIN; - } - - @Override - public AuthLoginRespVO register(AuthRegisterReqVO registerReqVO) { - // 1. 校验验证码 - validateCaptcha(registerReqVO); - - // 2. 校验用户名是否已存在 - Long userId = userService.registerUser(registerReqVO); - - // 3. 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(userId, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); - } - - @VisibleForTesting - void validateCaptcha(AuthRegisterReqVO reqVO) { - ResponseModel response = doValidateCaptcha(reqVO); - // 验证不通过 - if (!response.isSuccess()) { - throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void resetPassword(AuthResetPasswordReqVO reqVO) { - AdminUserDO userByMobile = userService.getUserByMobile(reqVO.getMobile()); - if (userByMobile == null) { - throw exception(USER_MOBILE_NOT_EXISTS); - } - - smsCodeApi.useSmsCode(new SmsCodeUseReqDTO() - .setCode(reqVO.getCode()) - .setMobile(reqVO.getMobile()) - .setScene(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene()) - .setUsedIp(getClientIP()) - ).checkError(); - - userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword()); - } -} +package com.viewsh.module.system.service.auth; + +import cn.hutool.core.util.ObjectUtil; +import com.viewsh.framework.common.enums.CommonStatusEnum; +import com.viewsh.framework.common.enums.UserTypeEnum; +import com.viewsh.framework.common.util.monitor.TracerUtils; +import com.viewsh.framework.common.util.object.BeanUtils; +import com.viewsh.framework.common.util.servlet.ServletUtils; +import com.viewsh.framework.common.util.validation.ValidationUtils; +import com.viewsh.framework.datapermission.core.annotation.DataPermission; +import com.viewsh.module.system.api.logger.dto.LoginLogCreateReqDTO; +import com.viewsh.module.system.api.sms.SmsCodeApi; +import com.viewsh.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import com.viewsh.module.system.api.social.dto.SocialUserBindReqDTO; +import com.viewsh.module.system.api.social.dto.SocialUserRespDTO; +import com.viewsh.module.system.controller.admin.auth.vo.*; +import com.viewsh.module.system.convert.auth.AuthConvert; +import com.viewsh.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import com.viewsh.module.system.dal.dataobject.user.AdminUserDO; +import com.viewsh.module.system.enums.logger.LoginLogTypeEnum; +import com.viewsh.module.system.enums.logger.LoginResultEnum; +import com.viewsh.module.system.enums.oauth2.OAuth2ClientConstants; +import com.viewsh.module.system.enums.sms.SmsSceneEnum; +import com.viewsh.module.system.service.logger.LoginLogService; +import com.viewsh.module.system.service.member.MemberService; +import com.viewsh.module.system.service.social.SocialClientService; +import com.viewsh.module.system.service.oauth2.OAuth2TokenService; +import com.viewsh.module.system.service.social.SocialUserService; +import com.viewsh.module.system.service.user.AdminUserService; +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.model.vo.CaptchaVO; +import com.anji.captcha.service.CaptchaService; +import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Resource; +import jakarta.validation.Validator; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.hutool.core.lang.Assert; +import com.viewsh.module.system.enums.social.SocialTypeEnum; + +import java.util.Objects; + +import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.viewsh.framework.common.util.servlet.ServletUtils.getClientIP; +import static com.viewsh.module.system.enums.ErrorCodeConstants.*; + +/** + * Auth Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class AdminAuthServiceImpl implements AdminAuthService { + + @Resource + private AdminUserService userService; + @Resource + private LoginLogService loginLogService; + @Resource + private OAuth2TokenService oauth2TokenService; + @Resource + private SocialUserService socialUserService; + @Resource + private MemberService memberService; + @Resource + private SocialClientService socialClientService; + @Resource + private Validator validator; + @Resource + private CaptchaService captchaService; + @Resource + private SmsCodeApi smsCodeApi; + + /** + * 验证码的开关,默认为 true + */ + @Value("${viewsh.captcha.enable:true}") + @Setter // 为了单测:开启或者关闭验证码 + private Boolean captchaEnable; + + @Override + public AdminUserDO authenticate(String username, String password) { + final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; + // 校验账号是否存在 + AdminUserDO user = userService.getUserByUsername(username); + if (user == null) { + createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + if (!userService.isPasswordMatch(password, user.getPassword())) { + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + // 校验是否禁用 + if (CommonStatusEnum.isDisable(user.getStatus())) { + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); + throw exception(AUTH_LOGIN_USER_DISABLED); + } + return user; + } + + @Override + @DataPermission(enable = false) + public AuthLoginRespVO login(AuthLoginReqVO reqVO) { + // 校验验证码 + validateCaptcha(reqVO); + + // 使用账号密码,进行登录 + AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); + + // 如果 socialType 非空,说明需要绑定社交用户 + if (reqVO.getSocialType() != null) { + socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); + } + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); + } + + @Override + public void sendSmsCode(AuthSmsSendReqVO reqVO) { + // 如果是重置密码场景,需要校验图形验证码是否正确 + if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) { + ResponseModel response = doValidateCaptcha(reqVO); + if (!response.isSuccess()) { + throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); + } + } + + // 登录场景,验证是否存在 + if (userService.getUserByMobile(reqVO.getMobile()) == null) { + throw exception(AUTH_MOBILE_NOT_EXISTS); + } + // 发送验证码 + smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); + } + + @Override + public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) { + // 校验验证码 + smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())).checkError(); + + // 获得用户信息 + AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); + } + + private void createLoginLog(Long userId, String username, + LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { + // 插入登录日志 + LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); + reqDTO.setLogType(logTypeEnum.getType()); + reqDTO.setTraceId(TracerUtils.getTraceId()); + reqDTO.setUserId(userId); + reqDTO.setUserType(getUserType().getValue()); + reqDTO.setUsername(username); + reqDTO.setUserAgent(ServletUtils.getUserAgent()); + reqDTO.setUserIp(ServletUtils.getClientIP()); + reqDTO.setResult(loginResult.getResult()); + loginLogService.createLoginLog(reqDTO); + // 更新最后登录时间 + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { + userService.updateUserLogin(userId, ServletUtils.getClientIP()); + } + } + + @Override + public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { + // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 + SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), + reqVO.getCode(), reqVO.getState()); + if (socialUser == null || socialUser.getUserId() == null) { + throw exception(AUTH_THIRD_LOGIN_NOT_BIND); + } + + // 获得用户 + AdminUserDO user = userService.getUser(socialUser.getUserId()); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public AuthLoginRespVO weixinMiniAppLogin(AuthWeixinMiniAppLoginReqVO reqVO) { + // 1. 通过 phoneCode 获取手机号 + WxMaPhoneNumberInfo phoneNumberInfo = socialClientService.getWxMaPhoneNumberInfo( + UserTypeEnum.ADMIN.getValue(), reqVO.getPhoneCode()); + Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空"); + String mobile = phoneNumberInfo.getPurePhoneNumber(); + + // 2. 通过手机号查找管理员 + AdminUserDO user = userService.getUserByMobile(mobile); + if (user == null) { + throw exception(AUTH_WEIXIN_MINI_APP_PHONE_NOT_FOUND); + } + if (CommonStatusEnum.isDisable(user.getStatus())) { + throw exception(AUTH_LOGIN_USER_DISABLED); + } + + // 3. 通过 loginCode 获取社交用户(含 openid) + SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode( + UserTypeEnum.ADMIN.getValue(), SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(), + reqVO.getLoginCode(), reqVO.getState()); + Assert.notNull(socialUser, "社交用户不能为空"); + + // 4. 绑定冲突检测 + // 4a. 当前 openid 是否已绑定其他管理员 + if (socialUser.getUserId() != null && !socialUser.getUserId().equals(user.getId())) { + throw exception(AUTH_WEIXIN_MINI_APP_WECHAT_BINDTO_OTHER); + } + // 4b. 目标管理员是否已绑定其他微信 + SocialUserRespDTO existByUser = socialUserService.getSocialUserByUserId( + UserTypeEnum.ADMIN.getValue(), user.getId(), + SocialTypeEnum.WECHAT_MINI_PROGRAM.getType()); + if (existByUser != null && !existByUser.getOpenid().equals(socialUser.getOpenid())) { + throw exception(AUTH_WEIXIN_MINI_APP_BINDTO_OTHER_WECHAT); + } + + // 5. 绑定社交用户(无冲突且未绑定时) + if (existByUser == null) { + socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), + getUserType().getValue(), SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(), + reqVO.getLoginCode(), reqVO.getState())); + } + + // 6. 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); + } + + @VisibleForTesting + void validateCaptcha(AuthLoginReqVO reqVO) { + ResponseModel response = doValidateCaptcha(reqVO); + // 校验验证码 + if (!response.isSuccess()) { + // 创建登录失败日志(验证码不正确) + createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR); + throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg()); + } + } + + private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) { + // 如果验证码关闭,则不进行校验 + if (!captchaEnable) { + return ResponseModel.success(); + } + ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class); + CaptchaVO captchaVO = new CaptchaVO(); + captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification()); + return captchaService.verification(captchaVO); + } + + private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { + // 插入登陆日志 + createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); + // 创建访问令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(), + OAuth2ClientConstants.CLIENT_ID_DEFAULT, null); + // 构建返回结果 + return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class); + } + + @Override + public AuthLoginRespVO refreshToken(String refreshToken) { + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT); + return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class); + } + + @Override + public void logout(String token, Integer logType) { + // 删除访问令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); + if (accessTokenDO == null) { + return; + } + // 删除成功,则记录登出日志 + createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType); + } + + private void createLogoutLog(Long userId, Integer userType, Integer logType) { + LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); + reqDTO.setLogType(logType); + reqDTO.setTraceId(TracerUtils.getTraceId()); + reqDTO.setUserId(userId); + reqDTO.setUserType(userType); + if (ObjectUtil.equal(getUserType().getValue(), userType)) { + reqDTO.setUsername(getUsername(userId)); + } else { + reqDTO.setUsername(memberService.getMemberUserMobile(userId)); + } + reqDTO.setUserAgent(ServletUtils.getUserAgent()); + reqDTO.setUserIp(ServletUtils.getClientIP()); + reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); + loginLogService.createLoginLog(reqDTO); + } + + private String getUsername(Long userId) { + if (userId == null) { + return null; + } + AdminUserDO user = userService.getUser(userId); + return user != null ? user.getUsername() : null; + } + + private UserTypeEnum getUserType() { + return UserTypeEnum.ADMIN; + } + + @Override + public AuthLoginRespVO register(AuthRegisterReqVO registerReqVO) { + // 1. 校验验证码 + validateCaptcha(registerReqVO); + + // 2. 校验用户名是否已存在 + Long userId = userService.registerUser(registerReqVO); + + // 3. 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(userId, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); + } + + @VisibleForTesting + void validateCaptcha(AuthRegisterReqVO reqVO) { + ResponseModel response = doValidateCaptcha(reqVO); + // 验证不通过 + if (!response.isSuccess()) { + throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resetPassword(AuthResetPasswordReqVO reqVO) { + AdminUserDO userByMobile = userService.getUserByMobile(reqVO.getMobile()); + if (userByMobile == null) { + throw exception(USER_MOBILE_NOT_EXISTS); + } + + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO() + .setCode(reqVO.getCode()) + .setMobile(reqVO.getMobile()) + .setScene(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene()) + .setUsedIp(getClientIP()) + ).checkError(); + + userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword()); + } +}