From b012b2ce71eb543319909e05b6eb28ddff77ac35 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 24 Jan 2025 20:55:43 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT=20?= =?UTF-8?q?=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=88?= =?UTF-8?q?=E6=89=80=E6=9C=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CombinationRecordServiceImpl.java | 2 +- .../service/coupon/CouponServiceImpl.java | 3 + .../point/PointActivityServiceImpl.java | 12 ++-- .../trade/enums/ErrorCodeConstants.java | 1 + .../brokerage/BrokerageUserController.java | 9 +-- .../brokerage/AppBrokerageUserController.java | 3 +- .../brokerage/BrokerageWithdrawMapper.java | 5 +- .../dal/mysql/order/TradeOrderMapper.java | 21 +++++-- .../brokerage/BrokerageWithdrawService.java | 4 +- .../BrokerageWithdrawServiceImpl.java | 14 +++-- .../service/order/TradeOrderQueryService.java | 8 ++- .../order/TradeOrderQueryServiceImpl.java | 5 +- .../order/handler/TradePointOrderHandler.java | 16 ++++- .../TradePointActivityPriceCalculator.java | 22 +++---- .../TradeSeckillActivityPriceCalculator.java | 7 ++- .../service/user/MemberUserServiceImpl.java | 8 +-- .../system/enums/ErrorCodeConstants.java | 1 + .../controller/admin/auth/AuthController.java | 30 ++++++++-- .../admin/auth/vo/AuthLoginReqVO.java | 14 +---- .../admin/auth/vo/AuthRegisterReqVO.java | 10 +--- .../admin/auth/vo/AuthResetPasswordReqVO.java | 32 ++++++++++ .../admin/auth/vo/AuthSmsSendReqVO.java | 2 +- .../auth/vo/CaptchaVerificationReqVO.java | 22 +++++++ .../system/service/auth/AdminAuthService.java | 9 ++- .../service/auth/AdminAuthServiceImpl.java | 58 +++++++++++++------ .../service/permission/RoleServiceImpl.java | 2 +- .../auth/AdminAuthServiceImplTest.java | 3 +- 27 files changed, 223 insertions(+), 100 deletions(-) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java index dc49d7859..0ca97abff 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java @@ -375,7 +375,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { CombinationRecordDO updateRecord = new CombinationRecordDO().setId(item.getId()) .setStatus(status.getStatus()).setEndTime(now); if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数 - updateRecord.setUserCount(records.size()); + updateRecord.setUserCount(records.size()).setVirtualGroup(Boolean.TRUE); // 标记为虚拟成团 } updateRecords.add(updateRecord); }); diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java index b46fccdd3..6e16a61ea 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java @@ -178,6 +178,9 @@ public class CouponServiceImpl implements CouponService { */ @Transactional(rollbackFor = Exception.class) public void invalidateCoupon(Long couponId, Long userId) { + if (couponId == null || couponId <= 0) { + return; + } // 1.1 校验优惠券 CouponDO coupon = couponMapper.selectByIdAndUserId(couponId, userId); if (coupon == null) { diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java index eea65d0b8..51058e8f8 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java @@ -100,7 +100,7 @@ public class PointActivityServiceImpl implements PointActivityService { } pointActivityMapper.updateById(updateObj); // 2.2 更新商品 - updateSeckillProduct(updateObj, updateReqVO.getProducts()); + updatePointProduct(updateObj, updateReqVO.getProducts()); } @Override @@ -157,12 +157,12 @@ public class PointActivityServiceImpl implements PointActivityService { } /** - * 更新秒杀商品 + * 更新积分商品 * - * @param activity 秒杀活动 + * @param activity 积分活动 * @param products 该活动的最新商品配置 */ - private void updateSeckillProduct(PointActivityDO activity, List products) { + private void updatePointProduct(PointActivityDO activity, List products) { // 第一步,对比新老数据,获得添加、修改、删除的列表 List newList = buildPointProductDO(activity, products); List oldList = pointProductMapper.selectListByActivityId(activity.getId()); @@ -211,10 +211,10 @@ public class PointActivityServiceImpl implements PointActivityService { } /** - * 校验秒杀商品是否都存在 + * 校验积分商品是否都存在 * * @param spuId 商品 SPU 编号 - * @param products 秒杀商品 + * @param products 积分商品 */ private void validateProductExists(Long spuId, List products) { // 1. 校验商品 spu 是否存在 diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java index 1055cfe8c..1e65aec62 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java @@ -37,6 +37,7 @@ public interface ErrorCodeConstants { ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单"); ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, "交易订单取消支付失败,原因:订单不是【{}】状态"); ErrorCode ORDER_PICK_UP_FAIL_NOT_VERIFY_USER = new ErrorCode(1_011_000_034, "交易订单自提失败,原因:你没有核销该门店订单的权限"); + ErrorCode ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS = new ErrorCode(1_011_000_035, "交易订单创建失败,原因:用户积分不足"); // ========== After Sale 模块 1-011-000-100 ========== ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在"); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java index fdc8dbb15..197b24903 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java @@ -13,23 +13,24 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService; import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService; import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService; -import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO; import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; +import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; import java.util.Map; import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static java.util.Arrays.asList; @Tag(name = "管理后台 - 分销用户") @RestController @@ -110,7 +111,7 @@ public class BrokerageUserController { // 合计分佣的提现 // TODO @疯狂:如果未来支持了打款这个动作,可能 status 会不对; Map withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId( - userIds, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS); + userIds, asList(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS)); // 拼接返回 return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap, brokerageOrderSummaryMap, withdrawMap)); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java index 1af8f7252..9f9dcde37 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java @@ -35,6 +35,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static java.util.Arrays.asList; @Tag(name = "用户 APP - 分销用户") @RestController @@ -84,7 +85,7 @@ public class AppBrokerageUserController { BrokerageRecordBizTypeEnum.ORDER, BrokerageRecordStatusEnum.SETTLEMENT, beginTime, endTime); // 统计用户提现的佣金 Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryListByUserId(Collections.singleton(userId), - BrokerageWithdrawStatusEnum.AUDIT_SUCCESS).stream() + asList(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS)).stream() .findFirst().map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0); // 统计分销用户数量(一级) Long firstBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(userId, 1); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java index dc2b00916..1942cc42b 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java @@ -41,13 +41,14 @@ public interface BrokerageWithdrawMapper extends BaseMapperX selectCountAndSumPriceByUserIdAndStatus(Collection userIds, Integer status) { + default List selectCountAndSumPriceByUserIdAndStatus(Collection userIds, + Collection status) { List> list = selectMaps(new MPJLambdaWrapper() .select(BrokerageWithdrawDO::getUserId) .selectCount(BrokerageWithdrawDO::getId, BrokerageWithdrawSummaryRespBO::getCount) .selectSum(BrokerageWithdrawDO::getPrice) .in(BrokerageWithdrawDO::getUserId, userIds) - .eq(BrokerageWithdrawDO::getStatus, status) + .in(BrokerageWithdrawDO::getStatus, status) .groupBy(BrokerageWithdrawDO::getUserId)); return BeanUtil.copyToList(list, BrokerageWithdrawSummaryRespBO.class); // selectJoinList有BUG,会与租户插件冲突:解析SQL时,发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java index 21fc038ba..9ea87e80b 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; @@ -106,10 +107,22 @@ public interface TradeOrderMapper extends BaseMapperX { .eq(TradeOrderDO::getCommentStatus, commentStatus)); } - default List selectListByUserIdAndSeckillActivityId(Long userId, Long seckillActivityId) { - return selectList(new LambdaUpdateWrapper<>(TradeOrderDO.class) - .eq(TradeOrderDO::getUserId, userId) - .eq(TradeOrderDO::getSeckillActivityId, seckillActivityId)); + default List selectListByUserIdAndActivityId(Long userId, Long activityId, TradeOrderTypeEnum type) { + LambdaQueryWrapperX queryWrapperX = new LambdaQueryWrapperX<>(); + queryWrapperX.eq(TradeOrderDO::getUserId, userId); + if (TradeOrderTypeEnum.isSeckill(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getSeckillActivityId, activityId); + } + if (TradeOrderTypeEnum.isBargain(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getBargainActivityId, activityId); + } + if (TradeOrderTypeEnum.isCombination(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getCombinationActivityId, activityId); + } + if (TradeOrderTypeEnum.isPoint(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getPointActivityId, activityId); + } + return selectList(queryWrapperX); } default TradeOrderDO selectOneByPickUpVerifyCode(String pickUpVerifyCode) { diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java index 386b4c610..400d68f76 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java @@ -74,7 +74,7 @@ public interface BrokerageWithdrawService { * @return 用户提现汇总 List */ List getWithdrawSummaryListByUserId(Collection userIds, - BrokerageWithdrawStatusEnum status); + Collection status); /** * 按照 userId,汇总每个用户的提现 @@ -84,7 +84,7 @@ public interface BrokerageWithdrawService { * @return 用户提现汇总 Map */ default Map getWithdrawSummaryMapByUserId(Set userIds, - BrokerageWithdrawStatusEnum status) { + Collection status) { return convertMap(getWithdrawSummaryListByUserId(userIds, status), BrokerageWithdrawSummaryRespBO::getUserId); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java index ccac4eb44..c7fec2ba3 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java @@ -42,6 +42,7 @@ import java.util.Collections; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; @@ -98,7 +99,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { // 3.1 审批通过的后续处理 if (BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.equals(status)) { auditBrokerageWithdrawSuccess(withdraw); - // 3.2 审批不通过的后续处理 + // 3.2 审批不通过的后续处理 } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) { brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT, String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle()); @@ -114,11 +115,11 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { .setUserId(withdraw.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue()) .setBizType(PayWalletBizTypeEnum.BROKERAGE_WITHDRAW.getType()).setBizId(withdraw.getId().toString()) .setPrice(withdraw.getPrice())).checkError(); - // 1.2 微信 API + // 1.2 微信 API } else if (BrokerageWithdrawTypeEnum.WECHAT_API.getType().equals(withdraw.getType())) { // TODO @luchi:这里,要加个转账单号的记录;另外,调用 API 转账,是立马成功,还是有延迟的哈? Long payTransferId = createPayTransfer(withdraw); - // 1.3 剩余类型,都是手动打款,所以不处理 + // 1.3 剩余类型,都是手动打款,所以不处理 } else { // TODO 可优化:未来可以考虑,接入支付宝、银联等 API 转账,实现自动打款 log.info("[auditBrokerageWithdrawSuccess][withdraw({}) 类型({}) 手动打款,无需处理]", withdraw.getId(), withdraw.getType()); @@ -239,11 +240,12 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @Override public List getWithdrawSummaryListByUserId(Collection userIds, - BrokerageWithdrawStatusEnum status) { - if (CollUtil.isEmpty(userIds)) { + Collection statuses) { + if (CollUtil.isEmpty(userIds) || CollUtil.isEmpty(statuses)) { return Collections.emptyList(); } - return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus()); + return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, + convertSet(statuses, BrokerageWithdrawStatusEnum::getStatus)); } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java index 5e436d959..d2509643d 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummary import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; import java.util.Collection; @@ -110,13 +111,14 @@ public interface TradeOrderQueryService { List getExpressTrackList(Long id); /** - * 【会员】在指定秒杀活动下,用户购买的商品数量 + * 【会员】在指定活动下,用户购买的商品数量 * * @param userId 用户编号 * @param activityId 活动编号 - * @return 秒杀商品数量 + * @param type 订单类型 + * @return 活动商品数量 */ - int getSeckillProductCount(Long userId, Long activityId); + int getActivityProductCount(Long userId, Long activityId, TradeOrderTypeEnum type); // =================== Order Item =================== diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java index 5cb73e8d4..a9de40e8d 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java @@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; @@ -174,9 +175,9 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { } @Override - public int getSeckillProductCount(Long userId, Long activityId) { + public int getActivityProductCount(Long userId, Long activityId, TradeOrderTypeEnum type) { // 获得订单列表 - List orders = tradeOrderMapper.selectListByUserIdAndSeckillActivityId(userId, activityId); + List orders = tradeOrderMapper.selectListByUserIdAndActivityId(userId, activityId, type); orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉【已取消】的订单 if (CollUtil.isEmpty(orders)) { return 0; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java index 7bd5e8784..51ffda812 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.trade.service.order.handler; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; @@ -13,6 +15,9 @@ import org.springframework.stereotype.Component; import java.util.List; import java.util.Objects; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS; + /** * 积分商城活动订单的 {@link TradeOrderHandler} 实现类 * @@ -23,6 +28,8 @@ public class TradePointOrderHandler implements TradeOrderHandler { @Resource private PointActivityApi pointActivityApi; + @Resource + private MemberUserApi memberUserApi; @Override public void beforeOrderCreate(TradeOrderDO order, List orderItems) { @@ -31,10 +38,15 @@ public class TradePointOrderHandler implements TradeOrderHandler { } // 明确校验一下 Assert.isTrue(orderItems.size() == 1, "积分商城活动兑换商品兑换时,只允许选择一个商品"); + // 校验用户剩余积分是否足够兑换商品 + MemberUserRespDTO user = memberUserApi.getUser(order.getUserId()).getCheckedData(); + if (user.getPoint() < order.getUsePoint()) { + throw exception(ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS); + } // 扣减积分商城活动的库存 pointActivityApi.updatePointStockDecr(order.getPointActivityId(), - orderItems.get(0).getSkuId(), orderItems.get(0).getCount()).getCheckedData(); + orderItems.get(0).getSkuId(), orderItems.get(0).getCount()).checkError(); // 如果支付金额为 0,则直接设置为已支付 if (Objects.equals(order.getPayPrice(), 0)) { @@ -65,7 +77,7 @@ public class TradePointOrderHandler implements TradeOrderHandler { } // 恢复积分商城活动的库存 pointActivityApi.updatePointStockIncr(order.getPointActivityId(), - orderItem.getSkuId(), orderItem.getCount()).getCheckedData(); + orderItem.getSkuId(), orderItem.getCount()).checkError(); } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java index 0a5498428..01397ddc3 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java @@ -56,36 +56,36 @@ public class TradePointActivityPriceCalculator implements TradePriceCalculator { Assert.isTrue(param.getItems().size() == 1, "积分商城兑换商品时,只允许选择一个商品"); // 2. 校验是否可以参与积分商城活动 TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); - PointValidateJoinRespDTO activity = validateJoinSeckill( + PointValidateJoinRespDTO activity = validateJoinPointActivity( param.getUserId(), param.getPointActivityId(), orderItem.getSkuId(), orderItem.getCount()); - // 3.1 记录优惠明细 - int discountPrice = orderItem.getPayPrice(); // 情况一:单使用积分兑换 + // 3.0 积分兑换前置校验 Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1"); - result.setUsePoint(activity.getPoint() * orderItem.getCount()); - orderItem.setUsePoint(activity.getPoint() * orderItem.getCount()); + // 3.1 记录优惠明细 + int usePoint = activity.getPoint() * orderItem.getCount(); + result.setUsePoint(usePoint); + orderItem.setUsePoint(usePoint); + int discountPrice = orderItem.getPayPrice(); // 情况一:单使用积分兑换 if (activity.getPrice() != null && activity.getPrice() > 0) { // 情况二:积分 + 金额 discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount(); } - // 3.2 记录优惠明细 TradePriceCalculatorHelper.addPromotion(result, orderItem, param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(), StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)), discountPrice); - - // 3.3 更新 SKU 优惠金额 + // 3.2 更新 SKU 优惠金额 orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); TradePriceCalculatorHelper.recountPayPrice(orderItem); TradePriceCalculatorHelper.recountAllPrice(result); } - private PointValidateJoinRespDTO validateJoinSeckill(Long userId, Long activityId, Long skuId, Integer count) { + private PointValidateJoinRespDTO validateJoinPointActivity(Long userId, Long activityId, Long skuId, Integer count) { // 1. 校验是否可以参与积分商城活动 PointValidateJoinRespDTO pointValidateJoinRespDTO = pointActivityApi.validateJoinPointActivity(activityId, skuId, count).getCheckedData(); // 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用 - int activityProductCount = tradeOrderQueryService.getSeckillProductCount(userId, activityId); - if (activityProductCount + count > pointValidateJoinRespDTO.getCount()) { + int pointProductCount = tradeOrderQueryService.getActivityProductCount(userId, activityId, TradeOrderTypeEnum.POINT); + if (pointProductCount + count > pointValidateJoinRespDTO.getCount()) { throw exception(PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT); } return pointValidateJoinRespDTO; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java index 7c5deb627..b43b424fc 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java @@ -5,18 +5,19 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO; import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.annotation.Resource; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import jakarta.annotation.Resource; - import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT; // TODO huihui:单测需要补充 + /** * 秒杀活动的 {@link TradePriceCalculator} 实现类 * @@ -61,7 +62,7 @@ public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator // 1. 校验是否可以参与秒杀 SeckillValidateJoinRespDTO seckillActivity = seckillActivityApi.validateJoinSeckill(activityId, skuId, count).getCheckedData(); // 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用 - int seckillProductCount = tradeOrderQueryService.getSeckillProductCount(userId, activityId); + int seckillProductCount = tradeOrderQueryService.getActivityProductCount(userId, activityId, TradeOrderTypeEnum.SECKILL); if (seckillProductCount + count > seckillActivity.getTotalLimitCount()) { throw exception(PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT); } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java index f7d074092..5ae72ffd9 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java @@ -158,11 +158,11 @@ public class MemberUserServiceImpl implements MemberUserService { // 补充说明:从安全性来说,老手机也校验 oldCode 验证码会更安全。但是由于 uni-app 商城界面暂时没做,所以这里不强制校验 if (StrUtil.isNotEmpty(reqVO.getOldCode())) { smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode()) - .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())).getCheckedData(); + .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())).checkError(); } // 2.2 使用新验证码 smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode()) - .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())).getCheckedData(); + .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())).checkError(); // 3. 更新用户手机 memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); @@ -187,7 +187,7 @@ public class MemberUserServiceImpl implements MemberUserService { MemberUserDO user = validateUserExists(userId); // 校验验证码 smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getCode()) - .setScene(SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene()).setUsedIp(getClientIP())).getCheckedData(); + .setScene(SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene()).setUsedIp(getClientIP())).checkError(); // 更新用户密码 memberUserMapper.updateById(MemberUserDO.builder().id(userId) @@ -201,7 +201,7 @@ public class MemberUserServiceImpl implements MemberUserService { // 使用验证码 smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_RESET_PASSWORD, - getClientIP())).getCheckedData(); + getClientIP())).checkError(); // 更新密码 memberUserMapper.updateById(MemberUserDO.builder().id(user.getId()) diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index c714d86a3..a3cc1cefa 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -43,6 +43,7 @@ public interface ErrorCodeConstants { ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); + ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); // ========== 部门模块 1-002-004-000 ========== ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index 281b766c0..1e7a99a32 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -7,7 +7,14 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginReqVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthResetPasswordReqVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsLoginReqVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsSendReqVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSocialLoginReqVO; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; @@ -23,14 +30,19 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - import jakarta.annotation.Resource; import jakarta.annotation.security.PermitAll; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + import java.util.Collections; import java.util.List; import java.util.Set; @@ -139,6 +151,14 @@ public class AuthController { return success(true); } + @PostMapping("/reset-password") + @PermitAll + @Operation(summary = "重置密码") + public CommonResult resetPassword(@RequestBody @Valid AuthResetPasswordReqVO reqVO) { + authService.resetPassword(reqVO); + return success(true); + } + // ========== 社交登录相关 ========== @GetMapping("/social-auth-redirect") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java index 43b1ea9a4..7325362ae 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java @@ -18,7 +18,7 @@ import org.hibernate.validator.constraints.Length; @NoArgsConstructor @AllArgsConstructor @Builder -public class AuthLoginReqVO { +public class AuthLoginReqVO extends CaptchaVerificationReqVO { @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") @NotEmpty(message = "登录账号不能为空") @@ -31,13 +31,6 @@ public class AuthLoginReqVO { @Length(min = 4, max = 16, message = "密码长度为 4-16 位") private String password; - // ========== 图片验证码相关 ========== - - @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED, - example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==") - @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class) - private String captchaVerification; - // ========== 绑定社交登录时,需要传递如下参数 ========== @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @@ -50,11 +43,6 @@ public class AuthLoginReqVO { @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") private String socialState; - /** - * 开启验证码的 Group - */ - public interface CodeEnableGroup {} - @AssertTrue(message = "授权码不能为空") public boolean isSocialCodeValid() { return socialType == null || StrUtil.isNotEmpty(socialCode); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java index fb88a077e..5f064662a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java @@ -11,7 +11,7 @@ import org.hibernate.validator.constraints.Length; @Schema(description = "管理后台 - Register Request VO") @Data -public class AuthRegisterReqVO { +public class AuthRegisterReqVO extends CaptchaVerificationReqVO { @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") @NotBlank(message = "用户账号不能为空") @@ -28,12 +28,4 @@ public class AuthRegisterReqVO { @NotEmpty(message = "密码不能为空") @Length(min = 4, max = 16, message = "密码长度为 4-16 位") private String password; - - // ========== 图片验证码相关 ========== - - @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED, - example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==") - @NotEmpty(message = "验证码不能为空", groups = AuthLoginReqVO.CodeEnableGroup.class) - private String captchaVerification; - } \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java new file mode 100644 index 000000000..16337cc8c --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +@Schema(description = "管理后台 - 短信重置账号密码 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthResetPasswordReqVO { + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234") + @NotEmpty(message = "密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13312341234") + @NotEmpty(message = "手机号不能为空") + @Mobile + private String mobile; + + @Schema(description = "手机短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotEmpty(message = "手机手机短信验证码不能为空") + private String code; +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java index e975e7d76..2738841b7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java @@ -16,7 +16,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder -public class AuthSmsSendReqVO { +public class AuthSmsSendReqVO extends CaptchaVerificationReqVO { @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") @NotEmpty(message = "手机号不能为空") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java new file mode 100644 index 000000000..b080cb5af --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 验证码 Request VO") +@Data +public class CaptchaVerificationReqVO { + + // ========== 图片验证码相关 ========== + @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED, + example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==") + @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class) + private String captchaVerification; + + /** + * 开启验证码的 Group + */ + public interface CodeEnableGroup { + } +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java index 9e4005c46..485907a11 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java @@ -51,7 +51,7 @@ public interface AdminAuthService { * @param reqVO 登录信息 * @return 登录结果 */ - AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) ; + AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO); /** * 社交快捷登录,使用 code 授权码 @@ -77,4 +77,11 @@ public interface AdminAuthService { */ AuthLoginRespVO register(AuthRegisterReqVO createReqVO); + /** + * 重置密码 + * + * @param reqVO 验证码信息 + */ + void resetPassword(AuthResetPasswordReqVO reqVO); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index 45b9ee1f5..30e5f82cb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; @@ -32,6 +33,7 @@ import jakarta.validation.Validator; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Objects; @@ -111,6 +113,14 @@ public class AdminAuthServiceImpl implements AdminAuthService { @Override public void sendSmsCode(AuthSmsSendReqVO reqVO) { + // 如果是重置密码场景,需要校验图形验证码是否正确 + if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) { + ResponseModel response = doValidateCaptcha(reqVO); + if (!response.isSuccess()) { + throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); + } + } + // 登录场景,验证是否存在 if (userService.getUserByMobile(reqVO.getMobile()) == null) { throw exception(AUTH_MOBILE_NOT_EXISTS); @@ -174,16 +184,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { @VisibleForTesting void validateCaptcha(AuthLoginReqVO reqVO) { - // 如果验证码关闭,则不进行校验 - if (!captchaEnable) { - return; - } + ResponseModel response = doValidateCaptcha(reqVO); // 校验验证码 - ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class); - CaptchaVO captchaVO = new CaptchaVO(); - captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification()); - ResponseModel response = captchaService.verification(captchaVO); - // 验证不通过 if (!response.isSuccess()) { // 创建登录失败日志(验证码不正确) createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR); @@ -191,6 +193,17 @@ public class AdminAuthServiceImpl implements AdminAuthService { } } + private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) { + // 如果验证码关闭,则不进行校验 + if (!captchaEnable) { + return ResponseModel.success(); + } + ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class); + CaptchaVO captchaVO = new CaptchaVO(); + captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification()); + return captchaService.verification(captchaVO); + } + private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { // 插入登陆日志 createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); @@ -261,19 +274,28 @@ public class AdminAuthServiceImpl implements AdminAuthService { @VisibleForTesting void validateCaptcha(AuthRegisterReqVO reqVO) { - // 如果验证码关闭,则不进行校验 - if (!captchaEnable) { - return; - } - // 校验验证码 - ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class); - CaptchaVO captchaVO = new CaptchaVO(); - captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification()); - ResponseModel response = captchaService.verification(captchaVO); + ResponseModel response = doValidateCaptcha(reqVO); // 验证不通过 if (!response.isSuccess()) { throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); } } + @Override + @Transactional(rollbackFor = Exception.class) + public void resetPassword(AuthResetPasswordReqVO reqVO) { + AdminUserDO userByMobile = userService.getUserByMobile(reqVO.getMobile()); + if (userByMobile == null) { + throw exception(USER_MOBILE_NOT_EXISTS); + } + + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO() + .setCode(reqVO.getCode()) + .setMobile(reqVO.getMobile()) + .setScene(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene()) + .setUsedIp(getClientIP()) + ).checkError(); + + userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword()); + } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java index 53d6b7e72..c18ef31c5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java @@ -86,6 +86,7 @@ public class RoleServiceImpl implements RoleService { roleMapper.updateById(updateObj); // 3. 记录操作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class)); LogRecordContext.putVariable("role", role); } @@ -118,7 +119,6 @@ public class RoleServiceImpl implements RoleService { permissionService.processRoleDeleted(id); // 3. 记录操作日志上下文 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class)); LogRecordContext.putVariable("role", role); } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java index 79ed72166..a3e3f14c6 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java @@ -297,7 +297,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { @Test public void testValidateCaptcha_constraintViolationException() { // 准备参数 - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class).setCaptchaVerification(null); + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); + reqVO.setCaptchaVerification(null); // mock 验证码打开 ReflectUtil.setFieldValue(authService, "captchaEnable", true);