code review:优惠劵逻辑

This commit is contained in:
YunaiV 2023-09-28 09:23:42 +08:00
parent 8811ce0200
commit 9e3564bd6a
24 changed files with 110 additions and 65 deletions

View File

@ -44,4 +44,5 @@ public class CouponTemplatePageReqVO extends PageParam {
@Schema(description = "商品范围编号", example = "1") @Schema(description = "商品范围编号", example = "1")
private Long productScopeValue; private Long productScopeValue;
} }

View File

@ -41,13 +41,15 @@ public class AppCouponController {
@Operation(summary = "领取优惠劵") @Operation(summary = "领取优惠劵")
@Parameter(name = "templateId", description = "优惠券模板编号", required = true, example = "1024") @Parameter(name = "templateId", description = "优惠券模板编号", required = true, example = "1024")
public CommonResult<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) { public CommonResult<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) {
Long userId = getLoginUserId();
// 领取 // 领取
Long userId = getLoginUserId();
couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER); couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
// 检查是否可以继续领取 // 检查是否可以继续领取
CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId()); CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId());
boolean canTakeAgain = true; boolean canTakeAgain = true;
if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) { if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) {
// TODO @疯狂要不要搞个 getTakeCount 方法
Integer takeCount = MapUtil.getInt(couponService.getTakeCountMapByTemplateIds( Integer takeCount = MapUtil.getInt(couponService.getTakeCountMapByTemplateIds(
Collections.singleton(reqVO.getTemplateId()), userId), reqVO.getTemplateId(), 0); Collections.singleton(reqVO.getTemplateId()), userId), reqVO.getTemplateId(), 0);
canTakeAgain = takeCount < couponTemplate.getTakeLimitCount(); canTakeAgain = takeCount < couponTemplate.getTakeLimitCount();
@ -58,7 +60,7 @@ public class AppCouponController {
@GetMapping("/match-list") @GetMapping("/match-list")
@Operation(summary = "获得匹配指定商品的优惠劵列表") @Operation(summary = "获得匹配指定商品的优惠劵列表")
public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) { public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
// todo: 惠金额倒序 // todo: 惠金额倒序
return success(CouponConvert.INSTANCE.convertList(couponService.getMatchCouponList(getLoginUserId(), matchReqVO))); return success(CouponConvert.INSTANCE.convertList(couponService.getMatchCouponList(getLoginUserId(), matchReqVO)));
} }

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.controller.app.coupon;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO; import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
@ -45,12 +46,14 @@ public class AppCouponTemplateController {
@Operation(summary = "获得优惠劵模版分页") @Operation(summary = "获得优惠劵模版分页")
public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) { public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {
// 1.1 处理查询条件商品范围编号 // 1.1 处理查询条件商品范围编号
Long productScopeValue = getaProductScopeValue(pageReqVO); Long productScopeValue = getProductScopeValue(pageReqVO);
// 1.2 处理查询条件领取方式=直接领取 // 1.2 处理查询条件领取方式 = 直接领取
List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue()); List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue());
// 2. 分页查询 // 2. 分页查询
PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage( PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(
CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue)); CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));
// 3.1 领取数量 // 3.1 领取数量
Map<Long, Integer> couponTakeCountMap = new HashMap<>(0); Map<Long, Integer> couponTakeCountMap = new HashMap<>(0);
Long userId = getLoginUserId(); Long userId = getLoginUserId();
@ -63,17 +66,24 @@ public class AppCouponTemplateController {
return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, couponTakeCountMap)); return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, couponTakeCountMap));
} }
private Long getaProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) { /**
Long productScopeValue = pageReqVO.getSpuId(); * 获得分页查询的商品范围
if (pageReqVO.getProductScope() == null || Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope())) { *
// 通用券清除商品范围 * @param pageReqVO 分页查询
productScopeValue = null; * @return 商品范围
} else if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) { */
// 品类券查询商品的品类 private Long getProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) {
productScopeValue = Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId())) // 通用券清除商品范围
if (pageReqVO.getProductScope() == null || ObjectUtils.equalsAny(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope(), null)) {
return null;
}
// 品类券查询商品的品类
if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) {
return Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId()))
.map(ProductSpuRespDTO::getCategoryId).orElse(null); .map(ProductSpuRespDTO::getCategoryId).orElse(null);
} }
return productScopeValue; // 商品卷直接返回
return pageReqVO.getSpuId();
} }
} }

