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] =?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 @@