From c8392c9b47f994fb090c721700dbe00ec39f42a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Wed, 14 May 2025 12:22:13 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=91=E9=80=81?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E5=8F=82=E6=95=B0=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=88=B0=E6=95=B0=E6=8D=AE=E5=BA=93=E9=87=8C=20--202505141129.?= =?UTF-8?q?sql=20--db.changelog-master.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/db/changelog/202505141129.sql | 11 +++++++++++ .../resources/db/changelog/db.changelog-master.yaml | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 main/manager-api/src/main/resources/db/changelog/202505141129.sql diff --git a/main/manager-api/src/main/resources/db/changelog/202505141129.sql b/main/manager-api/src/main/resources/db/changelog/202505141129.sql new file mode 100644 index 00000000..e18bb523 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202505141129.sql @@ -0,0 +1,11 @@ +-- 添加手机短信注册功能的需要的参数 +INSERT INTO + xiaozhi_esp32_server.sys_params +(id, param_code, param_value, value_type, param_type, remark, creator, create_date, updater, update_date) + VALUES +(501, 'system.enable_mobile_register', 'false', 'boolean', 1, '是否开启手机注册', NULL, NULL, NULL, NULL), +(502, 'system.sms.max_send_count', '5', 'number', 1, '单号码最大短信发送条数', NULL, NULL, NULL, NULL), +(510, 'aliyun.sms.access_key_id', '', 'string', 1, '阿里云平台access_key', NULL, NULL, NULL, NULL), +(511, 'aliyun.sms.access_key_secret', '', 'string', 1, '阿里云平台access_key_secret', NULL, NULL, NULL, NULL), +(512, 'aliyun.sms.sign_name', '', 'string', 1, '阿里云短信签名', NULL, NULL, NULL, NULL), +(513, 'aliyun.sms.sms_code_template_code', '', 'string', 1, '阿里云短信模板', NULL, NULL, NULL, NULL); \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 53f4609d..299efc9c 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -113,4 +113,11 @@ databaseChangeLog: changes: - sqlFile: encoding: utf8 - path: classpath:db/changelog/202505091409.sql \ No newline at end of file + path: classpath:db/changelog/202505091409.sql + - changeSet: + id: 202505141129 + author: zjy + changes: + - sqlFile: + encoding: utf8 + path: classpath:db/changelog/202505141129.sql \ No newline at end of file From e4194cdf4d29b9b5415c5c456463523fa0662c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Wed, 14 May 2025 14:18:01 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E9=98=BF=E9=87=8C=E4=BA=91=E5=B9=B3=E5=8F=B0=E5=8F=91=E9=80=81?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E7=9F=AD=E4=BF=A1=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20--pom.xml=20=E6=B7=BB=E5=8A=A0=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E5=8F=91=E9=80=81sdk=20--Constant.java=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=9F=AD=E4=BF=A1=E7=9A=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE=20--ALiYunSmsService.java=20=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91=E5=AE=9E=E7=8E=B0=E7=9F=AD=E4=BF=A1=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20--SmsService.java=20=E7=9F=AD=E4=BF=A1=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/manager-api/pom.xml | 15 ++++ .../xiaozhi/common/constant/Constant.java | 40 ++++++++++ .../modules/sms/service/SmsService.java | 17 +++++ .../sms/service/imp/ALiYunSmsService.java | 76 +++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java diff --git a/main/manager-api/pom.xml b/main/manager-api/pom.xml index d08e32f2..c4234647 100644 --- a/main/manager-api/pom.xml +++ b/main/manager-api/pom.xml @@ -28,6 +28,8 @@ 1.6.2 33.0.0-jre 4.20.0 + 4.1.0 + 3.4.0 @@ -37,6 +39,7 @@ jakarta ${shiro.version} + org.apache.shiro shiro-spring @@ -201,6 +204,18 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 + + + com.aliyun + dysmsapi20170525 + ${the-latest-version} + + + + com.squareup.okio + okio + ${okio-version} + diff --git a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java index 78154b5c..171d1a21 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java +++ b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java @@ -146,6 +146,46 @@ public interface Constant { } } + /** + * 系统短信 + */ + enum SysMSMParam { + /** + * 阿里云授权keyID + */ + ALIYUN_SMS_ACCESS_KEY_ID("aliyun.sms.access_key_id"), + /** + * 阿里云授权密钥 + */ + ALIYUN_SMS_ACCESS_KEY_SECRET("aliyun.sms.access_key_secret"), + /** + * + */ + ALIYUN_SMS_SIGN_NAME("aliyun.sms.sign_name"), + /** + * 已删除 + */ + ALIYUN_SMS_SMS_CODE_TEMPLATE_CODE("aliyun.sms.sms_code_template_code"), + /** + * 已删除 + */ + SYSTEM_SMS_MAX_SEND_COUNT("system.sms.max_send_count"), + /** + * 是否开启手机注册 + */ + SYSTEM_ENABLE_MOBILE_REGISTER("system.enable_mobile_register"); + + private String value; + + SysMSMParam(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + /** * 数据状态 */ diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java b/main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java new file mode 100644 index 00000000..c33ad945 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java @@ -0,0 +1,17 @@ +package xiaozhi.modules.sms.service; + +/** + * 短信服务的方法定义接口 + * + * @author zjy + * @since 2025-05-12 + */ +public interface SmsService { + + /** + * 发送验证码短信 + * @param phone 手机号码 + * @param VerificationCode 验证码 + */ + void sendVerificationCodeSms(String phone, String VerificationCode) ; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java b/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java new file mode 100644 index 00000000..0a73bf68 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java @@ -0,0 +1,76 @@ +package xiaozhi.modules.sms.service.imp; + +import com.aliyun.dysmsapi20170525.Client; +import com.aliyun.dysmsapi20170525.models.SendSmsRequest; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.tea.TeaException; +import com.aliyun.teaopenapi.models.Config; +import com.aliyun.teautil.Common; +import com.aliyun.teautil.models.RuntimeOptions; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import xiaozhi.common.constant.Constant; +import xiaozhi.modules.sms.service.SmsService; +import xiaozhi.modules.sys.service.SysParamsService; + +@Service +@AllArgsConstructor +@Slf4j +public class ALiYunSmsService implements SmsService { + private final SysParamsService sysParamsService; + + @Override + public void sendVerificationCodeSms(String phone, String VerificationCode) { + String SignName = sysParamsService.getValue(Constant.SysMSMParam + .ALIYUN_SMS_SIGN_NAME.getValue(),true); + String TemplateCode = sysParamsService.getValue(Constant.SysMSMParam + .ALIYUN_SMS_SMS_CODE_TEMPLATE_CODE.getValue(),true); + try { + Client client = createClient(); + SendSmsRequest sendSmsRequest = new SendSmsRequest() + .setSignName(SignName) + .setTemplateCode(TemplateCode) + .setPhoneNumbers(phone) + .setTemplateParam(String.format("{\"code\":\"%s\"}", VerificationCode)); + RuntimeOptions runtime = new RuntimeOptions(); + try { + // 复制代码运行请自行打印 API 的返回值 + SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); + System.out.println(sendSmsResponse.getBody().getRequestId()); + System.out.println(sendSmsResponse.getBody().getRequestId()); + } catch (TeaException error) { + // 错误 message + log.error(error.getMessage()); + // 诊断地址 + log.error(error.getData().get("Recommend").toString()); + log.error(Common.assertAsString(error.message)); + } + } catch (Exception e) { + // 错误 message + log.error(e.getMessage()); + throw new RuntimeException(e); + } + + } + + + /** + * 创建阿里云连接 + * @return 返回连接对象 + * @throws Exception + */ + private Client createClient() throws Exception { + String ACCESS_KEY_ID = sysParamsService.getValue(Constant.SysMSMParam + .ALIYUN_SMS_ACCESS_KEY_ID.getValue(),true); + String ACCESS_KEY_SECRET = sysParamsService.getValue(Constant.SysMSMParam + .ALIYUN_SMS_ACCESS_KEY_SECRET.getValue(),true); + Config config = new Config() + .setAccessKeyId(ACCESS_KEY_ID) + .setAccessKeySecret(ACCESS_KEY_SECRET); + // 配置 Endpoint。中国站请使用dysmsapi.aliyuncs.com + config.endpoint = "dysmsapi.aliyuncs.com"; + + return new Client(config); + } +} From 0509ddb1292d515a6428c5f15fa87dffdd2543a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Thu, 15 May 2025 21:45:33 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=91=E9=80=81=E7=9F=AD=E4=BF=A1=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E5=92=8C=E9=AA=8C=E8=AF=81=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=20--CaptchaService.java=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=8F=91=E9=80=81=E7=9F=AD=E4=BF=A1=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E5=92=8C=E9=AA=8C=E8=AF=81=E7=9F=AD=E4=BF=A1=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E6=96=B9=E6=B3=95=E5=AE=9A=E4=B9=89=20--Capt?= =?UTF-8?q?chaServiceImpl.java=20=E5=AE=9E=E7=8E=B0=E6=96=B0=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=AE=9A=E4=B9=89=E7=9A=84=E6=96=B9=E6=B3=95=20--Redi?= =?UTF-8?q?sKeys.java=20=E6=B7=BB=E5=8A=A0=E7=9F=AD=E4=BF=A1=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/xiaozhi/common/redis/RedisKeys.java | 8 +++++ .../security/service/CaptchaService.java | 13 +++++++- .../service/impl/CaptchaServiceImpl.java | 33 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java index 62487456..cc56e0ee 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java +++ b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java @@ -117,4 +117,12 @@ public class RedisKeys { public static String getAgentAudioIdKey(String uuid) { return "agent:audio:id:" + uuid; } + + /** + * 获取短信验证码的缓存key + */ + public static String getSMSValidateCodeKey(String phone) { + return "sms:Validate:Code:" + phone; + } + } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java b/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java index 9e5c153f..43ae6432 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java @@ -18,10 +18,21 @@ public interface CaptchaService { /** * 验证码效验 - * * @param uuid uuid * @param code 验证码 * @return true:成功 false:失败 */ boolean validate(String uuid, String code); + + /** + * 发送短信验证码 + * @param phone 手机 + */ + void sendSMSValidateCode(String phone); + + /** + * 验证短信验证码 + * @param phone 手机 + */ + boolean validateSMSValidateCode(String phone,String code); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java index 012b4d3e..f151d178 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java @@ -1,6 +1,7 @@ package xiaozhi.modules.security.service.impl; import java.io.IOException; +import java.util.Random; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; @@ -17,6 +18,7 @@ import jakarta.servlet.http.HttpServletResponse; import xiaozhi.common.redis.RedisKeys; import xiaozhi.common.redis.RedisUtils; import xiaozhi.modules.security.service.CaptchaService; +import xiaozhi.modules.sms.service.SmsService; /** * 验证码 @@ -25,6 +27,8 @@ import xiaozhi.modules.security.service.CaptchaService; public class CaptchaServiceImpl implements CaptchaService { @Resource private RedisUtils redisUtils; + @Resource + private SmsService smsService; @Value("${renren.redis.open}") private boolean open; /** @@ -66,6 +70,35 @@ public class CaptchaServiceImpl implements CaptchaService { return false; } + @Override + public void sendSMSValidateCode(String phone) { + String key = RedisKeys.getSMSValidateCodeKey(phone); + String validateCodes = generateValidateCode(6); + setCache(key,validateCodes); + //发送验证码短信 + smsService.sendVerificationCodeSms(phone,validateCodes); + } + + @Override + public boolean validateSMSValidateCode(String phone,String code) { + String key = RedisKeys.getSMSValidateCodeKey(phone); + return validate(key, code); + } + /** + * 生成指定数量的随机数验证码 + * @param length 数量 + * @return 随机码 + */ + private String generateValidateCode(Integer length) { + String chars = "0123456789"; // 字符范围可以自定义:数字 + Random random = new Random(); + StringBuilder code = new StringBuilder(); + for (int i = 0; i < length; i++) { + code.append(chars.charAt(random.nextInt(chars.length()))); + } + return code.toString(); + } + private void setCache(String key, String value) { if (open) { key = RedisKeys.getCaptchaKey(key); From aa251aa011b9608093dc64cef7a4597dc39b4d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Thu, 15 May 2025 22:47:57 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E5=8D=95=E5=8F=82=E6=95=B0=E6=98=AF=E5=90=A6=E6=98=AF=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E7=9A=84=E6=89=8B=E6=9C=BA=E5=8F=B7=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20--ValidatorUtils.java=20=E6=B7=BB=E5=8A=A0=E6=96=B0?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/validator/ValidatorUtils.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java b/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java index 9e31c964..b2e5b296 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java +++ b/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java @@ -2,6 +2,8 @@ package xiaozhi.common.validator; import java.util.Locale; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.springframework.context.i18n.LocaleContextHolder; @@ -45,4 +47,23 @@ public class ValidatorUtils { throw new RenException(constraint.getMessage()); } } + + /** + * 手机号正则表达式 + */ + private static final String PHONE_REGEX = "^1[3-9]\\d{9}$"; + + /** + * 校验单参数是否是正确的手机号 + * @param phone 手机号 + * @return boolean + */ + public static boolean isValidPhone(String phone) { + if (phone == null || phone.isEmpty()) { + return false; + } + Pattern pattern = Pattern.compile(PHONE_REGEX); + Matcher matcher = pattern.matcher(phone); + return matcher.matches(); + } } \ No newline at end of file From 2741e11078ccedd9ac925c7efd3ab053001f632c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Fri, 16 May 2025 00:16:20 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8F=91=E9=80=81?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E9=AA=8C=E8=AF=81=E7=A0=81=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E6=89=BE=E5=9B=9E=E5=AF=86=E7=A0=81=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9=E6=B3=A8=E5=86=8C=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E5=A6=82=E6=9E=9C=E5=BC=80=E5=90=AF=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=A8=A1=E5=BC=8F=EF=BC=8C=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E9=AA=8C=E8=AF=81=E6=A8=A1=E5=BC=8F=20--Logi?= =?UTF-8?q?nController.java=20=E6=B7=BB=E5=8A=A0=E5=92=8C=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=96=B9=E6=B3=95=20--RetrievePasswordDTO.java=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=89=BE=E5=9B=9E=E5=AF=86=E7=A0=81dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/controller/LoginController.java | 69 +++++++++++++++++-- .../modules/sys/dto/RetrievePasswordDTO.java | 30 ++++++++ 2 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java index 7cbaa8af..7731caa0 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java @@ -29,7 +29,9 @@ import xiaozhi.modules.security.service.CaptchaService; import xiaozhi.modules.security.service.SysUserTokenService; import xiaozhi.modules.security.user.SecurityUser; import xiaozhi.modules.sys.dto.PasswordDTO; +import xiaozhi.modules.sys.dto.RetrievePasswordDTO; import xiaozhi.modules.sys.dto.SysUserDTO; +import xiaozhi.modules.sys.service.SysParamsService; import xiaozhi.modules.sys.service.SysUserService; /** @@ -43,17 +45,26 @@ public class LoginController { private final SysUserService sysUserService; private final SysUserTokenService sysUserTokenService; private final CaptchaService captchaService; + private final SysParamsService sysParamsService; @GetMapping("/captcha") @Operation(summary = "验证码") public void captcha(HttpServletResponse response, String uuid) throws IOException { // uuid不能为空 AssertUtils.isBlank(uuid, ErrorCode.IDENTIFIER_NOT_NULL); - // 生成验证码 captchaService.create(response, uuid); } + @GetMapping("/smsVerification") + @Operation(summary = "短信验证码") + public void smsVerification(String phone) throws IOException { + // uuid不能为空 + AssertUtils.isBlank(phone, ErrorCode.IDENTIFIER_NOT_NULL); + // 发送短信验证码 + captchaService.sendSMSValidateCode(phone); + } + @PostMapping("/login") @Operation(summary = "登录") public Result login(@RequestBody LoginDTO login) { @@ -81,8 +92,23 @@ public class LoginController { if (!sysUserService.getAllowUserRegister()) { throw new RenException("当前不允许普通用户注册"); } - // 验证是否正确输入验证码 - boolean validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha()); + // 是否开启手机注册 + Boolean isMobileRegister = sysParamsService.getValueObject(Constant.SysMSMParam + .SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); + boolean validate; + if (isMobileRegister) { + // 验证用户是否是手机号码 + boolean validPhone = ValidatorUtils.isValidPhone(login.getUsername()); + if (validPhone) { + throw new RenException("用户名不是手机号码,请重新输入"); + } + // 验证短信验证码是否正常 + validate = captchaService.validateSMSValidateCode(login.getUsername(),login.getCaptcha()); + } else { + // 验证是否正确输入验证码 + validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha()); + } + // 判断是否通过验证 if (!validate) { throw new RenException("验证码错误,请重新获取"); } @@ -96,7 +122,6 @@ public class LoginController { userDTO.setPassword(login.getPassword()); sysUserService.save(userDTO); return new Result<>(); - } @GetMapping("/info") @@ -118,6 +143,42 @@ public class LoginController { return new Result<>(); } + @PutMapping("/retrieve-password") + @Operation(summary = "找回密码") + public Result retrievePassword(@RequestBody RetrievePasswordDTO dto) { + // 是否开启手机注册 + Boolean isMobileRegister = sysParamsService.getValueObject(Constant.SysMSMParam + .SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); + if (!isMobileRegister) { + throw new RenException("没有开启手机注册,没法使用找回密码功能"); + } + // 判断非空 + ValidatorUtils.validateEntity(dto); + // 验证用户是否是手机号码 + boolean validPhone = ValidatorUtils.isValidPhone(dto.getPhone()); + if (validPhone) { + throw new RenException("用户名不是手机号码,请重新输入"); + }; + + // 按照用户名获取用户 + SysUserDTO userDTO = sysUserService.getByUsername(dto.getPhone()); + if (userDTO == null) { + throw new RenException("用户名或验证码错误"); + } + // 验证用户是否是手机号码 + ValidatorUtils.isValidPhone(dto.getPhone()); + // 验证短信验证码是否正常 + boolean validate = captchaService.validateSMSValidateCode(dto.getPhone(),dto.getCode()); + // 判断是否通过验证 + if (!validate) { + throw new RenException("用户名或验证码错误"); + } + + Long userId = SecurityUser.getUserId(); + sysUserService.changePasswordDirectly(userId, dto.getPassword()); + return new Result<>(); + } + @GetMapping("/pub-config") @Operation(summary = "公共配置") public Result> pubConfig() { diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java new file mode 100644 index 00000000..efac8099 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java @@ -0,0 +1,30 @@ +package xiaozhi.modules.sys.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serializable; + +/** + * 找回密码DTO + */ +@Data +@Schema(description = "找回密码") +public class RetrievePasswordDTO implements Serializable { + + @Schema(description = "手机号码") + @NotBlank(message = "{sysuser.password.require}") + private String phone; + + @Schema(description = "验证码") + @NotBlank(message = "{sysuser.password.require}") + private String code; + + @Schema(description = "新密码") + @NotBlank(message = "{sysuser.password.require}") + private String password; + + + +} \ No newline at end of file From e66ab647e14937afebb8242984ce25484b34d102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Sat, 17 May 2025 10:57:02 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=9A=84=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= =?UTF-8?q?=20--ALiYunSmsService.java=20=E4=BC=98=E5=8C=96=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sms/service/imp/ALiYunSmsService.java | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java b/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java index 0a73bf68..b39b24c8 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java @@ -3,14 +3,13 @@ package xiaozhi.modules.sms.service.imp; import com.aliyun.dysmsapi20170525.Client; import com.aliyun.dysmsapi20170525.models.SendSmsRequest; import com.aliyun.dysmsapi20170525.models.SendSmsResponse; -import com.aliyun.tea.TeaException; import com.aliyun.teaopenapi.models.Config; -import com.aliyun.teautil.Common; import com.aliyun.teautil.models.RuntimeOptions; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import xiaozhi.common.constant.Constant; +import xiaozhi.common.exception.RenException; import xiaozhi.modules.sms.service.SmsService; import xiaozhi.modules.sys.service.SysParamsService; @@ -22,34 +21,25 @@ public class ALiYunSmsService implements SmsService { @Override public void sendVerificationCodeSms(String phone, String VerificationCode) { + Client client = createClient(); String SignName = sysParamsService.getValue(Constant.SysMSMParam .ALIYUN_SMS_SIGN_NAME.getValue(),true); String TemplateCode = sysParamsService.getValue(Constant.SysMSMParam .ALIYUN_SMS_SMS_CODE_TEMPLATE_CODE.getValue(),true); try { - Client client = createClient(); SendSmsRequest sendSmsRequest = new SendSmsRequest() .setSignName(SignName) .setTemplateCode(TemplateCode) .setPhoneNumbers(phone) .setTemplateParam(String.format("{\"code\":\"%s\"}", VerificationCode)); RuntimeOptions runtime = new RuntimeOptions(); - try { - // 复制代码运行请自行打印 API 的返回值 - SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); - System.out.println(sendSmsResponse.getBody().getRequestId()); - System.out.println(sendSmsResponse.getBody().getRequestId()); - } catch (TeaException error) { - // 错误 message - log.error(error.getMessage()); - // 诊断地址 - log.error(error.getData().get("Recommend").toString()); - log.error(Common.assertAsString(error.message)); - } + // 复制代码运行请自行打印 API 的返回值 + SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); + log.info("发送短信响应的requestID: {}", sendSmsResponse.getBody().getRequestId()); } catch (Exception e) { // 错误 message log.error(e.getMessage()); - throw new RuntimeException(e); + throw new RenException("短信发送失败"); } } @@ -58,19 +48,23 @@ public class ALiYunSmsService implements SmsService { /** * 创建阿里云连接 * @return 返回连接对象 - * @throws Exception */ - private Client createClient() throws Exception { + private Client createClient(){ String ACCESS_KEY_ID = sysParamsService.getValue(Constant.SysMSMParam .ALIYUN_SMS_ACCESS_KEY_ID.getValue(),true); String ACCESS_KEY_SECRET = sysParamsService.getValue(Constant.SysMSMParam .ALIYUN_SMS_ACCESS_KEY_SECRET.getValue(),true); - Config config = new Config() - .setAccessKeyId(ACCESS_KEY_ID) - .setAccessKeySecret(ACCESS_KEY_SECRET); - // 配置 Endpoint。中国站请使用dysmsapi.aliyuncs.com - config.endpoint = "dysmsapi.aliyuncs.com"; - - return new Client(config); + try { + Config config = new Config() + .setAccessKeyId(ACCESS_KEY_ID) + .setAccessKeySecret(ACCESS_KEY_SECRET); + // 配置 Endpoint。中国站请使用dysmsapi.aliyuncs.com + config.endpoint = "dysmsapi.aliyuncs.com"; + return new Client(config); + }catch (Exception e){ + // 错误 message + log.error(e.getMessage()); + throw new RenException("短信连接建立失败"); + } } } From 680ed4596f4459d6c9a997c737b9832c78244028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Sat, 17 May 2025 10:59:00 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=B8=9A=E5=8A=A1=E6=B5=81=E7=A8=8Bbug=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E7=9A=84=E9=94=99=E8=AF=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=92=8C=E8=AF=B4=E6=98=8E=20--ErrorCode.java=20?= =?UTF-8?q?=E6=96=B0=E7=9A=84=E9=94=99=E8=AF=AF=E4=BB=A3=E7=A0=81=20--Logi?= =?UTF-8?q?nController.java=20=E4=BF=AE=E5=A4=8D=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E6=B5=81=E7=A8=8Bbug=20--messages.properties=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=96=B0=E7=9A=84=E4=B8=9A=E5=8A=A1=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/xiaozhi/common/exception/ErrorCode.java | 2 +- .../modules/security/controller/LoginController.java | 12 +++++------- .../src/main/resources/i18n/messages.properties | 1 + 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java index c59a6a14..6a9af011 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java +++ b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java @@ -20,7 +20,7 @@ public interface ErrorCode { int ACCOUNT_DISABLE = 10005; int IDENTIFIER_NOT_NULL = 10006; int CAPTCHA_ERROR = 10007; - int SUB_MENU_EXIST = 10008; + int PHONE_NOT_NULL = 10008; int PASSWORD_ERROR = 10009; int SUPERIOR_DEPT_ERROR = 10011; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java index 7731caa0..8ffd2b95 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java @@ -59,8 +59,8 @@ public class LoginController { @GetMapping("/smsVerification") @Operation(summary = "短信验证码") public void smsVerification(String phone) throws IOException { - // uuid不能为空 - AssertUtils.isBlank(phone, ErrorCode.IDENTIFIER_NOT_NULL); + // 手机号码不能为空 + AssertUtils.isBlank(phone, ErrorCode.PHONE_NOT_NULL); // 发送短信验证码 captchaService.sendSMSValidateCode(phone); } @@ -99,7 +99,7 @@ public class LoginController { if (isMobileRegister) { // 验证用户是否是手机号码 boolean validPhone = ValidatorUtils.isValidPhone(login.getUsername()); - if (validPhone) { + if (!validPhone) { throw new RenException("用户名不是手机号码,请重新输入"); } // 验证短信验证码是否正常 @@ -156,8 +156,8 @@ public class LoginController { ValidatorUtils.validateEntity(dto); // 验证用户是否是手机号码 boolean validPhone = ValidatorUtils.isValidPhone(dto.getPhone()); - if (validPhone) { - throw new RenException("用户名不是手机号码,请重新输入"); + if (!validPhone) { + throw new RenException("用户名不是手机号码,无法提供找回密码服务"); }; // 按照用户名获取用户 @@ -165,8 +165,6 @@ public class LoginController { if (userDTO == null) { throw new RenException("用户名或验证码错误"); } - // 验证用户是否是手机号码 - ValidatorUtils.isValidPhone(dto.getPhone()); // 验证短信验证码是否正常 boolean validate = captchaService.validateSMSValidateCode(dto.getPhone(),dto.getCode()); // 判断是否通过验证 diff --git a/main/manager-api/src/main/resources/i18n/messages.properties b/main/manager-api/src/main/resources/i18n/messages.properties index 5126feb2..8d04c5a0 100644 --- a/main/manager-api/src/main/resources/i18n/messages.properties +++ b/main/manager-api/src/main/resources/i18n/messages.properties @@ -9,6 +9,7 @@ 10005=\u8D26\u53F7\u5DF2\u88AB\u505C\u7528 10006=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A 10007=\u9A8C\u8BC1\u7801\u4E0D\u6B63\u786E +10008=\u624B\u673A\u53F7\u7801\u4E0D\u53EF\u4EE5\u4E3A\u7A7A 10009=\u539F\u5BC6\u7801\u4E0D\u6B63\u786E 10010=\u8D26\u53F7\u6216\u5BC6\u7801\u4E0D\u6B63\u786E,\u60A8\u8FD8\u6709\u53EF\u4EE5\u5C1D\u8BD5{0}\u6B21 10011=\u4E0A\u7EA7\u90E8\u95E8\u9009\u62E9\u9519\u8BEF From 7d7324ddbc49316a452291a6137bfb8ba8f62b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=91=E9=9B=A8?= <2375294554@qq.com> Date: Sat, 17 May 2025 11:00:02 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E5=8F=91=E9=80=81=20--loginControllerTest.java=20=E7=9F=AD?= =?UTF-8?q?=E4=BF=A1=E6=B5=8B=E8=AF=95=E7=9A=84=E6=B5=8B=E8=AF=95=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/sys/loginControllerTest.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java diff --git a/main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java b/main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java new file mode 100644 index 00000000..777abbf9 --- /dev/null +++ b/main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java @@ -0,0 +1,53 @@ +package xiaozhi.modules.sys; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import xiaozhi.modules.security.controller.LoginController; +import xiaozhi.modules.security.dto.LoginDTO; +import xiaozhi.modules.sys.dto.RetrievePasswordDTO; + + +@Slf4j +@SpringBootTest +@ActiveProfiles("dev") +class loginControllerTest { + + @Autowired + LoginController loginController; + + @Test + public void testRegister() { + LoginDTO loginDTO = new LoginDTO(); + loginDTO.setUsername("手机号码"); + loginDTO.setPassword("密码"); + loginDTO.setCaptcha("123456"); + loginController.register(loginDTO); + } + + @Test + public void testSmsVerification(){ + try { + loginController.smsVerification("手机号码"); + }catch (Exception e){ + System.out.println(e.getMessage()); + } + } + + @Test + public void testRetrievePassword(){ + try { + RetrievePasswordDTO retrievePasswordDTO = new RetrievePasswordDTO(); + retrievePasswordDTO.setCode("123456"); + retrievePasswordDTO.setPhone("手机号码"); + retrievePasswordDTO.setPassword("密码"); + loginController.retrievePassword(retrievePasswordDTO); + }catch (Exception e){ + System.out.println(e.getMessage()); + } + + } + +} \ No newline at end of file From 91d2ab7d1d5787f2fa530b1c92fc4d5b6939ac27 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Sun, 18 May 2025 11:09:58 +0800 Subject: [PATCH 09/11] =?UTF-8?q?update:=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/manager-api/pom.xml | 4 ++-- .../java/xiaozhi/common/constant/Constant.java | 6 +++--- .../security/controller/LoginController.java | 16 +++++++++------- .../main/resources/db/changelog/202505141129.sql | 11 ----------- .../main/resources/db/changelog/202505141130.sql | 11 +++++++++++ .../db/changelog/db.changelog-master.yaml | 4 ++-- 6 files changed, 27 insertions(+), 25 deletions(-) delete mode 100644 main/manager-api/src/main/resources/db/changelog/202505141129.sql create mode 100644 main/manager-api/src/main/resources/db/changelog/202505141130.sql diff --git a/main/manager-api/pom.xml b/main/manager-api/pom.xml index c4234647..883295bf 100644 --- a/main/manager-api/pom.xml +++ b/main/manager-api/pom.xml @@ -28,7 +28,7 @@ 1.6.2 33.0.0-jre 4.20.0 - 4.1.0 + 4.1.0 3.4.0 @@ -208,7 +208,7 @@ com.aliyun dysmsapi20170525 - ${the-latest-version} + ${aliyun-sms-version} diff --git a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java index 171d1a21..9c0c5be7 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java +++ b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java @@ -159,15 +159,15 @@ public interface Constant { */ ALIYUN_SMS_ACCESS_KEY_SECRET("aliyun.sms.access_key_secret"), /** - * + * 阿里云短信签名 */ ALIYUN_SMS_SIGN_NAME("aliyun.sms.sign_name"), /** - * 已删除 + * 阿里云短信模板 */ ALIYUN_SMS_SMS_CODE_TEMPLATE_CODE("aliyun.sms.sms_code_template_code"), /** - * 已删除 + * 单号码最大短信发送条数 */ SYSTEM_SMS_MAX_SEND_COUNT("system.sms.max_send_count"), /** diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java index 8ffd2b95..3e23bd35 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java @@ -93,8 +93,8 @@ public class LoginController { throw new RenException("当前不允许普通用户注册"); } // 是否开启手机注册 - Boolean isMobileRegister = sysParamsService.getValueObject(Constant.SysMSMParam - .SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); + Boolean isMobileRegister = sysParamsService + .getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); boolean validate; if (isMobileRegister) { // 验证用户是否是手机号码 @@ -103,7 +103,7 @@ public class LoginController { throw new RenException("用户名不是手机号码,请重新输入"); } // 验证短信验证码是否正常 - validate = captchaService.validateSMSValidateCode(login.getUsername(),login.getCaptcha()); + validate = captchaService.validateSMSValidateCode(login.getUsername(), login.getCaptcha()); } else { // 验证是否正确输入验证码 validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha()); @@ -147,8 +147,8 @@ public class LoginController { @Operation(summary = "找回密码") public Result retrievePassword(@RequestBody RetrievePasswordDTO dto) { // 是否开启手机注册 - Boolean isMobileRegister = sysParamsService.getValueObject(Constant.SysMSMParam - .SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); + Boolean isMobileRegister = sysParamsService + .getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); if (!isMobileRegister) { throw new RenException("没有开启手机注册,没法使用找回密码功能"); } @@ -158,7 +158,7 @@ public class LoginController { boolean validPhone = ValidatorUtils.isValidPhone(dto.getPhone()); if (!validPhone) { throw new RenException("用户名不是手机号码,无法提供找回密码服务"); - }; + } // 按照用户名获取用户 SysUserDTO userDTO = sysUserService.getByUsername(dto.getPhone()); @@ -166,7 +166,7 @@ public class LoginController { throw new RenException("用户名或验证码错误"); } // 验证短信验证码是否正常 - boolean validate = captchaService.validateSMSValidateCode(dto.getPhone(),dto.getCode()); + boolean validate = captchaService.validateSMSValidateCode(dto.getPhone(), dto.getCode()); // 判断是否通过验证 if (!validate) { throw new RenException("用户名或验证码错误"); @@ -181,6 +181,8 @@ public class LoginController { @Operation(summary = "公共配置") public Result> pubConfig() { Map config = new HashMap<>(); + config.put("enableMobileRegister", sysParamsService + .getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class)); config.put("version", Constant.VERSION); config.put("allowUserRegister", sysUserService.getAllowUserRegister()); return new Result>().ok(config); diff --git a/main/manager-api/src/main/resources/db/changelog/202505141129.sql b/main/manager-api/src/main/resources/db/changelog/202505141129.sql deleted file mode 100644 index e18bb523..00000000 --- a/main/manager-api/src/main/resources/db/changelog/202505141129.sql +++ /dev/null @@ -1,11 +0,0 @@ --- 添加手机短信注册功能的需要的参数 -INSERT INTO - xiaozhi_esp32_server.sys_params -(id, param_code, param_value, value_type, param_type, remark, creator, create_date, updater, update_date) - VALUES -(501, 'system.enable_mobile_register', 'false', 'boolean', 1, '是否开启手机注册', NULL, NULL, NULL, NULL), -(502, 'system.sms.max_send_count', '5', 'number', 1, '单号码最大短信发送条数', NULL, NULL, NULL, NULL), -(510, 'aliyun.sms.access_key_id', '', 'string', 1, '阿里云平台access_key', NULL, NULL, NULL, NULL), -(511, 'aliyun.sms.access_key_secret', '', 'string', 1, '阿里云平台access_key_secret', NULL, NULL, NULL, NULL), -(512, 'aliyun.sms.sign_name', '', 'string', 1, '阿里云短信签名', NULL, NULL, NULL, NULL), -(513, 'aliyun.sms.sms_code_template_code', '', 'string', 1, '阿里云短信模板', NULL, NULL, NULL, NULL); \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/202505141130.sql b/main/manager-api/src/main/resources/db/changelog/202505141130.sql new file mode 100644 index 00000000..5afefe72 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202505141130.sql @@ -0,0 +1,11 @@ +-- 添加手机短信注册功能的需要的参数 +delete from sys_params where id in (601, 602, 610, 611, 612, 613); +INSERT INTO sys_params +(id, param_code, param_value, value_type, param_type, remark, creator, create_date, updater, update_date) + VALUES +(601, 'system.enable_mobile_register', 'false', 'boolean', 1, '是否开启手机注册', NULL, NULL, NULL, NULL), +(602, 'system.sms.max_send_count', '5', 'number', 1, '单号码最大短信发送条数', NULL, NULL, NULL, NULL), +(610, 'aliyun.sms.access_key_id', '', 'string', 1, '阿里云平台access_key', NULL, NULL, NULL, NULL), +(611, 'aliyun.sms.access_key_secret', '', 'string', 1, '阿里云平台access_key_secret', NULL, NULL, NULL, NULL), +(612, 'aliyun.sms.sign_name', '', 'string', 1, '阿里云短信签名', NULL, NULL, NULL, NULL), +(613, 'aliyun.sms.sms_code_template_code', '', 'string', 1, '阿里云短信模板', NULL, NULL, NULL, NULL); \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 299efc9c..8bc4797f 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -115,9 +115,9 @@ databaseChangeLog: encoding: utf8 path: classpath:db/changelog/202505091409.sql - changeSet: - id: 202505141129 + id: 202505141130 author: zjy changes: - sqlFile: encoding: utf8 - path: classpath:db/changelog/202505141129.sql \ No newline at end of file + path: classpath:db/changelog/202505141130.sql \ No newline at end of file From fe88db2094edf1932f7daf17deec319728960780 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Sun, 18 May 2025 17:38:20 +0800 Subject: [PATCH 10/11] =?UTF-8?q?add:=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=92=8C=E6=B3=A8=E5=86=8C=E9=A1=B5=E9=9D=A2=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=E7=A0=81=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/xiaozhi/common/redis/RedisKeys.java | 14 ++ .../common/validator/ValidatorUtils.java | 22 +- .../modules/security/config/ShiroConfig.java | 1 + .../security/controller/LoginController.java | 49 ++-- .../modules/security/dto/LoginDTO.java | 3 + .../security/dto/SmsVerificationDTO.java | 28 +++ .../security/service/CaptchaService.java | 17 +- .../service/impl/CaptchaServiceImpl.java | 73 +++++- .../resources/db/changelog/202505141130.sql | 11 - .../resources/db/changelog/202505141132.sql | 59 +++++ .../db/changelog/db.changelog-master.yaml | 4 +- main/manager-web/src/apis/httpRequest.js | 25 +- main/manager-web/src/apis/module/admin.js | 14 +- main/manager-web/src/apis/module/agent.js | 18 +- main/manager-web/src/apis/module/device.js | 8 +- main/manager-web/src/apis/module/dict.js | 22 +- main/manager-web/src/apis/module/model.js | 46 ++-- main/manager-web/src/apis/module/ota.js | 14 +- main/manager-web/src/apis/module/timbre.js | 10 +- main/manager-web/src/apis/module/user.js | 51 +++- main/manager-web/src/utils/index.js | 64 +++++ main/manager-web/src/views/DictManagement.vue | 4 +- main/manager-web/src/views/auth.scss | 15 +- main/manager-web/src/views/login.vue | 119 +++++++-- main/manager-web/src/views/register.vue | 227 ++++++++++++++---- 25 files changed, 701 insertions(+), 217 deletions(-) create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java delete mode 100644 main/manager-api/src/main/resources/db/changelog/202505141130.sql create mode 100644 main/manager-api/src/main/resources/db/changelog/202505141132.sql diff --git a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java index cc56e0ee..6daa3b2d 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java +++ b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java @@ -125,4 +125,18 @@ public class RedisKeys { return "sms:Validate:Code:" + phone; } + /** + * 获取短信验证码最后发送时间的缓存key + */ + public static String getSMSLastSendTimeKey(String phone) { + return "sms:Validate:Code:" + phone + ":last_send_time"; + } + + /** + * 获取短信验证码今日发送次数的缓存key + */ + public static String getSMSTodayCountKey(String phone) { + return "sms:Validate:Code:" + phone + ":today_count"; + } + } diff --git a/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java b/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java index b2e5b296..364da0d9 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java +++ b/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java @@ -2,7 +2,6 @@ package xiaozhi.common.validator; import java.util.Locale; import java.util.Set; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; @@ -49,12 +48,20 @@ public class ValidatorUtils { } /** - * 手机号正则表达式 + * 国际手机号正则表达式 + * 要求必须带国际区号,格式:+[国家代码][手机号] + * 例如: + * - +8613800138000 + * - +12345678900 + * - +447123456789 */ - private static final String PHONE_REGEX = "^1[3-9]\\d{9}$"; + private static final String INTERNATIONAL_PHONE_REGEX = "^\\+[1-9]\\d{0,3}[1-9]\\d{4,14}$"; /** - * 校验单参数是否是正确的手机号 + * 校验手机号是否有效 + * 要求必须带国际区号,格式:+[国家代码][手机号] + * 例如:+8613800138000 + * * @param phone 手机号 * @return boolean */ @@ -62,8 +69,9 @@ public class ValidatorUtils { if (phone == null || phone.isEmpty()) { return false; } - Pattern pattern = Pattern.compile(PHONE_REGEX); - Matcher matcher = pattern.matcher(phone); - return matcher.matches(); + + // 验证必须带国际区号的手机号格式 + Pattern pattern = Pattern.compile(INTERNATIONAL_PHONE_REGEX); + return pattern.matcher(phone).matches(); } } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java index 5252336f..318ceeff 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java @@ -80,6 +80,7 @@ public class ShiroConfig { filterMap.put("/doc.html", "anon"); filterMap.put("/favicon.ico", "anon"); filterMap.put("/user/captcha", "anon"); + filterMap.put("/user/smsVerification", "anon"); filterMap.put("/user/login", "anon"); filterMap.put("/user/pub-config", "anon"); filterMap.put("/user/register", "anon"); diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java index 3e23bd35..b8186423 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java @@ -2,6 +2,7 @@ package xiaozhi.modules.security.controller; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.springframework.web.bind.annotation.GetMapping; @@ -24,6 +25,7 @@ import xiaozhi.common.utils.Result; import xiaozhi.common.validator.AssertUtils; import xiaozhi.common.validator.ValidatorUtils; import xiaozhi.modules.security.dto.LoginDTO; +import xiaozhi.modules.security.dto.SmsVerificationDTO; import xiaozhi.modules.security.password.PasswordUtils; import xiaozhi.modules.security.service.CaptchaService; import xiaozhi.modules.security.service.SysUserTokenService; @@ -31,8 +33,10 @@ import xiaozhi.modules.security.user.SecurityUser; import xiaozhi.modules.sys.dto.PasswordDTO; import xiaozhi.modules.sys.dto.RetrievePasswordDTO; import xiaozhi.modules.sys.dto.SysUserDTO; +import xiaozhi.modules.sys.service.SysDictDataService; import xiaozhi.modules.sys.service.SysParamsService; import xiaozhi.modules.sys.service.SysUserService; +import xiaozhi.modules.sys.vo.SysDictDataItem; /** * 登录控制层 @@ -46,6 +50,7 @@ public class LoginController { private final SysUserTokenService sysUserTokenService; private final CaptchaService captchaService; private final SysParamsService sysParamsService; + private final SysDictDataService sysDictDataService; @GetMapping("/captcha") @Operation(summary = "验证码") @@ -56,22 +61,31 @@ public class LoginController { captchaService.create(response, uuid); } - @GetMapping("/smsVerification") + @PostMapping("/smsVerification") @Operation(summary = "短信验证码") - public void smsVerification(String phone) throws IOException { - // 手机号码不能为空 - AssertUtils.isBlank(phone, ErrorCode.PHONE_NOT_NULL); + public Result smsVerification(@RequestBody SmsVerificationDTO dto) { + // 验证图形验证码 + boolean validate = captchaService.validate(dto.getCaptchaId(), dto.getCaptcha(), true); + if (!validate) { + throw new RenException("图形验证码错误"); + } + Boolean isMobileRegister = sysParamsService + .getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); + if (!isMobileRegister) { + throw new RenException("没有开启手机注册,没法使用短信验证码功能"); + } // 发送短信验证码 - captchaService.sendSMSValidateCode(phone); + captchaService.sendSMSValidateCode(dto.getPhone()); + return new Result<>(); } @PostMapping("/login") @Operation(summary = "登录") public Result login(@RequestBody LoginDTO login) { // 验证是否正确输入验证码 - boolean validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha()); + boolean validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha(), true); if (!validate) { - throw new RenException("验证码错误,请重新获取"); + throw new RenException("图形验证码错误,请重新获取"); } // 按照用户名获取用户 SysUserDTO userDTO = sysUserService.getByUsername(login.getUsername()); @@ -103,15 +117,18 @@ public class LoginController { throw new RenException("用户名不是手机号码,请重新输入"); } // 验证短信验证码是否正常 - validate = captchaService.validateSMSValidateCode(login.getUsername(), login.getCaptcha()); + validate = captchaService.validateSMSValidateCode(login.getUsername(), login.getMobileCaptcha(), false); + if (!validate) { + throw new RenException("手机验证码错误,请重新获取"); + } } else { // 验证是否正确输入验证码 - validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha()); - } - // 判断是否通过验证 - if (!validate) { - throw new RenException("验证码错误,请重新获取"); + validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha(), true); + if (!validate) { + throw new RenException("图形验证码错误,请重新获取"); + } } + // 按照用户名获取用户 SysUserDTO userDTO = sysUserService.getByUsername(login.getUsername()); if (userDTO != null) { @@ -172,8 +189,7 @@ public class LoginController { throw new RenException("用户名或验证码错误"); } - Long userId = SecurityUser.getUserId(); - sysUserService.changePasswordDirectly(userId, dto.getPassword()); + sysUserService.changePasswordDirectly(userDTO.getId(), dto.getPassword()); return new Result<>(); } @@ -185,6 +201,9 @@ public class LoginController { .getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class)); config.put("version", Constant.VERSION); config.put("allowUserRegister", sysUserService.getAllowUserRegister()); + List list = sysDictDataService.getDictDataByType("MOBILE_AREA"); + config.put("mobileAreaList", list); + return new Result>().ok(config); } } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java index cab143be..12b99d32 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java @@ -25,6 +25,9 @@ public class LoginDTO implements Serializable { @NotBlank(message = "{sysuser.captcha.require}") private String captcha; + @Schema(description = "手机验证码") + private String mobileCaptcha; + @Schema(description = "唯一标识") @NotBlank(message = "{sysuser.uuid.require}") private String captchaId; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java new file mode 100644 index 00000000..425ef059 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java @@ -0,0 +1,28 @@ +package xiaozhi.modules.security.dto; + +import java.io.Serializable; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 短信验证码请求DTO + */ +@Data +@Schema(description = "短信验证码请求") +public class SmsVerificationDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "手机号码") + @NotBlank(message = "{sysuser.username.require}") + private String phone; + + @Schema(description = "验证码") + @NotBlank(message = "{sysuser.captcha.require}") + private String captcha; + + @Schema(description = "唯一标识") + @NotBlank(message = "{sysuser.uuid.require}") + private String captchaId; +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java b/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java index 43ae6432..0db9d296 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java @@ -18,21 +18,28 @@ public interface CaptchaService { /** * 验证码效验 - * @param uuid uuid - * @param code 验证码 + * + * @param uuid uuid + * @param code 验证码 + * @param delete 是否删除验证码 * @return true:成功 false:失败 */ - boolean validate(String uuid, String code); + boolean validate(String uuid, String code, Boolean delete); /** * 发送短信验证码 + * * @param phone 手机 */ void sendSMSValidateCode(String phone); /** * 验证短信验证码 - * @param phone 手机 + * + * @param phone 手机 + * @param code 验证码 + * @param delete 是否删除验证码 + * @return true:成功 false:失败 */ - boolean validateSMSValidateCode(String phone,String code); + boolean validateSMSValidateCode(String phone, String code, Boolean delete); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java index f151d178..a324c27f 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java @@ -15,10 +15,13 @@ import com.wf.captcha.base.Captcha; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; +import xiaozhi.common.constant.Constant; +import xiaozhi.common.exception.RenException; import xiaozhi.common.redis.RedisKeys; import xiaozhi.common.redis.RedisUtils; import xiaozhi.modules.security.service.CaptchaService; import xiaozhi.modules.sms.service.SmsService; +import xiaozhi.modules.sys.service.SysParamsService; /** * 验证码 @@ -29,6 +32,8 @@ public class CaptchaServiceImpl implements CaptchaService { private RedisUtils redisUtils; @Resource private SmsService smsService; + @Resource + private SysParamsService sysParamsService; @Value("${renren.redis.open}") private boolean open; /** @@ -55,12 +60,12 @@ public class CaptchaServiceImpl implements CaptchaService { } @Override - public boolean validate(String uuid, String code) { + public boolean validate(String uuid, String code, Boolean delete) { if (StringUtils.isBlank(code)) { return false; } // 获取验证码 - String captcha = getCache(uuid); + String captcha = getCache(uuid, delete); // 效验成功 if (code.equalsIgnoreCase(captcha)) { @@ -72,24 +77,71 @@ public class CaptchaServiceImpl implements CaptchaService { @Override public void sendSMSValidateCode(String phone) { + // 检查发送间隔 + String lastSendTimeKey = RedisKeys.getSMSLastSendTimeKey(phone); + String lastSendTime = (String) redisUtils.get(lastSendTimeKey); + if (lastSendTime != null) { + long lastSendTimeLong = Long.parseLong(lastSendTime); + long currentTime = System.currentTimeMillis(); + long timeDiff = currentTime - lastSendTimeLong; + if (timeDiff < 60000) { + throw new RenException("发送太频繁,请" + (60000 - timeDiff) / 1000 + "秒后再试"); + } + } + + // 检查今日发送次数 + String todayCountKey = RedisKeys.getSMSTodayCountKey(phone); + Integer todayCount = (Integer) redisUtils.get(todayCountKey); + if (todayCount == null) { + todayCount = 0; + } + + // 获取最大发送次数限制 + Integer maxSendCount = sysParamsService.getValueObject( + Constant.SysMSMParam.SYSTEM_SMS_MAX_SEND_COUNT.getValue(), + Integer.class); + if (maxSendCount == null) { + maxSendCount = 5; // 默认值 + } + + if (todayCount >= maxSendCount) { + throw new RenException("今日发送次数已达上限"); + } + String key = RedisKeys.getSMSValidateCodeKey(phone); String validateCodes = generateValidateCode(6); - setCache(key,validateCodes); - //发送验证码短信 - smsService.sendVerificationCodeSms(phone,validateCodes); + + // 设置验证码 + setCache(key, validateCodes); + + // 设置最后发送时间(60秒) + redisUtils.set(lastSendTimeKey, String.valueOf(System.currentTimeMillis()), 60); + + // 更新今日发送次数 + if (todayCount == 0) { + // 如果是今天第一次发送,设置24小时过期 + redisUtils.set(todayCountKey, 1, RedisUtils.DEFAULT_EXPIRE); + } else { + redisUtils.set(todayCountKey, todayCount + 1, RedisUtils.DEFAULT_EXPIRE); + } + + // 发送验证码短信 + // smsService.sendVerificationCodeSms(phone, validateCodes); } @Override - public boolean validateSMSValidateCode(String phone,String code) { + public boolean validateSMSValidateCode(String phone, String code, Boolean delete) { String key = RedisKeys.getSMSValidateCodeKey(phone); - return validate(key, code); + return validate(key, code, delete); } + /** * 生成指定数量的随机数验证码 + * * @param length 数量 * @return 随机码 */ - private String generateValidateCode(Integer length) { + private String generateValidateCode(Integer length) { String chars = "0123456789"; // 字符范围可以自定义:数字 Random random = new Random(); StringBuilder code = new StringBuilder(); @@ -102,18 +154,19 @@ public class CaptchaServiceImpl implements CaptchaService { private void setCache(String key, String value) { if (open) { key = RedisKeys.getCaptchaKey(key); + // 设置5分钟过期 redisUtils.set(key, value, 300); } else { localCache.put(key, value); } } - private String getCache(String key) { + private String getCache(String key, Boolean delete) { if (open) { key = RedisKeys.getCaptchaKey(key); String captcha = (String) redisUtils.get(key); // 删除验证码 - if (captcha != null) { + if (captcha != null && delete) { redisUtils.delete(key); } diff --git a/main/manager-api/src/main/resources/db/changelog/202505141130.sql b/main/manager-api/src/main/resources/db/changelog/202505141130.sql deleted file mode 100644 index 5afefe72..00000000 --- a/main/manager-api/src/main/resources/db/changelog/202505141130.sql +++ /dev/null @@ -1,11 +0,0 @@ --- 添加手机短信注册功能的需要的参数 -delete from sys_params where id in (601, 602, 610, 611, 612, 613); -INSERT INTO sys_params -(id, param_code, param_value, value_type, param_type, remark, creator, create_date, updater, update_date) - VALUES -(601, 'system.enable_mobile_register', 'false', 'boolean', 1, '是否开启手机注册', NULL, NULL, NULL, NULL), -(602, 'system.sms.max_send_count', '5', 'number', 1, '单号码最大短信发送条数', NULL, NULL, NULL, NULL), -(610, 'aliyun.sms.access_key_id', '', 'string', 1, '阿里云平台access_key', NULL, NULL, NULL, NULL), -(611, 'aliyun.sms.access_key_secret', '', 'string', 1, '阿里云平台access_key_secret', NULL, NULL, NULL, NULL), -(612, 'aliyun.sms.sign_name', '', 'string', 1, '阿里云短信签名', NULL, NULL, NULL, NULL), -(613, 'aliyun.sms.sms_code_template_code', '', 'string', 1, '阿里云短信模板', NULL, NULL, NULL, NULL); \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/202505141132.sql b/main/manager-api/src/main/resources/db/changelog/202505141132.sql new file mode 100644 index 00000000..4e60a350 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202505141132.sql @@ -0,0 +1,59 @@ +-- 添加手机短信注册功能的需要的参数 +delete from sys_params where id in (601, 602, 610, 611, 612, 613); +INSERT INTO sys_params +(id, param_code, param_value, value_type, param_type, remark, creator, create_date, updater, update_date) + VALUES +(601, 'system.enable_mobile_register', 'false', 'boolean', 1, '是否开启手机注册', NULL, NULL, NULL, NULL), +(602, 'system.sms.max_send_count', '10', 'number', 1, '单号码单日最大短信发送条数', NULL, NULL, NULL, NULL), +(610, 'aliyun.sms.access_key_id', '', 'string', 1, '阿里云平台access_key', NULL, NULL, NULL, NULL), +(611, 'aliyun.sms.access_key_secret', '', 'string', 1, '阿里云平台access_key_secret', NULL, NULL, NULL, NULL), +(612, 'aliyun.sms.sign_name', '', 'string', 1, '阿里云短信签名', NULL, NULL, NULL, NULL), +(613, 'aliyun.sms.sms_code_template_code', '', 'string', 1, '阿里云短信模板', NULL, NULL, NULL, NULL); + +-- 增加手机区域字典 +-- 插入固件类型字典类型 +delete from `sys_dict_type` where `id` = 102; +INSERT INTO `sys_dict_type` (`id`, `dict_type`, `dict_name`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES +(102, 'MOBILE_AREA', '手机区域', '手机区域字典', 0, 1, NOW(), 1, NOW()); + +-- 插入固件类型字典数据 +delete from `sys_dict_data` where `dict_type_id` = 102; +INSERT INTO `sys_dict_data` (`id`, `dict_type_id`, `dict_label`, `dict_value`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES +(102001, 102, '中国大陆', '+86', '中国大陆', 1, 1, NOW(), 1, NOW()), +(102002, 102, '中国香港', '+852', '中国香港', 2, 1, NOW(), 1, NOW()), +(102003, 102, '中国澳门', '+853', '中国澳门', 3, 1, NOW(), 1, NOW()), +(102004, 102, '中国台湾', '+886', '中国台湾', 4, 1, NOW(), 1, NOW()), +(102005, 102, '美国/加拿大', '+1', '美国/加拿大', 5, 1, NOW(), 1, NOW()), +(102006, 102, '英国', '+44', '英国', 6, 1, NOW(), 1, NOW()), +(102007, 102, '法国', '+33', '法国', 7, 1, NOW(), 1, NOW()), +(102008, 102, '意大利', '+39', '意大利', 8, 1, NOW(), 1, NOW()), +(102009, 102, '德国', '+49', '德国', 9, 1, NOW(), 1, NOW()), +(102010, 102, '波兰', '+48', '波兰', 10, 1, NOW(), 1, NOW()), +(102011, 102, '瑞士', '+41', '瑞士', 11, 1, NOW(), 1, NOW()), +(102012, 102, '西班牙', '+34', '西班牙', 12, 1, NOW(), 1, NOW()), +(102013, 102, '丹麦', '+45', '丹麦', 13, 1, NOW(), 1, NOW()), +(102014, 102, '马来西亚', '+60', '马来西亚', 14, 1, NOW(), 1, NOW()), +(102015, 102, '澳大利亚', '+61', '澳大利亚', 15, 1, NOW(), 1, NOW()), +(102016, 102, '印度尼西亚', '+62', '印度尼西亚', 16, 1, NOW(), 1, NOW()), +(102017, 102, '菲律宾', '+63', '菲律宾', 17, 1, NOW(), 1, NOW()), +(102018, 102, '新西兰', '+64', '新西兰', 18, 1, NOW(), 1, NOW()), +(102019, 102, '新加坡', '+65', '新加坡', 19, 1, NOW(), 1, NOW()), +(102020, 102, '泰国', '+66', '泰国', 20, 1, NOW(), 1, NOW()), +(102021, 102, '日本', '+81', '日本', 21, 1, NOW(), 1, NOW()), +(102022, 102, '韩国', '+82', '韩国', 22, 1, NOW(), 1, NOW()), +(102023, 102, '越南', '+84', '越南', 23, 1, NOW(), 1, NOW()), +(102024, 102, '印度', '+91', '印度', 24, 1, NOW(), 1, NOW()), +(102025, 102, '巴基斯坦', '+92', '巴基斯坦', 25, 1, NOW(), 1, NOW()), +(102026, 102, '尼日利亚', '+234', '尼日利亚', 26, 1, NOW(), 1, NOW()), +(102027, 102, '孟加拉国', '+880', '孟加拉国', 27, 1, NOW(), 1, NOW()), +(102028, 102, '沙特阿拉伯', '+966', '沙特阿拉伯', 28, 1, NOW(), 1, NOW()), +(102029, 102, '阿联酋', '+971', '阿联酋', 29, 1, NOW(), 1, NOW()), +(102030, 102, '巴西', '+55', '巴西', 30, 1, NOW(), 1, NOW()), +(102031, 102, '墨西哥', '+52', '墨西哥', 31, 1, NOW(), 1, NOW()), +(102032, 102, '智利', '+56', '智利', 32, 1, NOW(), 1, NOW()), +(102033, 102, '阿根廷', '+54', '阿根廷', 33, 1, NOW(), 1, NOW()), +(102034, 102, '埃及', '+20', '埃及', 34, 1, NOW(), 1, NOW()), +(102035, 102, '南非', '+27', '南非', 35, 1, NOW(), 1, NOW()), +(102036, 102, '肯尼亚', '+254', '肯尼亚', 36, 1, NOW(), 1, NOW()), +(102037, 102, '坦桑尼亚', '+255', '坦桑尼亚', 37, 1, NOW(), 1, NOW()), +(102038, 102, '哈萨克斯坦', '+7', '哈萨克斯坦', 38, 1, NOW(), 1, NOW()); diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 8bc4797f..d4dfc03c 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -115,9 +115,9 @@ databaseChangeLog: encoding: utf8 path: classpath:db/changelog/202505091409.sql - changeSet: - id: 202505141130 + id: 202505141132 author: zjy changes: - sqlFile: encoding: utf8 - path: classpath:db/changelog/202505141130.sql \ No newline at end of file + path: classpath:db/changelog/202505141132.sql \ No newline at end of file diff --git a/main/manager-web/src/apis/httpRequest.js b/main/manager-web/src/apis/httpRequest.js index 43cdf92d..245bf6cc 100755 --- a/main/manager-web/src/apis/httpRequest.js +++ b/main/manager-web/src/apis/httpRequest.js @@ -20,6 +20,7 @@ function sendRequest() { return { _sucCallback: null, _failCallback: null, + _networkFailCallback: null, _method: 'GET', _data: {}, _header: { 'content-type': 'application/json; charset=utf-8' }, @@ -36,7 +37,7 @@ function sendRequest() { headers: this._header, responseType: this._responseType }).then((res) => { - const error = httpHandlerError(res, this._failCallback); + const error = httpHandlerError(res, this._failCallback, this._networkFailCallback); if (error) { return } @@ -47,7 +48,7 @@ function sendRequest() { }).catch((res) => { // 打印失败响应 console.log('catch', res) - httpHandlerError(res, this._failCallback) + httpHandlerError(res, this._failCallback, this._networkFailCallback) }) return this }, @@ -59,6 +60,10 @@ function sendRequest() { this._failCallback = callback return this }, + 'networkFail'(callback) { + this._networkFailCallback = callback + return this + }, 'url'(url) { if (url) { url = url.replaceAll('$', '/') @@ -95,11 +100,11 @@ function sendRequest() { /** * Info 请求完成后返回信息 - * callBack 回调函数 - * errTip 自定义错误信息 + * failCallback 回调函数 + * networkFailCallback 回调函数 */ // 在错误处理函数中添加日志 -function httpHandlerError(info, callBack) { +function httpHandlerError(info, failCallback, networkFailCallback) { /** 请求成功,退出该函数 可以根据项目需求来判断是否请求成功。这里判断的是status为200的时候是成功 */ let networkError = false @@ -111,12 +116,16 @@ function httpHandlerError(info, callBack) { goToPage(Constant.PAGE.LOGIN, true); return true } else { - showDanger(info.data.msg) + if (failCallback) { + failCallback(info) + } else { + showDanger(info.data.msg) + } return true } } - if (callBack) { - callBack(info) + if (networkFailCallback) { + networkFailCallback(info) } else { showDanger(`网络请求出现了错误【${info.status}】`) } diff --git a/main/manager-web/src/apis/module/admin.js b/main/manager-web/src/apis/module/admin.js index fecc5877..92c344e2 100644 --- a/main/manager-web/src/apis/module/admin.js +++ b/main/manager-web/src/apis/module/admin.js @@ -18,7 +18,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('请求失败:', err) RequestService.reAjaxFun(() => { this.getUserList(callback) @@ -34,7 +34,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('删除失败:', err) RequestService.reAjaxFun(() => { this.deleteUser(id, callback) @@ -50,7 +50,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('重置密码失败:', err) RequestService.reAjaxFun(() => { this.resetUserPassword(id, callback) @@ -72,7 +72,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('获取参数列表失败:', err) RequestService.reAjaxFun(() => { this.getParamsList(params, callback) @@ -89,7 +89,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('添加参数失败:', err) RequestService.reAjaxFun(() => { this.addParam(data, callback) @@ -106,7 +106,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('更新参数失败:', err) RequestService.reAjaxFun(() => { this.updateParam(data, callback) @@ -123,7 +123,7 @@ export default { RequestService.clearRequestTime() callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('删除参数失败:', err) RequestService.reAjaxFun(() => { this.deleteParam(ids, callback) diff --git a/main/manager-web/src/apis/module/agent.js b/main/manager-web/src/apis/module/agent.js index 8aa89072..e912c534 100644 --- a/main/manager-web/src/apis/module/agent.js +++ b/main/manager-web/src/apis/module/agent.js @@ -12,7 +12,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.getAgentList(callback); }); @@ -28,7 +28,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.addAgent(agentName, callback); }); @@ -43,7 +43,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.deleteAgent(agentId, callback); }); @@ -58,7 +58,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('获取配置失败:', err); RequestService.reAjaxFun(() => { this.getDeviceConfig(deviceId, callback); @@ -75,7 +75,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.updateAgentConfig(agentId, configData, callback); }); @@ -90,7 +90,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('获取模板失败:', err); RequestService.reAjaxFun(() => { this.getAgentTemplate(callback); @@ -107,7 +107,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.getAgentSessions(agentId, params, callback); }); @@ -122,7 +122,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.getAgentChatHistory(agentId, sessionId, callback); }); @@ -137,7 +137,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.getAudioId(audioId, callback); }); diff --git a/main/manager-web/src/apis/module/device.js b/main/manager-web/src/apis/module/device.js index 53738bc6..2f20425d 100644 --- a/main/manager-web/src/apis/module/device.js +++ b/main/manager-web/src/apis/module/device.js @@ -11,7 +11,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('获取设备列表失败:', err); RequestService.reAjaxFun(() => { this.getAgentBindDevices(agentId, callback); @@ -28,7 +28,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('解绑设备失败:', err); RequestService.reAjaxFun(() => { this.unbindDevice(device_id, callback); @@ -44,7 +44,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('绑定设备失败:', err); RequestService.reAjaxFun(() => { this.bindDevice(agentId, deviceCode, callback); @@ -59,7 +59,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('更新OTA状态失败:', err) this.$message.error(err.msg || '更新OTA状态失败') RequestService.reAjaxFun(() => { diff --git a/main/manager-web/src/apis/module/dict.js b/main/manager-web/src/apis/module/dict.js index caf78279..8919c46b 100644 --- a/main/manager-web/src/apis/module/dict.js +++ b/main/manager-web/src/apis/module/dict.js @@ -18,7 +18,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('获取字典类型列表失败:', err) this.$message.error(err.msg || '获取字典类型列表失败') RequestService.reAjaxFun(() => { @@ -36,7 +36,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('获取字典类型详情失败:', err) this.$message.error(err.msg || '获取字典类型详情失败') RequestService.reAjaxFun(() => { @@ -55,7 +55,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('新增字典类型失败:', err) this.$message.error(err.msg || '新增字典类型失败') RequestService.reAjaxFun(() => { @@ -74,7 +74,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('更新字典类型失败:', err) this.$message.error(err.msg || '更新字典类型失败') RequestService.reAjaxFun(() => { @@ -93,7 +93,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('删除字典类型失败:', err) this.$message.error(err.msg || '删除字典类型失败') RequestService.reAjaxFun(() => { @@ -119,7 +119,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('获取字典数据列表失败:', err) this.$message.error(err.msg || '获取字典数据列表失败') RequestService.reAjaxFun(() => { @@ -137,7 +137,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('获取字典数据详情失败:', err) this.$message.error(err.msg || '获取字典数据详情失败') RequestService.reAjaxFun(() => { @@ -156,7 +156,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('新增字典数据失败:', err) this.$message.error(err.msg || '新增字典数据失败') RequestService.reAjaxFun(() => { @@ -175,7 +175,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('更新字典数据失败:', err) this.$message.error(err.msg || '更新字典数据失败') RequestService.reAjaxFun(() => { @@ -194,7 +194,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('删除字典数据失败:', err) this.$message.error(err.msg || '删除字典数据失败') RequestService.reAjaxFun(() => { @@ -217,7 +217,7 @@ export default { reject(new Error(res.data?.msg || '获取字典数据列表失败')) } }) - .fail((err) => { + .networkFail((err) => { console.error('获取字典数据列表失败:', err) reject(err) }).send() diff --git a/main/manager-web/src/apis/module/model.js b/main/manager-web/src/apis/module/model.js index ec0ea32f..f0a6efbd 100644 --- a/main/manager-web/src/apis/module/model.js +++ b/main/manager-web/src/apis/module/model.js @@ -18,7 +18,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('获取模型列表失败:', err) RequestService.reAjaxFun(() => { this.getModelList(params, callback) @@ -34,7 +34,7 @@ export default { RequestService.clearRequestTime() callback(res.data?.data || []) }) - .fail((err) => { + .networkFail((err) => { console.error('获取供应器列表失败:', err) this.$message.error('获取供应器列表失败') RequestService.reAjaxFun(() => { @@ -65,7 +65,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('新增模型失败:', err) this.$message.error(err.msg || '新增模型失败') RequestService.reAjaxFun(() => { @@ -82,7 +82,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('删除模型失败:', err) this.$message.error(err.msg || '删除模型失败') RequestService.reAjaxFun(() => { @@ -100,7 +100,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.getModelNames(modelType, modelName, callback); }); @@ -118,7 +118,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail(() => { + .networkFail(() => { RequestService.reAjaxFun(() => { this.getModelVoices(modelId, voiceName, callback); }); @@ -133,7 +133,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('获取模型配置失败:', err) this.$message.error(err.msg || '获取模型配置失败') RequestService.reAjaxFun(() => { @@ -150,7 +150,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('更新模型状态失败:', err) this.$message.error(err.msg || '更新模型状态失败') RequestService.reAjaxFun(() => { @@ -166,20 +166,20 @@ export default { configJson: formData.configJson }; RequestService.sendRequest() - .url(`${getServiceUrl()}/models/${modelType}/${provideCode}/${id}`) - .method('PUT') - .data(payload) - .success((res) => { - RequestService.clearRequestTime(); - callback(res); - }) - .fail((err) => { - console.error('更新模型失败:', err); - this.$message.error(err.msg || '更新模型失败'); - RequestService.reAjaxFun(() => { - this.updateModel(params, callback); - }); - }).send(); + .url(`${getServiceUrl()}/models/${modelType}/${provideCode}/${id}`) + .method('PUT') + .data(payload) + .success((res) => { + RequestService.clearRequestTime(); + callback(res); + }) + .networkFail((err) => { + console.error('更新模型失败:', err); + this.$message.error(err.msg || '更新模型失败'); + RequestService.reAjaxFun(() => { + this.updateModel(params, callback); + }); + }).send(); }, // 设置默认模型 setDefaultModel(id, callback) { @@ -190,7 +190,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('设置默认模型失败:', err) this.$message.error(err.msg || '设置默认模型失败') RequestService.reAjaxFun(() => { diff --git a/main/manager-web/src/apis/module/ota.js b/main/manager-web/src/apis/module/ota.js index 438aa3d8..4db86ad7 100644 --- a/main/manager-web/src/apis/module/ota.js +++ b/main/manager-web/src/apis/module/ota.js @@ -12,7 +12,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('获取OTA固件列表失败:', err); RequestService.reAjaxFun(() => { this.getOtaList(params, callback); @@ -28,7 +28,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('获取OTA固件信息失败:', err); RequestService.reAjaxFun(() => { this.getOtaInfo(id, callback); @@ -45,7 +45,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('保存OTA固件信息失败:', err); RequestService.reAjaxFun(() => { this.saveOta(entity, callback); @@ -62,7 +62,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('更新OTA固件信息失败:', err); RequestService.reAjaxFun(() => { this.updateOta(id, entity, callback); @@ -78,7 +78,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('删除OTA固件失败:', err); RequestService.reAjaxFun(() => { this.deleteOta(id, callback); @@ -97,7 +97,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('上传固件文件失败:', err); RequestService.reAjaxFun(() => { this.uploadFirmware(file, callback); @@ -113,7 +113,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('获取下载链接失败:', err); RequestService.reAjaxFun(() => { this.getDownloadUrl(id, callback); diff --git a/main/manager-web/src/apis/module/timbre.js b/main/manager-web/src/apis/module/timbre.js index 91920522..7aa6a711 100644 --- a/main/manager-web/src/apis/module/timbre.js +++ b/main/manager-web/src/apis/module/timbre.js @@ -1,4 +1,4 @@ -import {getServiceUrl} from '../api'; +import { getServiceUrl } from '../api'; import RequestService from '../httpRequest'; export default { @@ -18,7 +18,7 @@ export default { RequestService.clearRequestTime(); callback(res.data || []); }) - .fail((err) => { + .networkFail((err) => { console.error('获取音色列表失败:', err); RequestService.reAjaxFun(() => { this.getVoiceList(params, callback); @@ -42,7 +42,7 @@ export default { .success((res) => { callback(res.data); }) - .fail((err) => { + .networkFail((err) => { console.error('保存音色失败:', err); RequestService.reAjaxFun(() => { this.saveVoice(params, callback); @@ -59,7 +59,7 @@ export default { RequestService.clearRequestTime() callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('删除音色失败:', err); RequestService.reAjaxFun(() => { this.deleteVoice(ids, callback); @@ -82,7 +82,7 @@ export default { .success((res) => { callback(res.data); }) - .fail((err) => { + .networkFail((err) => { console.error('修改音色失败:', err); RequestService.reAjaxFun(() => { this.updateVoice(params, callback); diff --git a/main/manager-web/src/apis/module/user.js b/main/manager-web/src/apis/module/user.js index 84d5b7f9..1ad28b2e 100755 --- a/main/manager-web/src/apis/module/user.js +++ b/main/manager-web/src/apis/module/user.js @@ -4,7 +4,7 @@ import RequestService from '../httpRequest' export default { // 登录 - login(loginForm, callback) { + login(loginForm, callback, failCallback) { RequestService.sendRequest() .url(`${getServiceUrl()}/user/login`) .method('POST') @@ -13,7 +13,11 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail(() => { + .fail((err) => { + RequestService.clearRequestTime() + failCallback(err) + }) + .networkFail(() => { RequestService.reAjaxFun(() => { this.login(loginForm, callback) }) @@ -34,12 +38,32 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { // 添加错误参数 + .networkFail((err) => { // 添加错误参数 }).send() }, + // 发送短信验证码 + sendSmsVerification(data, callback, failCallback) { + RequestService.sendRequest() + .url(`${getServiceUrl()}/user/smsVerification`) + .method('POST') + .data(data) + .success((res) => { + RequestService.clearRequestTime() + callback(res) + }) + .fail((err) => { + RequestService.clearRequestTime() + failCallback(err) + }) + .networkFail(() => { + RequestService.reAjaxFun(() => { + this.sendSmsVerification(data, callback, failCallback) + }) + }).send() + }, // 注册账号 - register(registerForm, callback) { + register(registerForm, callback, failCallback) { RequestService.sendRequest() .url(`${getServiceUrl()}/user/register`) .method('POST') @@ -48,7 +72,14 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail(() => { + .fail((err) => { + RequestService.clearRequestTime() + failCallback(err) + }) + .networkFail(() => { + RequestService.reAjaxFun(() => { + this.register(registerForm, callback, failCallback) + }) }).send() }, // 保存设备配置 @@ -61,7 +92,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('保存配置失败:', err); RequestService.reAjaxFun(() => { this.saveDeviceConfig(device_id, configData, callback); @@ -77,7 +108,7 @@ export default { RequestService.clearRequestTime() callback(res) }) - .fail((err) => { + .networkFail((err) => { console.error('接口请求失败:', err) RequestService.reAjaxFun(() => { this.getUserInfo(callback) @@ -97,7 +128,7 @@ export default { RequestService.clearRequestTime(); successCallback(res); }) - .fail((error) => { + .networkFail((error) => { RequestService.reAjaxFun(() => { this.changePassword(oldPassword, newPassword, successCallback, errorCallback); }); @@ -115,7 +146,7 @@ export default { RequestService.clearRequestTime() successCallback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('修改用户状态失败:', err) RequestService.reAjaxFun(() => { this.changeUserStatus(status, userIds) @@ -131,7 +162,7 @@ export default { RequestService.clearRequestTime(); callback(res); }) - .fail((err) => { + .networkFail((err) => { console.error('获取公共配置失败:', err); RequestService.reAjaxFun(() => { this.getPubConfig(callback); diff --git a/main/manager-web/src/utils/index.js b/main/manager-web/src/utils/index.js index 8a5a960e..aef8de0a 100755 --- a/main/manager-web/src/utils/index.js +++ b/main/manager-web/src/utils/index.js @@ -136,3 +136,67 @@ export function getUUID() { }) } +/** + * 验证手机号格式 + * @param {string} mobile 手机号 + * @param {string} areaCode 区号 + * @returns {boolean} + */ +export function validateMobile(mobile, areaCode) { + // 移除所有非数字字符 + const cleanMobile = mobile.replace(/\D/g, ''); + + // 根据不同区号使用不同的验证规则 + switch (areaCode) { + case '+86': // 中国大陆 + return /^1[3-9]\d{9}$/.test(cleanMobile); + case '+852': // 中国香港 + return /^[569]\d{7}$/.test(cleanMobile); + case '+853': // 中国澳门 + return /^6\d{7}$/.test(cleanMobile); + case '+886': // 中国台湾 + return /^9\d{8}$/.test(cleanMobile); + case '+1': // 美国/加拿大 + return /^[2-9]\d{9}$/.test(cleanMobile); + case '+44': // 英国 + return /^7[1-9]\d{8}$/.test(cleanMobile); + case '+81': // 日本 + return /^[7890]\d{8}$/.test(cleanMobile); + case '+82': // 韩国 + return /^1[0-9]\d{7}$/.test(cleanMobile); + case '+65': // 新加坡 + return /^[89]\d{7}$/.test(cleanMobile); + case '+61': // 澳大利亚 + return /^[4578]\d{8}$/.test(cleanMobile); + case '+49': // 德国 + return /^1[5-7]\d{8}$/.test(cleanMobile); + case '+33': // 法国 + return /^[67]\d{8}$/.test(cleanMobile); + case '+39': // 意大利 + return /^3[0-9]\d{8}$/.test(cleanMobile); + case '+34': // 西班牙 + return /^[6-9]\d{8}$/.test(cleanMobile); + case '+55': // 巴西 + return /^[1-9]\d{10}$/.test(cleanMobile); + case '+91': // 印度 + return /^[6-9]\d{9}$/.test(cleanMobile); + case '+971': // 阿联酋 + return /^[5]\d{8}$/.test(cleanMobile); + case '+966': // 沙特阿拉伯 + return /^[5]\d{8}$/.test(cleanMobile); + case '+880': // 孟加拉国 + return /^1[3-9]\d{8}$/.test(cleanMobile); + case '+234': // 尼日利亚 + return /^[789]\d{9}$/.test(cleanMobile); + case '+254': // 肯尼亚 + return /^[17]\d{8}$/.test(cleanMobile); + case '+255': // 坦桑尼亚 + return /^[67]\d{8}$/.test(cleanMobile); + case '+7': // 哈萨克斯坦 + return /^[67]\d{9}$/.test(cleanMobile); + default: + // 其他国际号码:至少5位,最多15位 + return /^\d{5,15}$/.test(cleanMobile); + } +} + diff --git a/main/manager-web/src/views/DictManagement.vue b/main/manager-web/src/views/DictManagement.vue index 195abf1e..f4de8350 100644 --- a/main/manager-web/src/views/DictManagement.vue +++ b/main/manager-web/src/views/DictManagement.vue @@ -48,8 +48,7 @@