View File

@ -20,4 +20,5 @@ public class AppCouponTemplatePageReqVO extends PageParam {
@Schema(description = "商品标号", example = "1") @Schema(description = "商品标号", example = "1")
private Long spuId; private Long spuId;
} }

View File

@ -61,4 +61,5 @@ public interface CouponConvert {
PageResult<AppCouponRespVO> convertAppPage(PageResult<CouponDO> pageResult); PageResult<AppCouponRespVO> convertAppPage(PageResult<CouponDO> pageResult);
List<AppCouponMatchRespVO> convertList(List<CouponDO> list); List<AppCouponMatchRespVO> convertList(List<CouponDO> list);
} }

View File

@ -42,16 +42,16 @@ public interface CouponTemplateConvert {
if (MapUtil.isEmpty(couponTakeCountMap)) { if (MapUtil.isEmpty(couponTakeCountMap)) {
return result; return result;
} }
for (AppCouponTemplateRespVO template : result.getList()) {
for (AppCouponTemplateRespVO vo : result.getList()) {
// 每人领取数量无限制 // 每人领取数量无限制
if (vo.getTakeLimitCount() == -1) { if (template.getTakeLimitCount() == -1) {
vo.setTakeStatus(false); template.setTakeStatus(false);
continue; continue;
} }
// 检查已领取数量是否超过限领数量 // 检查已领取数量是否超过限领数量
vo.setTakeStatus(MapUtil.getInt(couponTakeCountMap, vo.getId(), 0) >= vo.getTakeLimitCount()); template.setTakeStatus(MapUtil.getInt(couponTakeCountMap, template.getId(), 0) >= template.getTakeLimitCount());
} }
return result; return result;
} }
} }

View File

@ -70,6 +70,7 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
); );
} }
// TODO @疯狂这个是不是搞个 Map 就可以呀
default List<CouponTakeCountBO> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) { default List<CouponTakeCountBO> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {
return BeanUtil.copyToList(selectMaps(MPJWrappers.lambdaJoin(CouponDO.class) return BeanUtil.copyToList(selectMaps(MPJWrappers.lambdaJoin(CouponDO.class)
.select(CouponDO::getTemplateId) .select(CouponDO::getTemplateId)
@ -81,18 +82,17 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope( default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) { Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {
Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream() Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
.map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id)) .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
.collect(Collectors.joining(" OR ")); .collect(Collectors.joining(" OR "));
return selectList(new LambdaQueryWrapperX<CouponDO>() return selectList(new LambdaQueryWrapperX<CouponDO>()
.eq(CouponDO::getUserId, userId) .eq(CouponDO::getUserId, userId)
.eq(CouponDO::getStatus, status) .eq(CouponDO::getStatus, status)
.le(CouponDO::getUsePrice, usePrice) .le(CouponDO::getUsePrice, usePrice) // 价格小于等于满足价格使用条件
.and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()) .and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()) // 商品范围一全部
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) // 商品范围二满足指定商品
.apply(productScopeValuesFindInSetFunc.apply(spuIds))) .apply(productScopeValuesFindInSetFunc.apply(spuIds)))
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) // 商品范围三满足指定分类
.apply(productScopeValuesFindInSetFunc.apply(categoryIds))))); .apply(productScopeValuesFindInSetFunc.apply(categoryIds)))));
} }
@ -102,4 +102,5 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
.le(CouponDO::getValidEndTime, validEndTime) .le(CouponDO::getValidEndTime, validEndTime)
); );
} }
} }

View File

