From 93b288ed9dc56b21bfa46f45d787d1aa311adf95 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Mon, 21 Sep 2026 14:59:18 +0800 Subject: [PATCH] =?UTF-8?q?1.=E7=99=BB=E5=BD=95=E5=A2=9E=E5=BC=BA=202.?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=A1=A8=E5=A2=9E=E5=8A=A0=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E8=AF=81=E5=AD=97=E6=AE=B5=203.=E9=92=89=E9=92=89=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/filter/ValidateCodeFilter.java | 19 +++-- .../canteen/entity/CanteenCategoryEntity.java | 6 ++ .../canteen/entity/CanteenSpuEntity.java | 12 ++++ .../canteen/vo/ADCanteenSpuListVO.java | 24 +++++++ .../canteen/vo/app/DailyMenuAppVO.java | 15 ++++ .../service/impl/CanteenSpuServiceImpl.java | 23 ++++-- .../mapper/CanteenCategoryMapper.xml | 1 + .../mapper/CanteenDailyMenuMapper.xml | 19 +++++ .../resources/mapper/CanteenSpuMapper.xml | 2 + .../resources/i18n/messages_en.properties | 4 +- .../resources/i18n/messages_zh_CN.properties | 4 +- .../resources/i18n/messages_zh_HK.properties | 4 +- .../thirdparty/controller/SmsController.java | 14 +++- .../src/main/resources/application.yml | 4 +- .../admin/api/dto/DeptInviteRegisterDTO.java | 28 +++++--- .../bsd/pjcloud/admin/api/entity/SysUser.java | 6 ++ .../admin/controller/SysUserController.java | 6 +- .../admin/service/SysMessageService.java | 13 +++- .../impl/SysDeptInviteServiceImpl.java | 72 ++++++++++++------- .../service/impl/SysMessageServiceImpl.java | 41 ++++++++--- .../service/impl/SysUserServiceImpl.java | 5 ++ 21 files changed, 256 insertions(+), 66 deletions(-) diff --git a/pjcloud-auth/src/main/java/com/bsd/pjcloud/auth/support/filter/ValidateCodeFilter.java b/pjcloud-auth/src/main/java/com/bsd/pjcloud/auth/support/filter/ValidateCodeFilter.java index 94bb000c..0d659d05 100644 --- a/pjcloud-auth/src/main/java/com/bsd/pjcloud/auth/support/filter/ValidateCodeFilter.java +++ b/pjcloud-auth/src/main/java/com/bsd/pjcloud/auth/support/filter/ValidateCodeFilter.java @@ -131,29 +131,40 @@ public class ValidateCodeFilter extends OncePerRequestFilter { String key = CacheConstants.DEFAULT_CODE_KEY + randomStr; if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) { - throw new ValidateCodeException(MsgUtils.getMessage(ErrorCodes.AUTH_CAPTCHA_INVALID)); + throw new ValidateCodeException(invalidCodeMessage(mobile)); } Object codeObj = redisTemplate.opsForValue().get(key); if (codeObj == null) { - throw new ValidateCodeException(MsgUtils.getMessage(ErrorCodes.AUTH_CAPTCHA_INVALID)); + throw new ValidateCodeException(invalidCodeMessage(mobile)); } String saveCode = codeObj.toString(); if (StrUtil.isBlank(saveCode)) { redisTemplate.delete(key); - throw new ValidateCodeException(MsgUtils.getMessage(ErrorCodes.AUTH_CAPTCHA_INVALID)); + throw new ValidateCodeException(invalidCodeMessage(mobile)); } if (!StrUtil.equals(saveCode, code)) { redisTemplate.delete(key); - throw new ValidateCodeException(MsgUtils.getMessage(ErrorCodes.AUTH_CAPTCHA_INVALID)); + throw new ValidateCodeException(invalidCodeMessage(mobile)); } redisTemplate.delete(key); } + /** + * 短信登录与图形验证码使用不同提示:已使用/过期的短信码对用户更友好。 + */ + private String invalidCodeMessage(String mobile) { + // mobile 参数形如 SMS@138xxxx + if (StrUtil.isNotBlank(mobile) && StrUtil.contains(mobile, "SMS")) { + return MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_CODE_INVALID); + } + return MsgUtils.getMessage(ErrorCodes.AUTH_CAPTCHA_INVALID); + } + /** * 是否需要校验客户端,根据client 查询客户端配置 * diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenCategoryEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenCategoryEntity.java index 81a91603..0d520452 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenCategoryEntity.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenCategoryEntity.java @@ -41,6 +41,12 @@ public class CanteenCategoryEntity extends Model { @Schema(description="分类名称") private String name; + /** + * 分类英文名称 + */ + @Schema(description = "分类英文名称") + private String nameEn; + /** * 分类图 */ diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenSpuEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenSpuEntity.java index b7664650..ac5c9483 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenSpuEntity.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenSpuEntity.java @@ -39,6 +39,18 @@ public class CanteenSpuEntity extends Model { @Schema(description="商品名称") private String name; + /** + * 菜品英文名称 + */ + @Schema(description = "菜品英文名称") + private String nameEn; + + /** + * 套餐名(仅 spuType=2 时使用) + */ + @Schema(description = "套餐名") + private String comboName; + /** * 商品简介 */ diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java index ae8c7498..9d260d05 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java @@ -24,6 +24,18 @@ public class ADCanteenSpuListVO { @Schema(description="商品名称") private String name; + /** + * 菜品英文名称 + */ + @Schema(description = "菜品英文名称") + private String nameEn; + + /** + * 套餐名(仅套餐时有值) + */ + @Schema(description = "套餐名") + private String comboName; + /** * 商品简介 */ @@ -43,6 +55,18 @@ public class ADCanteenSpuListVO { @Schema(description="商品分类编号") private Long categoryId; + /** + * 分类名称 + */ + @Schema(description = "分类名称") + private String categoryName; + + /** + * 分类英文名称 + */ + @Schema(description = "分类英文名称") + private String categoryNameEn; + /** * 商品封面图 */ diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java index 01d268be..054402af 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java @@ -36,6 +36,16 @@ public class DailyMenuAppVO { */ private String name; + /** + * 菜品英文名称。 + */ + private String nameEn; + + /** + * 套餐名(仅套餐时有值)。 + */ + private String comboName; + /** * 商品销售价格,单位为分。 */ @@ -56,6 +66,11 @@ public class DailyMenuAppVO { */ private String categoryName; + /** + * 菜品分类英文名称;分类不存在或已删除时为空。 + */ + private String categoryNameEn; + /** * 菜品封面图地址。 */ diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java index 3549d165..c3a50216 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java @@ -407,6 +407,8 @@ public class CanteenSpuServiceImpl extends ServiceImpl + diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml index 7423d4e5..0f0b7611 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml @@ -27,10 +27,13 @@ + + + @@ -80,6 +83,8 @@ t.merchant_id, sp.id AS spu_id, sp.name, + sp.name_en, + sp.combo_name, sp.price, sp.market_price, sp.pic_url, @@ -96,6 +101,7 @@ sp.is_show, c.id AS category_id, c.name AS category_name, + c.name_en AS category_name_en, IFNULL(sp.spu_type, 1) AS spu_type, t.hour_type, @@ -132,6 +138,8 @@ t.merchant_id, sp.id AS spu_id, sp.name, + sp.name_en, + sp.combo_name, sp.price, sp.market_price, sp.pic_url, @@ -148,6 +156,7 @@ sp.is_show, c.id AS category_id, c.name AS category_name, + c.name_en AS category_name_en, IFNULL(sp.spu_type, 1) AS spu_type, t.hour_type FROM canteen_daily_menu t @@ -172,6 +181,8 @@ t.merchant_id, sp.id AS spu_id, sp.name, + sp.name_en, + sp.combo_name, sp.price, sp.market_price, sp.pic_url, @@ -188,6 +199,7 @@ sp.is_show, c.id AS category_id, c.name AS category_name, + c.name_en AS category_name_en, IFNULL(sp.spu_type, 1) AS spu_type, t.hour_type, @@ -217,6 +229,8 @@ m.hour_type, s.id as spu_id, s.`name`, + s.name_en, + s.combo_name, s.price, IFNULL(m.online_stock, 600) as stock, IFNULL(m.online_stock, 600) as online_stock, @@ -227,6 +241,8 @@ s.sort, s.is_show, c.id as category_id, + c.name as category_name, + c.name_en as category_name_en, IFNULL(s.spu_type, 1) as spu_type FROM `canteen_daily_menu` m left JOIN canteen_spu s @@ -255,6 +271,8 @@ m.merchant_id, s.id AS spu_id, s.name, + s.name_en, + s.combo_name, s.price, s.market_price, IFNULL(m.online_stock, 600) AS stock, @@ -271,6 +289,7 @@ s.is_show, c.id AS category_id, c.name AS category_name, + c.name_en AS category_name_en, IFNULL(s.spu_type, 1) AS spu_type FROM canteen_daily_menu m INNER JOIN canteen_spu s ON m.spu_id = s.id diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenSpuMapper.xml b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenSpuMapper.xml index 354a726c..25f314a3 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenSpuMapper.xml +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenSpuMapper.xml @@ -6,6 +6,8 @@ + + diff --git a/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_en.properties b/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_en.properties index 9f07d6bf..ebbaf7c0 100644 --- a/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_en.properties +++ b/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_en.properties @@ -357,7 +357,7 @@ hard.car.batch.same.user.mch=Batch cart must share user/merchant/type hard.car.save.fail=Failed to save cart products common.idempotent.retry=Duplicate request. Please try again later auth.captcha.empty=Verification code is required -auth.captcha.invalid=Invalid verification code +auth.captcha.invalid=Incorrect or expired verification code auth.wx.auth.fail=WeChat authorization failed: {0} auth.wx.openid.missing=WeChat authorization failed; user identifier not received auth.wx.auth.expired=WeChat authorization expired; please authorize again @@ -366,7 +366,7 @@ auth.apple.not.enabled=Apple login is not enabled auth.mini.app.login.fail=WeChat mini program login failed auth.social.auth.expired=Authorization expired; please authorize again sys.app.sms.send.fail=Failed to send SMS code; please try again later -sys.app.sms.code.invalid=SMS code is invalid or expired +sys.app.sms.code.invalid=Incorrect or expired verification code sys.user.password.same=New password must differ from old password sys.user.unbind.type.invalid=Unbind account type does not exist third.recharge.biz.id.invalid=Invalid recharge business ID diff --git a/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_CN.properties b/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_CN.properties index f6b8a84c..371464df 100644 --- a/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_CN.properties +++ b/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_CN.properties @@ -357,7 +357,7 @@ hard.car.batch.same.user.mch=批量购物车必须属于同一用户、商户和 hard.car.save.fail=购物车商品保存失败 common.idempotent.retry=重复请求,请稍后重试 auth.captcha.empty=验证码不能为空 -auth.captcha.invalid=验证码不合法 +auth.captcha.invalid=验证码不正确或已过期 auth.wx.auth.fail=微信授权失败:{0} auth.wx.openid.missing=微信授权失败,未获取到用户标识 auth.wx.auth.expired=微信授权已过期,请重新授权 @@ -366,7 +366,7 @@ auth.apple.not.enabled=未启用Apple登录 auth.mini.app.login.fail=微信小程序登录失败 auth.social.auth.expired=授权已过期,请重新授权 sys.app.sms.send.fail=短信验证码发送失败,请稍后重试 -sys.app.sms.code.invalid=短信验证码错误或已过期 +sys.app.sms.code.invalid=验证码不正确或已过期 sys.user.password.same=新旧密码不能相同 sys.user.unbind.type.invalid=解绑账号类型不存在 third.recharge.biz.id.invalid=充值业务id解析错误 diff --git a/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_HK.properties b/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_HK.properties index 0ff97006..e7d1aa72 100644 --- a/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_HK.properties +++ b/pjcloud-common/pjcloud-common-core/src/main/resources/i18n/messages_zh_HK.properties @@ -357,7 +357,7 @@ hard.car.batch.same.user.mch=批量購物車必須屬於同一用戶、商戶和 hard.car.save.fail=購物車商品保存失敗 common.idempotent.retry=重複請求,請稍後重試 auth.captcha.empty=驗證碼不能為空 -auth.captcha.invalid=驗證碼不合法 +auth.captcha.invalid=驗證碼不正確或已過期 auth.wx.auth.fail=微信授權失敗:{0} auth.wx.openid.missing=微信授權失敗,未獲取到用戶標識 auth.wx.auth.expired=微信授權已過期,請重新授權 @@ -366,7 +366,7 @@ auth.apple.not.enabled=未啟用Apple登錄 auth.mini.app.login.fail=微信小程序登錄失敗 auth.social.auth.expired=授權已過期,請重新授權 sys.app.sms.send.fail=短信驗證碼發送失敗,請稍後重試 -sys.app.sms.code.invalid=短信驗證碼錯誤或已過期 +sys.app.sms.code.invalid=驗證碼不正確或已過期 sys.user.password.same=新舊密碼不能相同 sys.user.unbind.type.invalid=解綁賬號類型不存在 third.recharge.biz.id.invalid=充值業務id解析錯誤 diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java index 403772bd..3cefdebd 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java @@ -3,6 +3,7 @@ package com.bsd.pjcloud.thirdparty.controller; import com.bsd.pjcloud.common.security.annotation.Inner; import com.bsd.pjcloud.thirdparty.service.SmsService; import com.bsd.pjcloud.thirdparty.utils.sms.AliyunSMS; +import com.bsd.pjcloud.thirdparty.utils.sms.SmsHTTPSend; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -17,6 +18,8 @@ public class SmsController { private final SmsService smsService; private final AliyunSMS aliyunSMS; + private final SmsHTTPSend smsHTTPSend; + // 简单发送 @PostMapping("/sendMsg") public void sendSimpleMsg(@RequestParam String mobile,@RequestParam String templateId,@RequestParam("clientIp") String clientIp) { @@ -32,11 +35,18 @@ public class SmsController { smsService.sendSms(mobile, templateId, params,clientIp); } - // 阿里云发送验证码 + /** + * 发送登录/注册验证码(联通短信通道) + */ @PostMapping("/sendVerificationCode") @Inner(value = false) public void sendALiVerificationCode(@RequestParam("mobile") String mobile, @RequestParam("code") String code) { - aliyunSMS.sendVerificationCode(mobile, code); + String content = "您的验证码是" + code + ",5分钟内有效,仅用于完成本次验证。"; + Boolean ok = smsHTTPSend.smsSendByPost(content, mobile); + if (!Boolean.TRUE.equals(ok)) { + // 联通通道失败时回退阿里云,避免验证码完全发不出 + aliyunSMS.sendVerificationCode(mobile, code); + } } diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml index ae1bf739..3616ade5 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml @@ -191,11 +191,11 @@ LianTongSMS: #企业编号 SP_CODE: '11122' #登录用户名 - LOGIN_NAME: '' + LOGIN_NAME: 'zianxin' #登录用户密码 PASSWORD: 'Aa123456' #签名id - AUTOGRAPH_ID: '' + AUTOGRAPH_ID: '13725' hikvision: diff --git a/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/DeptInviteRegisterDTO.java b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/DeptInviteRegisterDTO.java index 43f81098..cdf015d6 100644 --- a/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/DeptInviteRegisterDTO.java +++ b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/dto/DeptInviteRegisterDTO.java @@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** - * 部门邀请注册请求 + * 部门邀请注册请求(手机号 + 短信验证码) */ @Data @Schema(description = "部门邀请注册") @@ -13,18 +13,26 @@ public class DeptInviteRegisterDTO { @Schema(description = "邀请 token", requiredMode = Schema.RequiredMode.REQUIRED) private String token; - @Schema(description = "账号(可与手机号相同)", requiredMode = Schema.RequiredMode.REQUIRED) - private String username; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED) - private String password; - - @Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED) - private String name; - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED) private String phone; + @Schema(description = "短信验证码", requiredMode = Schema.RequiredMode.REQUIRED) + private String smsCode; + + @Schema(description = "姓名(选填,默认用手机号脱敏)") + private String name; + + @Schema(description = "身份证号(选填)") + private String idCard; + @Schema(description = "性别 0未知 1男 2女") private Integer gender; + + /** @deprecated 兼容旧客户端;新流程以手机号为账号 */ + @Schema(description = "账号(可选,默认手机号)") + private String username; + + /** @deprecated 兼容旧客户端;新流程自动生成密码 */ + @Schema(description = "密码(可选,未传则自动生成)") + private String password; } diff --git a/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/entity/SysUser.java b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/entity/SysUser.java index df8354aa..9884f755 100644 --- a/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/entity/SysUser.java +++ b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/entity/SysUser.java @@ -157,6 +157,12 @@ public class SysUser extends Model implements Serializable { @Schema(description = "手机号") private String phone; + /** + * 身份证号(选填) + */ + @Schema(description = "身份证号") + private String idCard; + /** * 性别:0未知 1男 2女(骑手派单等) */ diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java index b15b9f15..040cfae4 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/controller/SysUserController.java @@ -550,11 +550,15 @@ public class SysUserController { } /** - * 忘记密码-发送短信验证码 + * 忘记密码-发送短信验证码(仅已注册手机号) */ @Inner(value = false) @GetMapping("/forgot-password/sms-code") public R sendForgotPasswordSmsCode(@RequestParam("mobile") String mobile) { + long count = userService.count(Wrappers.lambdaQuery().eq(SysUser::getPhone, mobile)); + if (count <= 0) { + return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); + } return sysMessageService.sendSmsCode(mobile); } diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysMessageService.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysMessageService.java index 986a6447..eb5a8f33 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysMessageService.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysMessageService.java @@ -63,12 +63,23 @@ public interface SysMessageService extends IService { /** * 发送手机验证码 + *

未注册手机号也会发送;返回 data=true 表示该手机号已注册,false 表示未注册(可引导邀请注册)。 * * @param mobile mobile - * @return code + * @return data=是否已注册 */ R sendSmsCode(String mobile); + /** + * 校验短信验证码(登录 Redis 键 SMS@mobile) + * + * @param mobile 手机号 + * @param code 验证码 + * @param consume 是否校验成功后删除 + * @return 是否通过 + */ + boolean verifySmsCode(String mobile, String code, boolean consume); + /** * 发送短信 * diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysDeptInviteServiceImpl.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysDeptInviteServiceImpl.java index d61bcf38..d3d0d791 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysDeptInviteServiceImpl.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysDeptInviteServiceImpl.java @@ -29,6 +29,7 @@ import com.bsd.pjcloud.admin.mapper.SysUserMapper; import com.bsd.pjcloud.admin.mapper.SysUserRoleMapper; import com.bsd.pjcloud.admin.service.SysDeptInviteService; import com.bsd.pjcloud.admin.service.SysDeptService; +import com.bsd.pjcloud.admin.service.SysMessageService; import com.bsd.pjcloud.admin.service.SysRoleService; import com.bsd.pjcloud.admin.service.SysUserIdentityService; import com.bsd.pjcloud.admin.utils.flow.FlowUtil; @@ -80,6 +81,7 @@ public class SysDeptInviteServiceImpl extends ServiceImpllambdaQuery().eq(SysUser::getUsername, username)); - if (usernameExists) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, username)); - } boolean phoneExists = sysUserMapper.exists(Wrappers.lambdaQuery().eq(SysUser::getPhone, phone)); if (phoneExists) { return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_PHONE_EXISTING, phone)); } + // 账号默认用手机号前缀 u,兼容旧客户端显式传 username/password + String username = StrUtil.blankToDefault(StrUtil.trim(dto.getUsername()), "u" + phone); + String usernameErr = validateUsername(username); + if (usernameErr != null) { + // 纯数字账号不符合「字母开头」规则时,强制用 u+手机号 + username = "u" + phone; + } + boolean usernameExists = sysUserMapper.exists(Wrappers.lambdaQuery().eq(SysUser::getUsername, username)); + if (usernameExists) { + username = "u" + phone + RandomUtil.randomNumbers(4); + } + + String password = StrUtil.isNotBlank(dto.getPassword()) ? dto.getPassword() + : (RandomUtil.randomNumbers(6) + RandomUtil.randomString("abcdef", 2) + "Aa1"); + if (StrUtil.isNotBlank(dto.getPassword())) { + String passwordErr = validateInvitePassword(password); + if (passwordErr != null) { + return R.failed(passwordErr); + } + } + + String displayName = StrUtil.blankToDefault(StrUtil.trim(dto.getName()), + "用户" + phone.substring(Math.max(0, phone.length() - 4))); + String nameErr = validateInviteName(displayName); + if (nameErr != null) { + displayName = "用户" + phone.substring(Math.max(0, phone.length() - 4)); + } + + String idCard = StrUtil.trim(dto.getIdCard()); + if (StrUtil.isNotBlank(idCard) && !ID_CARD_PATTERN.matcher(idCard).matches()) { + return R.failed("身份证号格式不正确"); + } + MchManageVo mch = resolveMerchantAlongUnitChain(invite.getDeptId()); String attr = mch != null ? StrUtil.blankToDefault(mch.getMerchantAttr(), MerchantAttrEnum.NORMAL.getCode()) : MerchantAttrEnum.NORMAL.getCode(); @@ -327,11 +349,11 @@ public class SysDeptInviteServiceImpl extends ServiceImpl sendSmsCode(String mobile) { - List userList = userMapper.selectList(Wrappers.query().lambda().eq(SysUser::getPhone, mobile)); - - if (CollUtil.isEmpty(userList)) { - log.info("手机号未注册:{}", mobile); - return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); - } + boolean registered = CollUtil.isNotEmpty( + userMapper.selectList(Wrappers.query().lambda().eq(SysUser::getPhone, mobile))); String redisKey = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile; String codeObj = redisTemplate.opsForValue().get(redisKey); + // 有未过期验证码时:仅限制 60 秒内不能重复发送;到期可覆盖重发(避免前端倒计时结束仍提示频繁) if (StrUtil.isNotBlank(codeObj)) { - log.info("手机号验证码未过期:{},{}", mobile, codeObj); - return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_OFTEN)); + Long ttl = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS); + if (ttl != null && ttl > SMS_CODE_TTL_SECONDS - SMS_SEND_INTERVAL_SECONDS) { + log.info("手机号验证码发送过频:{},剩余有效期{}秒", mobile, ttl); + return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_OFTEN)); + } } String code = RandomUtil.randomNumbers(SMS_CODE_LENGTH); log.info("手机号生成验证码成功:{},{}", mobile, code); - redisTemplate.opsForValue().set(redisKey, code, SecurityConstants.CODE_TIME, TimeUnit.SECONDS); + redisTemplate.opsForValue().set(redisKey, code, SMS_CODE_TTL_SECONDS, TimeUnit.SECONDS); try { remoterThirdPartySmsService.sendVerificationCode(mobile, code); - return R.ok(Boolean.TRUE); + // data=是否已注册,便于移动端未注册时引导邀请注册 + return R.ok(registered); } catch (Exception e) { redisTemplate.delete(redisKey); log.error("发送短信验证码失败: mobile={}", mobile, e); @@ -296,6 +301,22 @@ public class SysMessageServiceImpl extends ServiceImpl impl sysUser.setNickname(PersonNameUtils.normalize(userDto.getNickname())); sysUser.setName(PersonNameUtils.normalize(userDto.getName())); sysUser.setEmail(userDto.getEmail()); + // 身份证选填:传空串则清空 + if (userDto.getIdCard() != null) { + String idCard = StrUtil.trim(userDto.getIdCard()); + sysUser.setIdCard(StrUtil.isBlank(idCard) ? "" : idCard); + } // 个人资料:未传手机号时不修改(移动端手机号不可编辑) if (StrUtil.isNotBlank(userDto.getPhone()) && !userDto.getPhone().contains("*")) { sysUser.setPhone(userDto.getPhone());