@ -137,7 +137,8 @@ public interface CouponService {
* @return 领取优惠券的数量 * @return 领取优惠券的数量
*/ */
default Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) { default Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {
return convertMap(getTakeCountListByTemplateIds(templateIds, userId), CouponTakeCountBO::getTemplateId, CouponTakeCountBO::getCount); return convertMap(getTakeCountListByTemplateIds(templateIds, userId),
CouponTakeCountBO::getTemplateId, CouponTakeCountBO::getCount);
} }
/** /**

View File

@ -222,13 +222,12 @@ public class CouponServiceImpl implements CouponService {
private boolean expireCoupon(CouponDO coupon) { private boolean expireCoupon(CouponDO coupon) {
// 更新记录状态 // 更新记录状态
CouponDO updateObj = new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus()); int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(),
int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(), updateObj); new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus()));
if (updateRows == 0) { if (updateRows == 0) {
log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId()); log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId());
return false; return false;
} }
log.info("[expireCoupon][coupon({}) 更新为已过期成功]", coupon.getId()); log.info("[expireCoupon][coupon({}) 更新为已过期成功]", coupon.getId());
return true; return true;
} }

View File

@ -9,6 +9,7 @@ import lombok.Data;
*/ */
@Data @Data
public class CouponTakeCountBO { public class CouponTakeCountBO {
/** /**
* 优惠劵模板编号 * 优惠劵模板编号
*/ */
@ -17,4 +18,5 @@ public class CouponTakeCountBO {
* 领取数量 * 领取数量
*/ */
private Integer count; private Integer count;
} }

View File

@ -42,6 +42,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
@Validated @Validated
@Slf4j @Slf4j
public class AppBrokerageUserController { public class AppBrokerageUserController {
@Resource @Resource
private BrokerageUserService brokerageUserService; private BrokerageUserService brokerageUserService;
@Resource @Resource
@ -69,8 +70,7 @@ public class AppBrokerageUserController {
@Operation(summary = "绑定推广员") @Operation(summary = "绑定推广员")
@PreAuthenticated @PreAuthenticated
public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) { public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
MemberUserRespDTO user = memberUserApi.getUser(getLoginUserId()); return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId()));
return success(brokerageUserService.bindBrokerageUser(user.getId(), reqVO.getBindUserId(), user.getCreateTime()));
} }
@GetMapping("/get-summary") @GetMapping("/get-summary")
@ -86,7 +86,7 @@ public class AppBrokerageUserController {
Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(brokerageUser.getId(), Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(brokerageUser.getId(),
BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime); BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime);
// 统计用户提现的佣金 // 统计用户提现的佣金
Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryByUserId(Collections.singleton(brokerageUser.getId()), Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryListByUserId(Collections.singleton(brokerageUser.getId()),
BrokerageWithdrawStatusEnum.AUDIT_SUCCESS).stream() BrokerageWithdrawStatusEnum.AUDIT_SUCCESS).stream()
.findFirst().map(UserWithdrawSummaryBO::getPrice).orElse(0); .findFirst().map(UserWithdrawSummaryBO::getPrice).orElse(0);
// 统计分销用户数量一级 // 统计分销用户数量一级

View File

@ -68,6 +68,7 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
.eq(BrokerageRecordDO::getStatus, status) .eq(BrokerageRecordDO::getStatus, status)
.groupBy(BrokerageRecordDO::getUserId)); .groupBy(BrokerageRecordDO::getUserId));
return BeanUtil.copyToList(list, UserBrokerageSummaryBO.class); return BeanUtil.copyToList(list, UserBrokerageSummaryBO.class);
// selectJoinList有BUG会与租户插件冲突解析SQL时发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW
// return selectJoinList(UserBrokerageSummaryBO.class, MPJWrappers.lambdaJoin(BrokerageRecordDO.class) // return selectJoinList(UserBrokerageSummaryBO.class, MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
// .select(BrokerageRecordDO::getUserId) // .select(BrokerageRecordDO::getUserId)
// .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount) // .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)

View File

@ -78,25 +78,25 @@ public interface BrokerageRecordService {
int unfreezeRecord(); int unfreezeRecord();
/** /**
* 汇总用户佣金 * 按照 userId汇总每个用户佣金
* *
* @param userIds 用户编号 * @param userIds 用户编号
* @param bizType 业务类型 * @param bizType 业务类型
* @param status 佣金状态 * @param status 佣金状态
* @return 用户佣金汇总 * @return 用户佣金汇总 List
*/ */
List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status); List<UserBrokerageSummaryBO> getUserBrokerageSummaryListByUserId(Collection<Long> userIds, Integer bizType, Integer status);
/** /**
* 汇总用户佣金 * 按照 userId汇总每个用户佣金
* *
* @param userIds 用户编号 * @param userIds 用户编号
* @param bizType 业务类型 * @param bizType 业务类型
* @param status 佣金状态 * @param status 佣金状态
* @return 用户佣金汇总 * @return 用户佣金汇总 Map
*/ */
default Map<Long, UserBrokerageSummaryBO> getUserBrokerageSummaryMapByUserId(Collection<Long> userIds, Integer bizType, Integer status) { default Map<Long, UserBrokerageSummaryBO> getUserBrokerageSummaryMapByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
return convertMap(getUserBrokerageSummaryByUserId(userIds, bizType, status), UserBrokerageSummaryBO::getUserId); return convertMap(getUserBrokerageSummaryListByUserId(userIds, bizType, status), UserBrokerageSummaryBO::getUserId);
} }
/** /**

View File

@ -232,7 +232,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
} }
@Override @Override
public List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status) { public List<UserBrokerageSummaryBO> getUserBrokerageSummaryListByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status); return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status);
} }

View File

@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokera
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -107,10 +106,9 @@ public interface BrokerageUserService {
* *
* @param userId 用户编号 * @param userId 用户编号
* @param bindUserId 推广员编号 * @param bindUserId 推广员编号
* @param registerTime 用户注册时间
* @return 是否绑定 * @return 是否绑定
*/ */
boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime); boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId);
/** /**
* 获取用户是否有分销资格 * 获取用户是否有分销资格

View File

@ -7,6 +7,8 @@ import cn.hutool.core.util.BooleanUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
@ -48,6 +50,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
@Resource @Resource
private TradeConfigService tradeConfigService; private TradeConfigService tradeConfigService;
@Resource
private MemberUserApi memberUserApi;
@Override @Override
public BrokerageUserDO getBrokerageUser(Long id) { public BrokerageUserDO getBrokerageUser(Long id) {
return brokerageUserMapper.selectById(id); return brokerageUserMapper.selectById(id);
@ -155,7 +160,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
@Override @Override
public boolean bindBrokerageUser(Long userId, Long bindUserId, LocalDateTime registerTime) { public boolean bindBrokerageUser(Long userId, Long bindUserId) {
// 1. 获得分销用户 // 1. 获得分销用户
boolean isNewBrokerageUser = false; boolean isNewBrokerageUser = false;
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId); BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
@ -165,7 +170,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
// 2.1 校验是否能绑定用户 // 2.1 校验是否能绑定用户
boolean validated = isUserCanBind(brokerageUser, registerTime); boolean validated = isUserCanBind(brokerageUser);
if (!validated) { if (!validated) {
return false; return false;
} }
@ -223,7 +228,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal()); return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
} }
private boolean isUserCanBind(BrokerageUserDO user, LocalDateTime registerTime) { private boolean isUserCanBind(BrokerageUserDO user) {
// 校验分销功能是否启用 // 校验分销功能是否启用
TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) { if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
@ -237,9 +242,8 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
// 校验分销关系绑定模式 // 校验分销关系绑定模式
if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) { if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) {
// 判断是否为新用户注册时间在30秒内的都算新用户 // 判断是否为新用户注册时间在 30 秒内的都算新用户
boolean isNotNewUser = LocalDateTimeUtils.beforeNow(registerTime.plusSeconds(30)); if (!isNewRegisterUser(user.getId())) {
if (isNotNewUser) {
throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定 throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定
} }
} else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) { } else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) {
@ -247,14 +251,29 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
throw exception(BROKERAGE_BIND_OVERRIDE); // 已绑定了推广人 throw exception(BROKERAGE_BIND_OVERRIDE); // 已绑定了推广人
} }
} }
return true; return true;
} }
/**
* 判断是否为新用户
*
* 标准注册时间在 30 秒内的都算新用户
*
* 疑问为什么通过这样的方式实现
* 回答因为注册在 member 模块希望它和 trade 模块解耦所以只能用这种约定的逻辑
*
* @param userId 用户编号
* @return 是否新用户
*/
private boolean isNewRegisterUser(Long userId) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
return user != null && LocalDateTimeUtils.beforeNow(user.getCreateTime().plusSeconds(30));
}
private void validateCanBindUser(BrokerageUserDO user, Long bindUserId) { private void validateCanBindUser(BrokerageUserDO user, Long bindUserId) {
// 校验要绑定的用户有无推广资格 // 校验要绑定的用户有无推广资格
BrokerageUserDO bindUser = brokerageUserMapper.selectById(bindUserId); BrokerageUserDO bindUser = brokerageUserMapper.selectById(bindUserId);
if (bindUser == null || !BooleanUtil.isTrue(bindUser.getBrokerageEnabled())) { if (bindUser == null || BooleanUtil.isFalse(bindUser.getBrokerageEnabled())) {
throw exception(BROKERAGE_BIND_USER_NOT_ENABLED); throw exception(BROKERAGE_BIND_USER_NOT_ENABLED);
} }

View File

@ -56,22 +56,23 @@ public interface BrokerageWithdrawService {
Long createBrokerageWithdraw(AppBrokerageWithdrawCreateReqVO createReqVO, Long userId); Long createBrokerageWithdraw(AppBrokerageWithdrawCreateReqVO createReqVO, Long userId);
/** /**
* 汇总用户提现 * 按照 userId汇总每个用户提现
* *
* @param userIds 用户编号 * @param userIds 用户编号
* @param status 提现状态 * @param status 提现状态
* @return 用户提现汇总 * @return 用户提现汇总 List
*/ */
List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status); List<UserWithdrawSummaryBO> getWithdrawSummaryListByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status);
/** /**
* 汇总用户提现 * 按照 userId汇总每个用户提现
* *
* @param userIds 用户编号 * @param userIds 用户编号
* @param status 提现状态 * @param status 提现状态
* @return 用户提现汇总 * @return 用户提现汇总 Map
*/ */
default Map<Long, UserWithdrawSummaryBO> getWithdrawSummaryMapByUserId(Set<Long> userIds, BrokerageWithdrawStatusEnum status) { default Map<Long, UserWithdrawSummaryBO> getWithdrawSummaryMapByUserId(Set<Long> userIds, BrokerageWithdrawStatusEnum status) {
return convertMap(getWithdrawSummaryByUserId(userIds, status), UserWithdrawSummaryBO::getUserId); return convertMap(getWithdrawSummaryListByUserId(userIds, status), UserWithdrawSummaryBO::getUserId);
} }
} }

View File

@ -144,7 +144,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
} }
@Override @Override
public List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status) { public List<UserWithdrawSummaryBO> getWithdrawSummaryListByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status) {
return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus()); return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus());
} }

View File

@ -13,12 +13,13 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class UserBrokerageSummaryBO { public class UserBrokerageSummaryBO {
/** /**
* 用户编号 * 用户编号
*/ */
private Long userId; private Long userId;
/** /**
* 佣金数量 * 推广数量
*/ */
private Integer count; private Integer count;
/** /**

View File

@ -125,7 +125,6 @@ public interface TradeOrderUpdateService {
*/ */
void updateOrderItemWhenAfterSaleCreate(@NotNull Long id, @NotNull Long afterSaleId); void updateOrderItemWhenAfterSaleCreate(@NotNull Long id, @NotNull Long afterSaleId);
/** /**
* 当售后完成后更新交易订单项的售后状态 * 当售后完成后更新交易订单项的售后状态
* *

View File

@ -753,6 +753,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override @Override
public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) { public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
// TODO @疯狂这个可以直接在接口上 @Null 参数校验
if (afterSaleId == null) { if (afterSaleId == null) {
throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id)); throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id));
} }
@ -765,6 +766,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) { public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
// TODO @疯狂这个可以直接在接口上 @Null 参数校验
if (refundPrice == null) { if (refundPrice == null) {
throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id)); throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
} }
@ -787,15 +789,17 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
} }
// TODO 芋艿这块扣减规则需要在考虑下 // TODO 芋艿这块扣减规则需要在考虑下
// 3.1 回滚数据增加 SKU 库存 // 3. 回滚数据增加 SKU 库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem))); productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
// 3.2 回滚数据扣减用户积分赠送的
// 4.1 回滚数据扣减用户积分赠送的
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId()); reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
// 3.3 回滚数据增加用户积分返还抵扣 // 4.2 回滚数据增加用户积分返还抵扣
addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId()); addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId());
// 3.4 回滚数据扣减用户经验 // 4.3 回滚数据扣减用户经验
getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, orderItem.getAfterSaleId()); getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, orderItem.getAfterSaleId());
// 3.5 回滚数据更新分佣记录为已失效
// 5. 回滚数据更新分佣记录为已失效
getSelf().cancelBrokerageAsync(order.getUserId(), id); getSelf().cancelBrokerageAsync(order.getUserId(), id);
} }
@ -826,7 +830,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
couponApi.returnUsedCoupon(order.getCouponId()); couponApi.returnUsedCoupon(order.getCouponId());
} }
/** /**
* 判断指定订单的所有订单项是不是都售后成功 * 判断指定订单的所有订单项是不是都售后成功
* *

View File

@ -87,10 +87,11 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
if (config.getTradeDeductMaxPrice() != null && config.getTradeDeductMaxPrice() > 0) { if (config.getTradeDeductMaxPrice() != null && config.getTradeDeductMaxPrice() > 0) {
usePoint = Math.min(usePoint, config.getTradeDeductMaxPrice()); usePoint = Math.min(usePoint, config.getTradeDeductMaxPrice());
} }
// TODO @疯狂这里应该是抵扣到只剩下 0.01
// 积分优惠金额 // 积分优惠金额
int pointPrice = usePoint * config.getTradeDeductUnitPrice(); int pointPrice = usePoint * config.getTradeDeductUnitPrice();
if (result.getPrice().getPayPrice() <= pointPrice) { if (result.getPrice().getPayPrice() <= pointPrice) {
// 禁止0元购 // 禁止 0 元购
throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL); throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
} }
// // 允许0 元购!!!用户积分比较多时积分可以抵扣的金额要大于支付金额这时需要根据支付金额反推使用多少积分 // // 允许0 元购!!!用户积分比较多时积分可以抵扣的金额要大于支付金额这时需要根据支付金额反推使用多少积分

View File

@ -41,4 +41,8 @@ public class MemberUserPageReqVO extends PageParam {
@Schema(description = "用户分组编号", example = "1") @Schema(description = "用户分组编号", example = "1")
private Long groupId; private Long groupId;
// TODO 芋艿注册用户类型
// TODO 芋艿登录用户类型
} }

View File

@ -190,7 +190,7 @@ public class MemberUserServiceImpl implements MemberUserService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateUser(MemberUserUpdateReqVO updateReqVO) { public void updateUser(MemberUserUpdateReqVO updateReqVO) {
// 校验存在 // 校验存在
MemberUserDO user = validateUserExists(updateReqVO.getId()); validateUserExists(updateReqVO.getId());
// 校验手机唯一 // 校验手机唯一
validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile()); validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());