Merge remote-tracking branch 'origin/feature/mall_product' into brokerate

# Conflicts:
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/record/BrokerageRecordConvert.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/user/BrokerageUserConvert.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/record/BrokerageRecordServiceImpl.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/user/BrokerageUserServiceImpl.java
plp
owen 3 years ago
commit cddeb37289

@ -8,8 +8,8 @@ CREATE TABLE `pay_wallet`
`user_id` bigint NOT NULL COMMENT '', `user_id` bigint NOT NULL COMMENT '',
`user_type` tinyint NOT NULL DEFAULT 0 COMMENT '', `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '',
`balance` int NOT NULL DEFAULT 0 COMMENT '', `balance` int NOT NULL DEFAULT 0 COMMENT '',
`total_expense` bigint NOT NULL DEFAULT 0 COMMENT '', `total_expense` int NOT NULL DEFAULT 0 COMMENT '',
`total_recharge` bigint NOT NULL DEFAULT 0 COMMENT '', `total_recharge` int NOT NULL DEFAULT 0 COMMENT '',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '', `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '', `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '',

@ -41,7 +41,7 @@ public class CommonResult<T> implements Serializable {
* A CommonResult B * A CommonResult B
* *
* @param result result * @param result result
* @param <T> * @param <T>
* @return CommonResult * @return CommonResult
*/ */
public static <T> CommonResult<T> error(CommonResult<?> result) { public static <T> CommonResult<T> error(CommonResult<?> result) {

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.promotion.api.bargain;
/**
* Api
*
* @author HUIHUI
*/
public interface BargainActivityApi {
/**
*
*
* @param activityId
* @param count
*/
void updateBargainActivityStock(Long activityId, Integer count);
}

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.promotion.api.bargain.dto;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
// TODO @芋艿:这块要在看看 // TODO @芋艿:这块要在看看
@ -40,17 +39,7 @@ public class BargainRecordCreateReqDTO {
*/ */
@NotNull(message = "订单编号不能为空") @NotNull(message = "订单编号不能为空")
private Long orderId; private Long orderId;
// TODO @puhui999spuName、picUrl、 之类字段不用传递;
/**
*
*/
@NotEmpty(message = "商品名字不能为空")
private String spuName;
/**
*
*/
@NotEmpty(message = "商品图片不能为空")
private String picUrl;
/** /**
* *
*/ */
@ -61,17 +50,7 @@ public class BargainRecordCreateReqDTO {
*/ */
@NotNull(message = "商品原价不能为空") @NotNull(message = "商品原价不能为空")
private Integer price; private Integer price;
// TODO @puhui999nickname、avatar 不用传递,去查询;
/**
*
*/
@NotEmpty(message = "用户昵称不能为空")
private String nickname;
/**
*
*/
@NotEmpty(message = "用户头像不能为空")
private String avatar;
/** /**
* *
*/ */

@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import javax.validation.Valid; import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
// TODO @芋艿:后面也再撸撸这几个接口 // TODO @芋艿:后面也再撸撸这几个接口
@ -51,13 +51,29 @@ public interface CombinationRecordApi {
*/ */
void validateCombinationLimitCount(Long activityId, Integer count, Integer sumCount); void validateCombinationLimitCount(Long activityId, Integer count, Integer sumCount);
// TODO @puhui999是不是搞成具体的方法拼团成功拼团失败这种方法 /**
*
*
* @param userId
* @param orderId
*/
void updateRecordStatusToSuccess(Long userId, Long orderId);
/** /**
* *
* *
* @param reqDTO DTO * @param userId
* @param orderId
*/
void updateRecordStatusToFailed(Long userId, Long orderId);
/**
*
*
* @param userId
* @param orderId
* @param startTime
*/ */
void updateCombinationRecordStatus(CombinationRecordUpdateStatusReqDTO reqDTO); void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime);
} }

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.promotion.api.combination.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* Request DTO
*
* @author HUIHUI
*/
@Data
public class CombinationRecordUpdateStatusReqDTO {
/**
*
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
*
*/
@NotNull(message = "订单编号不能为空")
private Long orderId;
/**
*
*/
@NotNull(message = "开团状态不能为空")
private Integer status;
/**
*
*/
private LocalDateTime startTime;
}

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.promotion.api.seckill;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
/**
* API
*
* @author HUIHUI
*/
public interface SeckillActivityApi {
/**
*
*
* @param updateStockReqDTO
*/
void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO);
}

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.promotion.api.seckill.dto;
import lombok.Data;
import java.util.List;
/**
* request DTO
*
* @author HUIHUI
*/
@Data
public class SeckillActivityUpdateStockReqDTO {
// TODO @puhui999参数校验
// TODO @puhui999秒杀的话一次只能购买一种商品哈不能多个哈
/**
*
*/
private Long activityId;
/**
*
*/
private Integer count;
/**
*
*/
private List<Item> items;
@Data
public static class Item {
/**
* SPU
*/
private Long spuId;
/**
* SKU
*/
private Long skuId;
/**
*
*/
private Integer count;
}
}

@ -51,10 +51,11 @@ public interface ErrorCodeConstants {
// ========== 秒杀活动 1013008000 ========== // ========== 秒杀活动 1013008000 ==========
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在"); ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在");
ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013008002, "存在商品参加了其它秒杀活动"); ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013008002, "存在商品参加了其它秒杀活动,秒杀时段冲突");
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改"); ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除"); ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除");
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭"); ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭");
ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "更新秒杀活动库存失败,原因秒杀库存不足");
// ========== 秒杀时段 1013009000 ========== // ========== 秒杀时段 1013009000 ==========
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在"); ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.promotion.api.bargain;
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.BARGAIN_ACTIVITY_NOT_EXISTS;
/**
* Api
*
* @author HUIHUI
*/
@Service
public class BargainActivityApiImpl implements BargainActivityApi {
@Resource
private BargainActivityService bargainActivityService;
@Override
public void updateBargainActivityStock(Long activityId, Integer count) {
// TODO @puhui999可以整个实现到 bargainActivityService 中
// 查询砍价活动
BargainActivityDO activity = bargainActivityService.getBargainActivity(activityId);
if (activity == null) {
throw exception(BARGAIN_ACTIVITY_NOT_EXISTS);
}
// 更新砍价库存
// TODO @puhui999考虑下并发更新问题
BargainActivityUpdateReqVO reqVO = new BargainActivityUpdateReqVO();
reqVO.setId(activityId);
reqVO.setStock(activity.getStock() - count);
bargainActivityService.updateBargainActivity(reqVO);
}
}

@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -43,12 +43,19 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
} }
@Override @Override
public void updateCombinationRecordStatus(CombinationRecordUpdateStatusReqDTO reqDTO) { public void updateRecordStatusToSuccess(Long userId, Long orderId) {
if (null == reqDTO.getStartTime()) { recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.SUCCESS.getStatus(), userId, orderId);
recordService.updateCombinationRecordStatusByUserIdAndOrderId(reqDTO); }
} else {
recordService.updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(reqDTO); @Override
} public void updateRecordStatusToFailed(Long userId, Long orderId) {
recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.FAILED.getStatus(), userId, orderId);
}
@Override
public void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime) {
recordService.updateRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordStatusEnum.IN_PROGRESS.getStatus(),
userId, orderId, startTime);
} }
} }

@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.promotion.api.seckill;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.SECKILL_ACTIVITY_UPDATE_STOCK_FAIL;
/**
* Api
*
* @author HUIHUI
*/
@Service
public class SeckillActivityApiImpl implements SeckillActivityApi {
@Resource
private SeckillActivityService activityService;
// TODO @puhui建议这块弄到 activityService 实现哈;
// TODO @puhui这个方法要考虑事务性
@Override
public void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO) {
// TODO @puhui999长方法最好有 1.1 1.2 2.1 这种步骤哈;
SeckillActivityDO seckillActivity = activityService.getSeckillActivity(updateStockReqDTO.getActivityId());
if (seckillActivity.getStock() < updateStockReqDTO.getCount()) {
throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
}
// 获取活动商品
// TODO @puhui999在一个方法里dos 和 dolist 最好保持一致,要么用 s要么用 list 哈;
List<SeckillProductDO> productDOs = activityService.getSeckillProductListByActivityId(updateStockReqDTO.getActivityId());
// TODO @puhui999这个是不是搞成 CollectionUtils.convertMultiMap()
List<SeckillActivityUpdateStockReqDTO.Item> items = updateStockReqDTO.getItems();
Map<Long, List<Long>> map = new HashMap<>();
items.forEach(item -> {
if (map.containsKey(item.getSpuId())) {
List<Long> skuIds = map.get(item.getSpuId());
skuIds.add(item.getSkuId());
map.put(item.getSpuId(), skuIds);
} else {
List<Long> list = new ArrayList<>();
list.add(item.getSkuId());
map.put(item.getSpuId(), list);
}
});
// 过滤出购买的商品
// TODO @puhui999productDOList 可以简化成 productList一般来说do 之类不用带着哈,在变量里;
List<SeckillProductDO> productDOList = CollectionUtils.filterList(productDOs, item -> map.get(item.getSpuId()).contains(item.getSkuId()));
Map<Long, SeckillActivityUpdateStockReqDTO.Item> productDOMap = CollectionUtils.convertMap(items, SeckillActivityUpdateStockReqDTO.Item::getSkuId, p -> p);
// 检查活动商品库存是否充足
// TODO @puhui999避免 b 这种无业务含义的变量;
boolean b = CollectionUtils.anyMatch(productDOList, item -> {
SeckillActivityUpdateStockReqDTO.Item item1 = productDOMap.get(item.getSkuId());
return (item.getStock() < item1.getCount()) || (item.getStock() - item1.getCount()) < 0;
});
if (b) {
throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
}
// TODO @puhui999类似 doList应该和下面的 update 逻辑粘的更紧密一点so 在空行的时候,应该挪到 74 之后里去;甚至更合理,应该是 79 之后;说白了,逻辑要分块,每个模块涉及的代码要紧密在一起;
List<SeckillProductDO> doList = CollectionUtils.convertList(productDOList, item -> {
item.setStock(item.getStock() - productDOMap.get(item.getSkuId()).getCount());
return item;
});
// 更新活动库存
// TODO @puhui999考虑下并发更新
seckillActivity.setStock(seckillActivity.getStock() + updateStockReqDTO.getCount());
seckillActivity.setTotalStock(seckillActivity.getTotalStock() - updateStockReqDTO.getCount());
activityService.updateSeckillActivity(seckillActivity);
// 更新活动商品库存
activityService.updateSeckillActivityProductList(doList);
}
}

@ -9,6 +9,7 @@ import lombok.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
// TODO 芋艿:把字段的顺序,和 do 顺序对齐下
/** /**
* DO * DO
* *
@ -27,34 +28,28 @@ import java.time.LocalDateTime;
@AllArgsConstructor @AllArgsConstructor
public class CombinationRecordDO extends BaseDO { public class CombinationRecordDO extends BaseDO {
/**
*
*/
@TableId @TableId
private Long id; private Long id;
/** /**
* *
*
* {@link CombinationActivityDO#getId()}
*/ */
private Long activityId; private Long activityId;
/** /**
* SPU *
*/ *
private Long spuId; * {@link CombinationProductDO#getCombinationPrice()}
/**
* SKU
*/
private Long skuId;
/**
*
*/
private Long userId;
/**
*
*/ */
private Long orderId; private Integer combinationPrice;
/** /**
* * SPU
*
* {@link CombinationRecordDO#getId()}
*/ */
private Long headId; private Long spuId;
/** /**
* *
*/ */
@ -64,9 +59,14 @@ public class CombinationRecordDO extends BaseDO {
*/ */
private String picUrl; private String picUrl;
/** /**
* * SKU
*/ */
private Integer combinationPrice; private Long skuId;
/**
*
*/
private Long userId;
/** /**
* *
*/ */
@ -75,22 +75,44 @@ public class CombinationRecordDO extends BaseDO {
* *
*/ */
private String avatar; private String avatar;
/**
*
*
* {@link CombinationRecordDO#getId()}
*/
private Long headId;
/** /**
* *
* *
* {@link CombinationRecordStatusEnum} * {@link CombinationRecordStatusEnum}
*/ */
private Integer status; private Integer status;
/**
*
*/
private Long orderId;
/**
*
*
* {@link CombinationActivityDO#getUserSize()}
*/
private Integer userSize;
/**
*
*/
private Integer userCount;
/** /**
* *
*/ */
private Boolean virtualGroup; private Boolean virtualGroup;
/** /**
* *
* *
* {@link CombinationActivityDO#getLimitDuration()} * {@link CombinationRecordDO#getStartTime()} + {@link CombinationActivityDO#getLimitDuration()}
*/ */
private Integer expireTime; private LocalDateTime expireTime;
/** /**
* () * ()
*/ */
@ -99,15 +121,5 @@ public class CombinationRecordDO extends BaseDO {
* / * /
*/ */
private LocalDateTime endTime; private LocalDateTime endTime;
/**
*
*
* {@link CombinationActivityDO#getUserSize()}
*/
private Integer userSize;
/**
*
*/
private Integer userCount;
} }

@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.promotion.service.combination; package cn.iocoder.yudao.module.promotion.service.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -16,9 +16,11 @@ public interface CombinationRecordService {
/** /**
* *
* *
* @param reqDTO DTO * @param status
* @param userId
* @param orderId
*/ */
void updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO); void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId);
/** /**
* *
@ -30,9 +32,12 @@ public interface CombinationRecordService {
/** /**
* *
* *
* @param reqDTO DTO * @param status
* @param userId
* @param orderId
* @param startTime
*/ */
void updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO); void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime);
/** /**
* *

@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.promotion.service.combination;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
@ -21,6 +20,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
// TODO 芋艿:等拼团记录做完,完整 review 下 // TODO 芋艿:等拼团记录做完,完整 review 下
/** /**
* Service * Service
* *
@ -38,27 +38,27 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) { public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) {
// 校验拼团是否存在 // 校验拼团是否存在
CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId()); CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
// 更新状态 // 更新状态
recordDO.setStatus(reqDTO.getStatus()); recordDO.setStatus(status);
recordMapper.updateById(recordDO); recordMapper.updateById(recordDO);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) { public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime) {
CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId()); CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
// 更新状态 // 更新状态
recordDO.setStatus(reqDTO.getStatus()); recordDO.setStatus(status);
// 更新开始时间 // 更新开始时间
recordDO.setStartTime(reqDTO.getStartTime()); recordDO.setStartTime(startTime);
recordMapper.updateById(recordDO); recordMapper.updateById(recordDO);
// 更新拼团参入人数 // 更新拼团参入人数
List<CombinationRecordDO> recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), reqDTO.getStatus()); List<CombinationRecordDO> recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), status);
if (CollUtil.isNotEmpty(recordDOs)) { if (CollUtil.isNotEmpty(recordDOs)) {
recordDOs.forEach(item -> { recordDOs.forEach(item -> {
item.setUserCount(recordDOs.size()); item.setUserCount(recordDOs.size());
@ -115,8 +115,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
// 2. 创建拼团记录 // 2. 创建拼团记录
CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO); CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO);
record.setVirtualGroup(false); record.setVirtualGroup(false);
// TODO @puhui999过期时间应该是 Date 哈; record.setExpireTime(record.getStartTime().plusHours(activity.getLimitDuration()));
record.setExpireTime(activity.getLimitDuration());
record.setUserSize(activity.getUserSize()); record.setUserSize(activity.getUserSize());
recordMapper.insert(record); recordMapper.insert(record);
} }

@ -33,6 +33,20 @@ public interface SeckillActivityService {
*/ */
void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO); void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO);
/**
*
*
* @param activityDO
*/
void updateSeckillActivity(SeckillActivityDO activityDO);
/**
*
*
* @param productList
*/
void updateSeckillActivityProductList(List<SeckillProductDO> productList);
/** /**
* *
* *

@ -79,8 +79,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
* 1. * 1.
* 2. * 2.
* *
* @param configIds * @param configIds
* @param spuId SPU * @param spuId SPU
* @param activityId * @param activityId
*/ */
private void validateProductConflict(List<Long> configIds, Long spuId, Long activityId) { private void validateProductConflict(List<Long> configIds, Long spuId, Long activityId) {
@ -92,15 +92,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
if (activityId != null) { // 排除自己 if (activityId != null) { // 排除自己
activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId)); activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));
} }
// TODO @puhui999一个 spu参与两个活动应该没关系关键是活动时间不充能重叠 // 2.2 过滤出所有 configIds 有交集的活动,判断是否存在重叠
// 2.2 过滤出所有 spuId 有交集的活动,判断是否存在重叠 List<SeckillActivityDO> conflictActivityList = filterList(activityList, s -> containsAny(s.getConfigIds(), configIds));
List<SeckillActivityDO> activityDOs1 = filterList(activityList, s -> ObjectUtil.equal(s.getSpuId(), spuId)); if (isNotEmpty(conflictActivityList)) {
if (isNotEmpty(activityDOs1)) {
throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
}
// 2.3 过滤出所有 configIds 有交集的活动,判断是否存在重叠
List<SeckillActivityDO> activityDOs2 = filterList(activityList, s -> containsAny(s.getConfigIds(), configIds));
if (isNotEmpty(activityDOs2)) {
throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS); throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
} }
} }
@ -108,7 +102,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
/** /**
* *
* *
* @param spuId SPU * @param spuId SPU
* @param products * @param products
*/ */
private void validateProductExists(Long spuId, List<SeckillProductBaseVO> products) { private void validateProductExists(Long spuId, List<SeckillProductBaseVO> products) {
@ -150,11 +144,21 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
updateSeckillProduct(updateObj, updateReqVO.getProducts()); updateSeckillProduct(updateObj, updateReqVO.getProducts());
} }
@Override
public void updateSeckillActivity(SeckillActivityDO activityDO) {
seckillActivityMapper.updateById(activityDO);
}
@Override
public void updateSeckillActivityProductList(List<SeckillProductDO> productList) {
seckillProductMapper.updateBatch(productList);
}
/** /**
* *
* *
* @param activity * @param activity
* @param products * @param products
*/ */
private void updateSeckillProduct(SeckillActivityDO activity, List<SeckillProductBaseVO> products) { private void updateSeckillProduct(SeckillActivityDO activity, List<SeckillProductBaseVO> products) {
// 第一步,对比新老数据,获得添加、修改、删除的列表 // 第一步,对比新老数据,获得添加、修改、删除的列表

@ -1,18 +1,9 @@
package cn.iocoder.yudao.module.promotion.util; package cn.iocoder.yudao.module.promotion.util;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
/** /**
* *
@ -31,21 +22,4 @@ public class PromotionUtils {
return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus(); return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus();
} }
/**
* sku
*
* @param skus skus
* @param products
* @param func skuId
*/
public static <T> void validateProductSkuAllExists(List<ProductSkuRespDTO> skus, List<T> products, Function<T, Long> func) {
// 校验 sku 个数是否一致
Set<Long> skuIdsSet = CollectionUtils.convertSet(products, func);
Set<Long> skuIdsSet1 = CollectionUtils.convertSet(skus, ProductSkuRespDTO::getId);
// 校验 skuId 是否存在
if (anyMatch(skuIdsSet, s -> !skuIdsSet1.contains(s))) {
throw exception(SKU_NOT_EXISTS);
}
}
} }

@ -86,6 +86,11 @@
<artifactId>yudao-spring-boot-starter-mybatis</artifactId> <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-redis</artifactId>
</dependency>
<!-- Test 测试相关 --> <!-- Test 测试相关 -->
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>

@ -20,13 +20,14 @@ public class BrokerageApiImpl implements BrokerageApi {
@Resource @Resource
private BrokerageUserService brokerageUserService; private BrokerageUserService brokerageUserService;
@Override
public BrokerageUserDTO getBrokerageUser(Long userId) { public BrokerageUserDTO getBrokerageUser(Long userId) {
return BrokerageUserConvert.INSTANCE.convertDTO(brokerageUserService.getBrokerageUser(userId)); return BrokerageUserConvert.INSTANCE.convertDTO(brokerageUserService.getBrokerageUser(userId));
} }
@Override @Override
public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) { public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) {
return brokerageUserService.bindUser(userId, bindUserId, isNewUser); return brokerageUserService.bindBrokerageUser(userId, bindUserId, isNewUser);
} }
} }

@ -76,6 +76,11 @@ public class TradeAfterSaleController {
public CommonResult<TradeAfterSaleDetailRespVO> getOrderDetail(@RequestParam("id") Long id) { public CommonResult<TradeAfterSaleDetailRespVO> getOrderDetail(@RequestParam("id") Long id) {
// 查询订单 // 查询订单
TradeAfterSaleDO afterSale = afterSaleService.getAfterSale(id); TradeAfterSaleDO afterSale = afterSaleService.getAfterSale(id);
// TODO @puhui999这里建议改成如果为 null直接返回 success null主要查询操作尽量不要有非空的提示哈交给前端处理
// if (afterSale == null) {
// return success(null, AFTER_SALE_NOT_FOUND.getMsg());
// }
// 查询订单 // 查询订单
TradeOrderDO order = tradeOrderQueryService.getOrder(afterSale.getOrderId()); TradeOrderDO order = tradeOrderQueryService.getOrder(afterSale.getOrderId());
// 查询订单项 // 查询订单项
@ -92,7 +97,11 @@ public class TradeAfterSaleController {
TradeAfterSaleLogRespDTO respVO = new TradeAfterSaleLogRespDTO(); TradeAfterSaleLogRespDTO respVO = new TradeAfterSaleLogRespDTO();
respVO.setId((long) i); respVO.setId((long) i);
respVO.setUserId((long) i); respVO.setUserId((long) i);
respVO.setUserType(1); respVO.setUserType(i % 2 == 0 ? 2 : 1);
// 模拟系统操作
if (i == 2) {
respVO.setUserType(3);
}
respVO.setAfterSaleId(id); respVO.setAfterSaleId(id);
respVO.setOrderId((long) i); respVO.setOrderId((long) i);
respVO.setOrderItemId((long) i); respVO.setOrderItemId((long) i);

@ -35,7 +35,7 @@ public class TradeAfterSaleDetailRespVO extends TradeAfterSaleBaseVO {
/** /**
* *
*/ */
private List<TradeAfterSaleLogRespVO> afterSaleLog; private List<TradeAfterSaleLogRespVO> logs;
@Schema(description = "管理后台 - 交易订单的详情的订单项目") @Schema(description = "管理后台 - 交易订单的详情的订单项目")
@Data @Data

@ -62,7 +62,7 @@ public class BrokerageUserController {
@Operation(summary = "修改推广资格") @Operation(summary = "修改推广资格")
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-brokerage-enable')") @PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-brokerage-enable')")
public CommonResult<Boolean> updateBrokerageEnabled(@Valid @RequestBody BrokerageUserUpdateBrokerageEnabledReqVO updateReqVO) { public CommonResult<Boolean> updateBrokerageEnabled(@Valid @RequestBody BrokerageUserUpdateBrokerageEnabledReqVO updateReqVO) {
brokerageUserService.updateBrokerageEnabled(updateReqVO.getId(), updateReqVO.getEnabled()); brokerageUserService.updateBrokerageUserEnabled(updateReqVO.getId(), updateReqVO.getEnabled());
return success(true); return success(true);
} }
@ -90,12 +90,12 @@ public class BrokerageUserController {
// 合计分佣订单 // 合计分佣订单
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = convertMap(userIds, Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = convertMap(userIds,
userId -> userId, userId -> userId,
userId -> brokerageRecordService.summaryByUserIdAndBizTypeAndStatus(userId, userId -> brokerageRecordService.getUserBrokerageSummaryByUserId(userId,
BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus())); BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus()));
// 合计推广用户数量 // 合计推广用户数量
Map<Long, Long> brokerageUserCountMap = convertMap(userIds, Map<Long, Long> brokerageUserCountMap = convertMap(userIds,
userId -> userId, userId -> userId,
userId -> brokerageUserService.getCountByBindUserId(userId)); userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId));
// todo 合计提现 // todo 合计提现

@ -19,7 +19,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime; private LocalDateTime createTime;
// ========== 用户信息 ========== // ========== 用户信息 ==========
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
@ -27,7 +26,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
@Schema(description = "用户昵称", example = "李四") @Schema(description = "用户昵称", example = "李四")
private String nickname; private String nickname;
// ========== 推广信息 ========== // ========== 推广信息 ==========
@Schema(description = "推广用户数量(一级)", example = "20019") @Schema(description = "推广用户数量(一级)", example = "20019")
@ -37,7 +35,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
@Schema(description = "推广订单金额", example = "20019") @Schema(description = "推广订单金额", example = "20019")
private Integer brokerageOrderPrice; private Integer brokerageOrderPrice;
// ========== 提现信息 ========== // ========== 提现信息 ==========
@Schema(description = "已提现金额", example = "20019") @Schema(description = "已提现金额", example = "20019")

@ -67,6 +67,11 @@ public class TradeOrderController {
public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long id) { public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long id) {
// 查询订单 // 查询订单
TradeOrderDO order = tradeOrderQueryService.getOrder(id); TradeOrderDO order = tradeOrderQueryService.getOrder(id);
// TODO @puhui999这里建议改成如果为 null直接返回 success null主要查询操作尽量不要有非空的提示哈交给前端处理
// if (order == null) {
// return success(null, ORDER_NOT_FOUND.getMsg());
// }
// 查询订单项 // 查询订单项
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id); List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);
// orderLog // orderLog

@ -26,24 +26,23 @@ public class TradeOrderDetailRespVO extends TradeOrderBaseVO {
private MemberUserRespVO user; private MemberUserRespVO user;
/** /**
* TODO , logs * TODO ,
*/ */
private List<OrderLog> orderLog; private List<OrderLog> logs;
// TODO @puhui999swagger 注解 @Schema(description = "管理后台 - 交易订单的操作日志")
@Data @Data
public static class OrderLog { public static class OrderLog {
/** @Schema(description = "操作详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单发货")
*
*/
private String content; private String content;
/** @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-06-01 10:50:20")
*
*/
private LocalDateTime createTime; private LocalDateTime createTime;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer userType;
} }
@Schema(description = "管理后台 - 交易订单的详情的订单项目") @Schema(description = "管理后台 - 交易订单的详情的订单项目")

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.controller.app.brokerage;
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.security.core.annotations.PreAuthenticated; import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -11,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid; import javax.validation.Valid;
@ -40,4 +42,14 @@ public class AppBrokerageRecordController {
return success(new PageResult<>(asList(vo1, vo2), 10L)); return success(new PageResult<>(asList(vo1, vo2), 10L));
} }
@GetMapping("/get-product-brokerage-price")
@Operation(summary = "获得商品的分销金额")
public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO();
respVO.setEnabled(true); // TODO @疯狂:需要开启分销 + 人允许分销
respVO.setBrokerageMinPrice(1);
respVO.setBrokerageMaxPrice(2);
return success(respVO);
}
} }

@ -37,11 +37,19 @@ public class AppBrokerageUserController {
@PreAuthenticated @PreAuthenticated
public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() { public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() {
AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO() AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()
.setBrokerageEnabled(true)
.setPrice(2000) .setPrice(2000)
.setFrozenPrice(3000); .setFrozenPrice(3000);
return success(respVO); return success(respVO);
} }
@PutMapping("/bind")
@Operation(summary = "绑定推广员")
@PreAuthenticated
public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId(), false));
}
// TODO 芋艿:临时 mock => // TODO 芋艿:临时 mock =>
@GetMapping("/get-summary") @GetMapping("/get-summary")
@Operation(summary = "获得个人分销统计") @Operation(summary = "获得个人分销统计")
@ -49,7 +57,7 @@ public class AppBrokerageUserController {
public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() { public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO() AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
.setYesterdayPrice(1) .setYesterdayPrice(1)
.setPrice(2) .setBrokeragePrice(2)
.setFrozenPrice(3) .setFrozenPrice(3)
.setWithdrawPrice(4) .setWithdrawPrice(4)
.setFirstBrokerageUserCount(166) .setFirstBrokerageUserCount(166)
@ -84,16 +92,16 @@ public class AppBrokerageUserController {
public CommonResult<PageResult<AppBrokerageUserRankByPriceRespVO>> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) { public CommonResult<PageResult<AppBrokerageUserRankByPriceRespVO>> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
AppBrokerageUserRankByPriceRespVO vo1 = new AppBrokerageUserRankByPriceRespVO() AppBrokerageUserRankByPriceRespVO vo1 = new AppBrokerageUserRankByPriceRespVO()
.setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg") .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
.setPrice(10); .setBrokeragePrice(10);
AppBrokerageUserRankByPriceRespVO vo2 = new AppBrokerageUserRankByPriceRespVO() AppBrokerageUserRankByPriceRespVO vo2 = new AppBrokerageUserRankByPriceRespVO()
.setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg") .setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
.setPrice(6); .setBrokeragePrice(6);
AppBrokerageUserRankByPriceRespVO vo3 = new AppBrokerageUserRankByPriceRespVO() AppBrokerageUserRankByPriceRespVO vo3 = new AppBrokerageUserRankByPriceRespVO()
.setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg") .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
.setPrice(4); .setBrokeragePrice(4);
AppBrokerageUserRankByPriceRespVO vo4 = new AppBrokerageUserRankByPriceRespVO() AppBrokerageUserRankByPriceRespVO vo4 = new AppBrokerageUserRankByPriceRespVO()
.setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg") .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
.setPrice(4); .setBrokeragePrice(4);
return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L)); return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L));
} }
@ -105,11 +113,11 @@ public class AppBrokerageUserController {
AppBrokerageUserChildSummaryPageReqVO pageReqVO) { AppBrokerageUserChildSummaryPageReqVO pageReqVO) {
AppBrokerageUserChildSummaryRespVO vo1 = new AppBrokerageUserChildSummaryRespVO() AppBrokerageUserChildSummaryRespVO vo1 = new AppBrokerageUserChildSummaryRespVO()
.setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg") .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
.setPrice(10).setPrice(20).setBrokerageOrderCount(30) .setBrokeragePrice(10).setBrokeragePrice(20).setBrokerageOrderCount(30)
.setBrokerageTime(LocalDateTime.now()); .setBrokerageTime(LocalDateTime.now());
AppBrokerageUserChildSummaryRespVO vo2 = new AppBrokerageUserChildSummaryRespVO() AppBrokerageUserChildSummaryRespVO vo2 = new AppBrokerageUserChildSummaryRespVO()
.setId(1L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg") .setId(1L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
.setPrice(20).setPrice(30).setBrokerageOrderCount(40) .setBrokeragePrice(20).setBrokeragePrice(30).setBrokerageOrderCount(40)
.setBrokerageTime(LocalDateTime.now()); .setBrokerageTime(LocalDateTime.now());
return success(new PageResult<>(asList(vo1, vo2), 10L)); return success(new PageResult<>(asList(vo1, vo2), 10L));
} }
@ -118,15 +126,9 @@ public class AppBrokerageUserController {
@GetMapping("/get-rank-by-price") @GetMapping("/get-rank-by-price")
@Operation(summary = "获得分销用户排行(基于佣金)") @Operation(summary = "获得分销用户排行(基于佣金)")
@Parameter(name = "times", description = "时间段", required = true) @Parameter(name = "times", description = "时间段", required = true)
public CommonResult<Integer> getBrokerageUserRankByPrice( public CommonResult<Integer> bindBrokerageUser(
@RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) { @RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) {
return success(1); return success(1);
} }
@PutMapping("/bind-user")
@Operation(summary = "绑定推广员")
public CommonResult<Boolean> getBrokerageUserRankByPrice(@Valid AppBrokerageUserBindReqVO reqVO) {
return success(brokerageUserService.bindUser(getLoginUserId(), reqVO.getBindUserId(), false));
}
} }

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 App - 商品的分销金额 Response VO")
@Data
public class AppBrokerageProductPriceRespVO {
@Schema(description = "是否开启", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Boolean enabled;
@Schema(description = "分销最小金额,单位:分", example = "100")
private Integer brokerageMinPrice;
@Schema(description = "分销最大金额,单位:分", example = "100")
private Integer brokerageMaxPrice;
}

@ -19,7 +19,7 @@ public class AppBrokerageUserChildSummaryRespVO {
private String avatar; private String avatar;
@Schema(description = "佣金金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "佣金金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer price; private Integer brokeragePrice;
@Schema(description = "分销订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") @Schema(description = "分销订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer brokerageOrderCount; private Integer brokerageOrderCount;

@ -14,7 +14,7 @@ public class AppBrokerageUserMySummaryRespVO {
private Integer withdrawPrice; private Integer withdrawPrice;
@Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408") @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
private Integer price; private Integer brokeragePrice;
@Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234") @Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
private Integer frozenPrice; private Integer frozenPrice;

@ -17,6 +17,6 @@ public class AppBrokerageUserRankByPriceRespVO {
private String avatar; private String avatar;
@Schema(description = "佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer price; private Integer brokeragePrice;
} }

@ -7,6 +7,9 @@ import lombok.Data;
@Data @Data
public class AppBrokerageUserRespVO { public class AppBrokerageUserRespVO {
@Schema(description = "是否有分销资格", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean brokerageEnabled;
@Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408") @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
private Integer price; private Integer price;

@ -23,21 +23,18 @@ public class AppBrokerageWithdrawCreateReqVO {
@Min(value = 1, message = "提现金额不能小于 1") @Min(value = 1, message = "提现金额不能小于 1")
private Integer price; private Integer price;
// ========== 银行卡、微信、支付宝 提现相关字段 ========== // ========== 银行卡、微信、支付宝 提现相关字段 ==========
@Schema(description = "提现账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456789") @Schema(description = "提现账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456789")
@NotBlank(message = "提现账号不能为空", groups = {Bank.class, Wechat.class, Alipay.class}) @NotBlank(message = "提现账号不能为空", groups = {Bank.class, Wechat.class, Alipay.class})
private String accountNo; private String accountNo;
// ========== 微信、支付宝 提现相关字段 ========== // ========== 微信、支付宝 提现相关字段 ==========
@Schema(description = "收款码的图片", example = "https://www.iocoder.cn/1.png") @Schema(description = "收款码的图片", example = "https://www.iocoder.cn/1.png")
@URL(message = "收款码的图片,必须是一个 URL") @URL(message = "收款码的图片,必须是一个 URL")
private String accountQrCodeUrl; private String accountQrCodeUrl;
// ========== 银行卡 提现相关字段 ========== // ========== 银行卡 提现相关字段 ==========
@Schema(description = "持卡人姓名", example = "张三") @Schema(description = "持卡人姓名", example = "张三")

@ -4,7 +4,6 @@ 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.security.core.annotations.PreAuthenticated; import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
@ -82,9 +81,10 @@ public class AppTradeOrderController {
public CommonResult<AppTradeOrderDetailRespVO> getOrder(@RequestParam("id") Long id) { public CommonResult<AppTradeOrderDetailRespVO> getOrder(@RequestParam("id") Long id) {
// 查询订单 // 查询订单
TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id); TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id);
if (order == null) { // TODO @puhui999这里建议改成如果为 null直接返回 success null主要查询操作尽量不要有非空的提示哈交给前端处理
return success(null); // if (order == null) {
} // return success(null, ORDER_NOT_FOUND.getMsg());
// }
// 查询订单项 // 查询订单项
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId()); List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId());

@ -50,12 +50,18 @@ public class AppTradeOrderSettlementReqVO {
private Long seckillActivityId; private Long seckillActivityId;
// ========== 拼团活动相关字段 ========== // ========== 拼团活动相关字段 ==========
// TODO @puhui999是不是拼团记录的编号哈
@Schema(description = "拼团活动编号", example = "1024") @Schema(description = "拼团活动编号", example = "1024")
private Long combinationActivityId; private Long combinationActivityId;
@Schema(description = "拼团团长编号", example = "2048") @Schema(description = "拼团团长编号", example = "2048")
private Long combinationHeadId; private Long combinationHeadId;
// ========== 砍价活动相关字段 ==========
// TODO @puhui999是不是砍价记录的编号哈
@Schema(description = "砍价活动编号", example = "123")
private Long bargainActivityId;
@Data @Data
@Schema(description = "用户 App - 商品项") @Schema(description = "用户 App - 商品项")
@Valid @Valid

@ -78,7 +78,7 @@ public interface TradeAfterSaleConvert {
// 处理订单信息 // 处理订单信息
respVO.setOrder(convert(order)); respVO.setOrder(convert(order));
// 处理售后日志 // 处理售后日志
respVO.setAfterSaleLog(convertList1(logs)); respVO.setLogs(convertList1(logs));
return respVO; return respVO;
} }
List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespDTO> list); List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespDTO> list);

@ -33,27 +33,22 @@ public interface BrokerageRecordConvert {
PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> page); PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> page);
// TODO @疯狂:可能 title 不是很固化会存在类似沐晴成功购买《XXX JVM 实战》
default BrokerageRecordDO convert(BrokerageUserDO user, BrokerageRecordBizTypeEnum bizType, String bizId, default BrokerageRecordDO convert(BrokerageUserDO user, BrokerageRecordBizTypeEnum bizType, String bizId,
Integer brokerageFrozenDays, int brokerage, LocalDateTime unfreezeTime, Integer brokerageFrozenDays, int brokeragePrice, LocalDateTime unfreezeTime,
String title, Long sourceUserId, Integer sourceUserType) { String title, Long sourceUserId, Integer sourceUserType) {
brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0); brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0);
// 不冻结时,佣金直接就是结算状态 // 不冻结时,佣金直接就是结算状态
Integer status = brokerageFrozenDays > 0 Integer status = brokerageFrozenDays > 0
? BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus() ? BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus()
: BrokerageRecordStatusEnum.SETTLEMENT.getStatus(); : BrokerageRecordStatusEnum.SETTLEMENT.getStatus();
return new BrokerageRecordDO() return new BrokerageRecordDO().setUserId(user.getId())
.setUserId(user.getId()) .setBizType(bizType.getType()).setBizId(bizId)
.setBizType(bizType.getType()) .setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice())
.setBizId(bizId)
.setPrice(brokerage)
.setTotalPrice(user.getPrice())
.setTitle(title) .setTitle(title)
.setDescription(StrUtil.format(bizType.getDescription(), String.format("¥%.2f", brokerage / 100d))) .setDescription(StrUtil.format(bizType.getDescription(), String.format("¥%.2f", brokeragePrice / 100d)))
.setStatus(status) .setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime)
.setFrozenDays(brokerageFrozenDays) .setSourceUserType(sourceUserType).setSourceUserId(sourceUserId);
.setUnfreezeTime(unfreezeTime)
.setSourceUserType(sourceUserType)
.setSourceUserId(sourceUserId);
} }
default PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> pageResult, Map<Long, MemberUserRespDTO> userMap) { default PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> pageResult, Map<Long, MemberUserRespDTO> userMap) {

@ -41,25 +41,19 @@ public interface BrokerageUserConvert {
// 推广用户数量(一级) // 推广用户数量(一级)
vo.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, vo.getId(), 0)); vo.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, vo.getId(), 0));
// 推广订单数量、推广订单金额
Optional<UserBrokerageSummaryBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(vo.getId())); Optional<UserBrokerageSummaryBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(vo.getId()));
// 推广订单数量 vo.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryBO::getCount).orElse(0))
vo.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryBO::getCount).orElse(0)); .setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryBO::getPrice).orElse(0));
// 推广订单金额 // todo 已提现次数、已提现金额
vo.setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryBO::getPrice).orElse(0)); vo.setWithdrawCount(0).setWithdrawPrice(0);
// todo 已提现次数
vo.setWithdrawCount(0);
// todo 已提现金额
vo.setWithdrawPrice(0);
} }
return result; return result;
} }
default BrokerageUserRespVO copyTo(MemberUserRespDTO source, BrokerageUserRespVO target) { default BrokerageUserRespVO copyTo(MemberUserRespDTO source, BrokerageUserRespVO target) {
Optional.ofNullable(source) Optional.ofNullable(source).ifPresent(
.ifPresent(user -> target.setNickname(user.getNickname()) user -> target.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
.setAvatar(user.getAvatar()));
return target; return target;
} }

@ -95,6 +95,7 @@ public interface TradeOrderConvert {
items.forEach(item -> item.setIncrCount(-item.getIncrCount())); items.forEach(item -> item.setIncrCount(-item.getIncrCount()));
return new ProductSkuUpdateStockReqDTO(items); return new ProductSkuUpdateStockReqDTO(items);
} }
List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list); List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
@Mappings({ @Mappings({
@ -153,9 +154,10 @@ public interface TradeOrderConvert {
TradeOrderDetailRespVO.OrderLog orderLog = new TradeOrderDetailRespVO.OrderLog(); TradeOrderDetailRespVO.OrderLog orderLog = new TradeOrderDetailRespVO.OrderLog();
orderLog.setContent("订单操作" + i); orderLog.setContent("订单操作" + i);
orderLog.setCreateTime(LocalDateTime.now()); orderLog.setCreateTime(LocalDateTime.now());
orderLog.setUserType(i % 2 == 0 ? 2 : 1);
orderLogs.add(orderLog); orderLogs.add(orderLog);
} }
orderVO.setOrderLog(orderLogs); orderVO.setLogs(orderLogs);
return orderVO; return orderVO;
} }

@ -43,7 +43,7 @@ public class BrokerageUserDO extends BaseDO {
private LocalDateTime bindUserTime; private LocalDateTime bindUserTime;
/** /**
* 广 *
*/ */
private Boolean brokerageEnabled; private Boolean brokerageEnabled;
/** /**
@ -54,7 +54,7 @@ public class BrokerageUserDO extends BaseDO {
/** /**
* *
*/ */
private Integer price; private Integer brokeragePrice;
/** /**
* *
*/ */

@ -53,6 +53,7 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
BrokerageRecordDO::getBizId, bizId); BrokerageRecordDO::getBizId, bizId);
} }
// TODO @疯狂mysql 关键字,大写哈;这样看起来清晰点;例如说 SELECT COUNT(1)
@Select("select count(1), sum(price) from trade_brokerage_record where user_id = #{userId} and biz_type = #{bizType} and status = #{status}") @Select("select count(1), sum(price) from trade_brokerage_record where user_id = #{userId} and biz_type = #{bizType} and status = #{status}")
UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId, UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId,
@Param("bizType") Integer bizType, @Param("bizType") Integer bizType,

@ -134,4 +134,5 @@ public interface BrokerageUserMapper extends BaseMapperX<BrokerageUserDO> {
.eq(BrokerageUserDO::getId, id) .eq(BrokerageUserDO::getId, id)
.set(BrokerageUserDO::getBrokerageEnabled, false).set(BrokerageUserDO::getBrokerageTime, null)); .set(BrokerageUserDO::getBrokerageEnabled, false).set(BrokerageUserDO::getBrokerageTime, null));
} }
} }

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.trade.dal.redis.no;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* Redis DAO
*
* @author HUIHUI
*/
@Repository
public class TradeOrderNoRedisDAO {
public static final String TRADE_ORDER_NO_PREFIX = "O";
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
*
*
* @param prefix
* @return
*/
public String generate(String prefix) {
String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN);
Long no = stringRedisTemplate.opsForValue().increment(noPrefix);
return noPrefix + no;
}
}

@ -90,12 +90,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
@Override @Override
public TradeAfterSaleDO getAfterSale(Long id) { public TradeAfterSaleDO getAfterSale(Long id) {
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id); return tradeAfterSaleMapper.selectById(id);
// TODO @puhui999读不到不要这里报错哈交给前端报错一般是读取信息不到message 提示,然后 close tab
if (afterSale == null) {
throw exception(AFTER_SALE_NOT_FOUND);
}
return afterSale;
} }
// TODO 芋艿:拼团失败,要不要发起售后的方式退款?还是走取消逻辑? // TODO 芋艿:拼团失败,要不要发起售后的方式退款?还是走取消逻辑?

@ -5,6 +5,7 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/** /**
* Request BO * Request BO
@ -24,6 +25,7 @@ public class BrokerageAddReqBO {
/** /**
* *
*/ */
@NotNull(message = "佣金基数不能为空")
private Integer basePrice; private Integer basePrice;
/** /**
* *

@ -13,6 +13,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class UserBrokerageSummaryBO { public class UserBrokerageSummaryBO {
/** /**
* *
*/ */
@ -21,4 +22,5 @@ public class UserBrokerageSummaryBO {
* *
*/ */
private Integer price; private Integer price;
} }

@ -66,5 +66,5 @@ public interface BrokerageRecordService {
* @param status * @param status
* @return * @return
*/ */
UserBrokerageSummaryBO summaryByUserIdAndBizTypeAndStatus(Long userId, Integer bizType, Integer status); UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status);
} }

@ -91,6 +91,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId) { public void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId) {
// TODO @疯狂userId 加进去查询,会不会更好一点?万一穿错参数;
BrokerageRecordDO record = brokerageRecordMapper.selectByBizTypeAndBizId(bizType.getType(), bizId); BrokerageRecordDO record = brokerageRecordMapper.selectByBizTypeAndBizId(bizType.getType(), bizId);
if (record == null || ObjectUtil.notEqual(record.getUserId(), userId)) { if (record == null || ObjectUtil.notEqual(record.getUserId(), userId)) {
log.error("[cancelBrokerage][userId({})][bizId({}) 更新为已失效失败:记录不存在]", userId, bizId); log.error("[cancelBrokerage][userId({})][bizId({}) 更新为已失效失败:记录不存在]", userId, bizId);
@ -209,9 +210,9 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
} }
@Override @Override
public UserBrokerageSummaryBO summaryByUserIdAndBizTypeAndStatus(Long userId, Integer bizType, Integer status) { public UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status) {
UserBrokerageSummaryBO summaryBO = brokerageRecordMapper.selectCountAndSumPriceByUserIdAndBizTypeAndStatus(userId, bizType, status); UserBrokerageSummaryBO summaryBO = brokerageRecordMapper.selectCountAndSumPriceByUserIdAndBizTypeAndStatus(userId, bizType, status);
return summaryBO == null ? new UserBrokerageSummaryBO(0, 0) : summaryBO; return summaryBO != null ? summaryBO : new UserBrokerageSummaryBO(0, 0);
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)

@ -52,7 +52,7 @@ public interface BrokerageUserService {
* @param id * @param id
* @param enabled 广 * @param enabled 广
*/ */
void updateBrokerageEnabled(Long id, Boolean enabled); void updateBrokerageUserEnabled(Long id, Boolean enabled);
/** /**
* 广 * 广
@ -79,20 +79,21 @@ public interface BrokerageUserService {
void updateUserFrozenPrice(Long id, Integer frozenPrice); void updateUserFrozenPrice(Long id, Integer frozenPrice);
/** /**
* , *
* *
* @param id * @param id
* @param frozenPrice * @param frozenPrice
*/ */
void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice); void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice);
// TODO @疯狂:这个后面可能要支持下,二级
/** /**
* 广 * 广
* *
* @param bindUserId 广 * @param bindUserId 广
* @return 广 * @return 广
*/ */
Long getCountByBindUserId(Long bindUserId); Long getBrokerageUserCountByBindUserId(Long bindUserId);
/** /**
* 广 * 广
@ -102,5 +103,6 @@ public interface BrokerageUserService {
* @param isNewUser * @param isNewUser
* @return * @return
*/ */
boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser); boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser);
} }

@ -55,23 +55,24 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
@Override @Override
public void updateBrokerageUserId(Long id, Long bindUserId) { public void updateBrokerageUserId(Long id, Long bindUserId) {
// 0. 校验存在 // 校验存在
BrokerageUserDO brokerageUser = validateBrokerageUserExists(id); BrokerageUserDO brokerageUser = validateBrokerageUserExists(id);
// 情况一:清除推广员
if (bindUserId == null) { if (bindUserId == null) {
// 1. 清除推广员 // 清除推广员
brokerageUserMapper.updateBindUserIdAndBindUserTimeToNull(id); brokerageUserMapper.updateBindUserIdAndBindUserTimeToNull(id);
return; return;
} }
// 2.1 校验能否绑定 // 情况二:修改推广员
validateCanBindUser(brokerageUser, bindUserId); validateCanBindUser(brokerageUser, bindUserId);
// 2.2 修改推广员
brokerageUserMapper.updateById(new BrokerageUserDO().setId(id) brokerageUserMapper.updateById(new BrokerageUserDO().setId(id)
.setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now())); .setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now()));
} }
@Override @Override
public void updateBrokerageEnabled(Long id, Boolean enabled) { public void updateBrokerageUserEnabled(Long id, Boolean enabled) {
// 校验存在 // 校验存在
validateBrokerageUserExists(id); validateBrokerageUserExists(id);
if (BooleanUtil.isTrue(enabled)) { if (BooleanUtil.isTrue(enabled)) {
@ -130,39 +131,42 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
@Override @Override
public Long getCountByBindUserId(Long bindUserId) { public Long getBrokerageUserCountByBindUserId(Long bindUserId) {
// TODO @疯狂mapper 封装下哈;不直接在 service 调用这种基础 mapper 的基础方法
return brokerageUserMapper.selectCount(BrokerageUserDO::getBindUserId, bindUserId); return brokerageUserMapper.selectCount(BrokerageUserDO::getBindUserId, bindUserId);
} }
// TODO @疯狂:因为现在 user 会存在使用验证码直接注册,所以 isNewUser 不太好传递我们是不是可以约定绑定的时间createTime 在 30 秒内,就认为新用户;
@Override @Override
public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) { public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) {
// TODO @疯狂userId 为空,搞到参数校验里哇;
if (userId == null) { if (userId == null) {
throw exception(0); throw exception(0);
} }
boolean isInsert = false; // 1. 获得分销用户
boolean isNewBrokerageUser = false;
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId); BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
// 分销用户不存在的情况1.新注册 2.旧数据 3.分销功能关闭后又打开 if (brokerageUser == null) { // 分销用户不存在的情况1. 新注册2. 旧数据3. 分销功能关闭后又打开
if (brokerageUser == null) { isNewBrokerageUser = true;
isInsert = true; brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setBrokeragePrice(0).setFrozenPrice(0);
brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setPrice(0).setFrozenPrice(0);
} }
// 校验分配配置 // 2.1 校验是否能绑定用户
boolean validated = validateTradeConfig(brokerageUser, bindUserId, isNewUser); boolean validated = isUserCanBind(brokerageUser, bindUserId, isNewUser);
if (!validated) { if (!validated) {
return false; return false;
} }
// 2.3 校验能否绑定
// 校验能否绑定
validateCanBindUser(brokerageUser, bindUserId); validateCanBindUser(brokerageUser, bindUserId);
// 2.3 绑定用户
if (isInsert) { if (isNewBrokerageUser) {
Integer enabledCondition = tradeConfigService.getTradeConfig().getBrokerageEnabledCondition(); Integer enabledCondition = tradeConfigService.getTradeConfig().getBrokerageEnabledCondition();
if (BrokerageEnabledConditionEnum.ALL.getCondition().equals(enabledCondition)) { if (BrokerageEnabledConditionEnum.ALL.getCondition().equals(enabledCondition)) { // 人人分销:用户默认就有分销资格
// 人人分销:用户默认就有分销资格 // TODO @疯狂:应该设置下 brokerageTime而不是 bindUserTime
brokerageUser.setBrokerageEnabled(true).setBindUserTime(LocalDateTime.now()); brokerageUser.setBrokerageEnabled(true).setBindUserTime(LocalDateTime.now());
} }
// TODO @疯狂:这里是不是要设置 bindUserId、bindUserTime 字段哈;
brokerageUserMapper.insert(brokerageUser); brokerageUserMapper.insert(brokerageUser);
} else { } else {
brokerageUserMapper.updateById(new BrokerageUserDO().setId(userId) brokerageUserMapper.updateById(new BrokerageUserDO().setId(userId)
@ -171,7 +175,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
return true; return true;
} }
private boolean validateTradeConfig(BrokerageUserDO user, Long bindUserId, Boolean isNewUser) { private boolean isUserCanBind(BrokerageUserDO user, Long bindUserId, Boolean isNewUser) {
// 校验分销功能是否启用 // 校验分销功能是否启用
TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) { if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
@ -194,7 +198,6 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
} }
} }
validateCanBindUser(user, bindUserId);
return true; return true;
} }
@ -210,6 +213,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
throw exception(BROKERAGE_BIND_SELF); throw exception(BROKERAGE_BIND_SELF);
} }
// TODO @疯狂:这块是不是一直查询到根节点,中间不允许出现自己;就是不能形成环。虽然目前是 2 级,但是未来可能会改多级; = = 环的话,就会存在问题哈
// A->B->A下级不能绑定自己的上级, A->B->C->A可以!! // A->B->A下级不能绑定自己的上级, A->B->C->A可以!!
if (Objects.equals(user.getId(), bindUser.getBindUserId())) { if (Objects.equals(user.getId(), bindUser.getBindUserId())) {
throw exception(BROKERAGE_BIND_LOOP); throw exception(BROKERAGE_BIND_LOOP);

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.service.order;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
@ -25,12 +24,14 @@ import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi;
import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
@ -46,6 +47,7 @@ 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.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.redis.no.TradeOrderNoRedisDAO;
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
@ -74,7 +76,7 @@ import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_NOT_FOUND; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_UPDATE_PRICE_FAIL_EQUAL;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_UPDATE_PRICE_FAIL_PAID; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_UPDATE_PRICE_FAIL_PAID;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
@ -92,6 +94,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
private TradeOrderMapper tradeOrderMapper; private TradeOrderMapper tradeOrderMapper;
@Resource @Resource
private TradeOrderItemMapper tradeOrderItemMapper; private TradeOrderItemMapper tradeOrderItemMapper;
@Resource
private TradeOrderNoRedisDAO orderNoRedisDAO;
@Resource @Resource
private CartService cartService; private CartService cartService;
@ -115,6 +119,10 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Resource @Resource
private BargainRecordApi bargainRecordApi; private BargainRecordApi bargainRecordApi;
@Resource @Resource
private SeckillActivityApi seckillActivityApi;
@Resource
private BargainActivityApi bargainActivityApi;
@Resource
private MemberUserApi memberUserApi; private MemberUserApi memberUserApi;
@Resource @Resource
private MemberLevelApi memberLevelApi; private MemberLevelApi memberLevelApi;
@ -195,22 +203,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// TODO @puhui999这个逻辑先抽个小方法未来要通过设计模式把这些拼团之类的逻辑抽象出去 // TODO @puhui999这个逻辑先抽个小方法未来要通过设计模式把这些拼团之类的逻辑抽象出去
// 拼团 // 拼团
if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
MemberUserRespDTO user = memberUserApi.getUser(userId); createCombinationRecord(userId, createReqVO, orderItems, order);
List<CombinationRecordRespDTO> recordRespDTOS = combinationRecordApi.getRecordListByUserIdAndActivityId(userId, createReqVO.getCombinationActivityId());
// TODO 拼团一次应该只能选择一种规格的商品
TradeOrderItemDO orderItemDO = orderItems.get(0);
if (CollUtil.isNotEmpty(recordRespDTOS)) {
List<Long> skuIds = convertList(recordRespDTOS, CombinationRecordRespDTO::getSkuId, item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()));
List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectListByOrderIdAnSkuId(convertList(recordRespDTOS,
CombinationRecordRespDTO::getOrderId, item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), skuIds);
combinationRecordApi.validateCombinationLimitCount(createReqVO.getCombinationActivityId(),
CollectionUtils.getSumValue(tradeOrderItemDOS, TradeOrderItemDO::getCount, Integer::sum), orderItemDO.getCount());
}
combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, orderItemDO, createReqVO, user));
} }
// 3.2 秒杀的特殊逻辑 // 3.2 秒杀的特殊逻辑
// TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除
if (Objects.equals(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) { if (Objects.equals(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) {
} }
@ -220,6 +215,22 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
return order; return order;
} }
private void createCombinationRecord(Long userId, AppTradeOrderCreateReqVO createReqVO, List<TradeOrderItemDO> orderItems, TradeOrderDO order) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
List<CombinationRecordRespDTO> recordRespDTOS = combinationRecordApi.getRecordListByUserIdAndActivityId(userId, createReqVO.getCombinationActivityId());
// TODO 拼团一次应该只能选择一种规格的商品
TradeOrderItemDO orderItemDO = orderItems.get(0);
if (CollUtil.isNotEmpty(recordRespDTOS)) {
List<Long> skuIds = convertList(recordRespDTOS, CombinationRecordRespDTO::getSkuId, item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()));
List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectListByOrderIdAnSkuId(convertList(recordRespDTOS,
CombinationRecordRespDTO::getOrderId, item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), skuIds);
combinationRecordApi.validateCombinationLimitCount(createReqVO.getCombinationActivityId(),
CollectionUtils.getSumValue(tradeOrderItemDOS, TradeOrderItemDO::getCount, Integer::sum), orderItemDO.getCount());
}
combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, orderItemDO, createReqVO, user));
}
// TODO @puhui999订单超时自动取消 // TODO @puhui999订单超时自动取消
/** /**
@ -246,8 +257,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
address = validateAddress(userId, createReqVO.getAddressId()); address = validateAddress(userId, createReqVO.getAddressId());
} }
TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address); TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
String no = orderNoRedisDAO.generate(TradeOrderNoRedisDAO.TRADE_ORDER_NO_PREFIX);
order.setType(validateActivity(createReqVO)); order.setType(validateActivity(createReqVO));
order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @puhui999: 参考支付订单,的 no 生成哈; order.setNo(no);
order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
@ -299,19 +311,23 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems, TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems,
TradePriceCalculateRespBO calculateRespBO) { TradePriceCalculateRespBO calculateRespBO) {
// 下单时扣减商品库存 Integer count = getSumValue(orderItems, TradeOrderItemDO::getCount, Integer::sum);
// TODO @puhui999扣库存需要前置
// 1如果是秒杀商品额外扣减秒杀的库存 // 1如果是秒杀商品额外扣减秒杀的库存
// 2如果是拼团活动额外扣减拼团的库存 if (Objects.equals(TradeOrderTypeEnum.SECKILL.getType(), tradeOrderDO.getType())) {
// 3如果是砍价活动额外扣减砍价的库存 SeckillActivityUpdateStockReqDTO updateStockReqDTO = new SeckillActivityUpdateStockReqDTO();
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems)); updateStockReqDTO.setActivityId(createReqVO.getSeckillActivityId());
updateStockReqDTO.setCount(count);
// 删除购物车商品 updateStockReqDTO.setItems(CollectionUtils.convertList(orderItems, item -> {
Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); SeckillActivityUpdateStockReqDTO.Item item1 = new SeckillActivityUpdateStockReqDTO.Item();
if (CollUtil.isNotEmpty(cartIds)) { item1.setSpuId(item.getSpuId());
cartService.deleteCart(userId, cartIds); item1.setSkuId(item.getSkuId());
} item1.setCount(item.getCount());
return item1;
}));
seckillActivityApi.updateSeckillStock(updateStockReqDTO);
}
// 2如果是砍价活动额外扣减砍价的库存
bargainActivityApi.updateBargainActivityStock(createReqVO.getBargainActivityId(), count);
// 扣减积分 TODO 芋艿:待实现,需要前置; // 扣减积分 TODO 芋艿:待实现,需要前置;
// 这个是不是应该放到支付成功之后?如果支付后的话,可能积分可以重复使用哈。资源类,都要预扣 // 这个是不是应该放到支付成功之后?如果支付后的话,可能积分可以重复使用哈。资源类,都要预扣
@ -321,6 +337,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
.setOrderId(tradeOrderDO.getId())); .setOrderId(tradeOrderDO.getId()));
} }
// 下单时扣减商品库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));
// 删除购物车商品
Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
if (CollUtil.isNotEmpty(cartIds)) {
cartService.deleteCart(userId, cartIds);
}
// 生成预支付 // 生成预支付
createPayOrder(tradeOrderDO, orderItems, calculateRespBO); createPayOrder(tradeOrderDO, orderItems, calculateRespBO);
@ -357,8 +382,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// 1、拼团活动 // 1、拼团活动
if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
// 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录 // 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录
combinationRecordApi.updateCombinationRecordStatus(new CombinationRecordUpdateStatusReqDTO().setUserId(order.getUserId()) combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now());
.setOrderId(order.getId()).setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()).setStartTime(LocalDateTime.now()));
} }
// TODO 芋艿:发送订单变化的消息 // TODO 芋艿:发送订单变化的消息
@ -371,7 +395,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// 增加用户经验 // 增加用户经验
getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId()); getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId());
// 增加用户佣金 // 增加用户佣金
getSelf().addBrokerageAsync(order.getUserId(), BrokerageRecordBizTypeEnum.ORDER, order.getId()); getSelf().addBrokerageAsync(order.getUserId(), order.getId());
} }
/** /**
@ -473,11 +497,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
*/ */
private TradeOrderDO validateOrderDeliverable(Long id) { private TradeOrderDO validateOrderDeliverable(Long id) {
TradeOrderDO order = validateOrderExists(id); TradeOrderDO order = validateOrderExists(id);
// 校验订单是否是待发货状态
// TODO @puhui999已经发货可以重新发货修改信息
if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())) {
throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
}
// 校验订单是否退款 // 校验订单是否退款
if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE); throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
@ -543,20 +562,35 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
} }
@Override @Override
// TODO @puhui999考虑事务性
public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) { public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) {
// 校验交易订单 // 校验交易订单
TradeOrderDO order = validateOrderExists(reqVO.getId()); TradeOrderDO order = validateOrderExists(reqVO.getId());
if (order.getPayStatus()) { if (order.getPayStatus()) {
throw exception(ORDER_UPDATE_PRICE_FAIL_PAID); throw exception(ORDER_UPDATE_PRICE_FAIL_PAID);
} }
// TODO @puhui999如果改价需要校验下是否真的变化 if (ObjectUtil.equal(order.getAdjustPrice(), reqVO.getAdjustPrice())) {
throw exception(ORDER_UPDATE_PRICE_FAIL_EQUAL);
// 更新 }
// TODO @puhui999TradeOrderItemDO 需要做 adjustPrice 的分摊;另外,支付订单那的价格,需要 update 下;
// TODO @puhui999应该是按照 payPrice 分配并且要考虑取余问题payPrice 也要考虑item 里的
List<TradeOrderItemDO> itemDOs = tradeOrderItemMapper.selectListByOrderId(order.getId());
// TradeOrderItemDO 需要做 adjustPrice 的分摊
int price = reqVO.getAdjustPrice() / itemDOs.size();
itemDOs.forEach(item -> {
item.setAdjustPrice(price);
});
// 更新 TradeOrderItem
// TODO @puhui999不要整个对象去更新哈应该 new 一下;
tradeOrderItemMapper.updateBatch(itemDOs);
// 更新订单
// TODO @puhui999要考虑多次修改价格不能单单的 payPrice + 价格;
TradeOrderDO update = TradeOrderConvert.INSTANCE.convert(reqVO); TradeOrderDO update = TradeOrderConvert.INSTANCE.convert(reqVO);
update.setPayPrice(update.getPayPrice() + update.getAdjustPrice()); update.setPayPrice(update.getPayPrice() + update.getAdjustPrice());
// TODO @芋艿:改价时,赠送的积分,要不要做改动??? // TODO @芋艿:改价时,赠送的积分,要不要做改动???
tradeOrderMapper.updateById(update); tradeOrderMapper.updateById(update);
// 更新支付订单
payOrderApi.updatePayOrderPriceById(order.getPayOrderId(), update.getPayPrice());
} }
@Override @Override
@ -641,7 +675,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// 扣减用户经验 // 扣减用户经验
getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId); getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId);
// 更新分佣记录为已失效 // 更新分佣记录为已失效
getSelf().cancelBrokerageAsync(order.getUserId(), BrokerageRecordBizTypeEnum.ORDER, id); getSelf().cancelBrokerageAsync(order.getUserId(), id);
} }
@Override @Override
@ -751,16 +785,16 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Async @Async
protected void addBrokerageAsync(Long userId, BrokerageRecordBizTypeEnum bizType, Long orderId) { protected void addBrokerageAsync(Long userId, Long orderId) {
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId); List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId);
List<BrokerageAddReqBO> list = convertList(orderItems, List<BrokerageAddReqBO> list = convertList(orderItems,
item -> TradeOrderConvert.INSTANCE.convert(item, productSkuApi.getSku(item.getSkuId()))); item -> TradeOrderConvert.INSTANCE.convert(item, productSkuApi.getSku(item.getSkuId())));
brokerageRecordService.addBrokerage(userId, bizType, list); brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, list);
} }
@Async @Async
protected void cancelBrokerageAsync(Long userId, BrokerageRecordBizTypeEnum bizType, Long orderItemId) { protected void cancelBrokerageAsync(Long userId, Long orderItemId) {
brokerageRecordService.cancelBrokerage(userId, bizType, String.valueOf(orderItemId)); brokerageRecordService.cancelBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItemId));
} }
/** /**

@ -33,11 +33,6 @@
<artifactId>yudao-module-infra-api</artifactId> <artifactId>yudao-module-infra-api</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-trade-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 --> <!-- 业务组件 -->
<dependency> <dependency>

@ -28,6 +28,17 @@ tenant-id: {{appTenentId}}
"code": 9999 "code": 9999
} }
### 请求 /social-login 接口 => 成功
POST {{appApi}}/member/auth/social-login
Content-Type: application/json
tenant-id: {{appTenentId}}
{
"type": 34,
"code": "0e1oc9000CTjFQ1oim200bhtb61oc90g",
"state": "default"
}
### 请求 /weixin-mini-app-login 接口 => 成功 ### 请求 /weixin-mini-app-login 接口 => 成功
POST {{appApi}}/member/auth/weixin-mini-app-login POST {{appApi}}/member/auth/weixin-mini-app-login
Content-Type: application/json Content-Type: application/json
@ -38,7 +49,6 @@ tenant-id: {{appTenentId}}
"loginCode": "001frTkl21JUf94VGxol2hSlff1frTkR" "loginCode": "001frTkl21JUf94VGxol2hSlff1frTkR"
} }
### 请求 /logout 接口 => 成功 ### 请求 /logout 接口 => 成功
POST {{appApi}}/member/auth/logout POST {{appApi}}/member/auth/logout
Content-Type: application/json Content-Type: application/json

@ -27,4 +27,12 @@ public class AppAuthLoginRespVO {
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expiresTime; private LocalDateTime expiresTime;
/**
*
*
* openid
*/
@Schema(description = "社交用户 openid", example = "qq768")
private String openid;
} }

@ -8,8 +8,6 @@ import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.service.level.MemberLevelService; import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService; import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import cn.iocoder.yudao.module.trade.api.brokerage.BrokerageApi;
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -18,7 +16,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Optional;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@ -32,23 +29,16 @@ public class AppMemberUserController {
@Resource @Resource
private MemberUserService userService; private MemberUserService userService;
@Resource @Resource
private MemberLevelService levelService; private MemberLevelService levelService;
@Resource
private BrokerageApi brokerageApi;
@GetMapping("/get") @GetMapping("/get")
@Operation(summary = "获得基本信息") @Operation(summary = "获得基本信息")
@PreAuthenticated @PreAuthenticated
public CommonResult<AppMemberUserInfoRespVO> getUserInfo() { public CommonResult<AppMemberUserInfoRespVO> getUserInfo() {
MemberUserDO user = userService.getUser(getLoginUserId()); MemberUserDO user = userService.getUser(getLoginUserId());
MemberLevelDO level = levelService.getLevel(user.getLevelId()); MemberLevelDO level = levelService.getLevel(user.getLevelId());
BrokerageUserDTO brokerageUser = brokerageApi.getBrokerageUser(user.getId()); return success(MemberUserConvert.INSTANCE.convert(user, level));
return success(MemberUserConvert.INSTANCE.convert(user, level)
.setBrokerageEnabled(Optional.ofNullable(brokerageUser).map(BrokerageUserDTO::getBrokerageEnabled).orElse(false))
);
} }
@PutMapping("/update") @PutMapping("/update")

@ -25,7 +25,7 @@ public interface AuthConvert {
SmsCodeUseReqDTO convert(AppMemberUserResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp); SmsCodeUseReqDTO convert(AppMemberUserResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp); SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean); AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean, String openid);
SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean); SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean);

@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.social.SocialUserApi; import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.enums.logger.LoginLogTypeEnum; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants; import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
@ -65,13 +66,14 @@ public class MemberAuthServiceImpl implements MemberAuthService {
MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword()); MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword());
// 如果 socialType 非空,说明需要绑定社交用户 // 如果 socialType 非空,说明需要绑定社交用户
String openid = null;
if (reqVO.getSocialType() != null) { if (reqVO.getSocialType() != null) {
socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
} }
// 创建 Token 令牌,记录登录日志 // 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, openid);
} }
@Override @Override
@ -86,32 +88,33 @@ public class MemberAuthServiceImpl implements MemberAuthService {
Assert.notNull(user, "获取用户失败,结果为空"); Assert.notNull(user, "获取用户失败,结果为空");
// 如果 socialType 非空,说明需要绑定社交用户 // 如果 socialType 非空,说明需要绑定社交用户
String openid = null;
if (reqVO.getSocialType() != null) { if (reqVO.getSocialType() != null) {
socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
} }
// 创建 Token 令牌,记录登录日志 // 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS); return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS, openid);
} }
@Override @Override
public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) { public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) {
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), SocialUserRespDTO socialUser = socialUserApi.getSocialUser(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
reqVO.getCode(), reqVO.getState()); reqVO.getCode(), reqVO.getState());
if (userId == null) { if (socialUser == null) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND); throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
} }
// 自动登录 // 自动登录
MemberUserDO user = userService.getUser(userId); MemberUserDO user = userService.getUser(socialUser.getUserId());
if (user == null) { if (user == null) {
throw exception(USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }
// 创建 Token 令牌,记录登录日志 // 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL); return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, socialUser.getOpenid());
} }
@Override @Override
@ -129,14 +132,15 @@ public class MemberAuthServiceImpl implements MemberAuthService {
Assert.notNull(user, "获取用户失败,结果为空"); Assert.notNull(user, "获取用户失败,结果为空");
// 绑定社交用户 // 绑定社交用户
socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), "")); SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), ""));
// 创建 Token 令牌,记录登录日志 // 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL); return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, openid);
} }
private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) { private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile,
LoginLogTypeEnum logType, String openid) {
// 插入登陆日志 // 插入登陆日志
createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS); createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
// 创建 Token 令牌 // 创建 Token 令牌
@ -144,7 +148,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
.setUserId(user.getId()).setUserType(getUserType().getValue()) .setUserId(user.getId()).setUserType(getUserType().getValue())
.setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT)); .setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT));
// 构建返回结果 // 构建返回结果
return AuthConvert.INSTANCE.convert(accessTokenRespDTO); return AuthConvert.INSTANCE.convert(accessTokenRespDTO, openid);
} }
@Override @Override
@ -231,7 +235,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
public AppAuthLoginRespVO refreshToken(String refreshToken) { public AppAuthLoginRespVO refreshToken(String refreshToken) {
OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken,
OAuth2ClientConstants.CLIENT_ID_DEFAULT); OAuth2ClientConstants.CLIENT_ID_DEFAULT);
return AuthConvert.INSTANCE.convert(accessTokenDO); return AuthConvert.INSTANCE.convert(accessTokenDO, null);
} }
private void createLogoutLog(Long userId) { private void createLogoutLog(Long userId) {

@ -29,4 +29,13 @@ public interface PayOrderApi {
*/ */
PayOrderRespDTO getOrder(Long id); PayOrderRespDTO getOrder(Long id);
// TODO @puhui999可以去掉 byId然后 payOrderId 参数改成 id
/**
*
*
* @param payOrderId
* @param payPrice
*/
void updatePayOrderPriceById(Long payOrderId, Integer payPrice);
} }

@ -28,6 +28,7 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}"); ErrorCode ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}");
ErrorCode ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1007002005, "支付订单退款失败,原因:状态不是已支付或已退款"); ErrorCode ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1007002005, "支付订单退款失败,原因:状态不是已支付或已退款");
ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1007002006, "支付订单调价失败,原因:支付订单已付款,不能调价"); ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1007002006, "支付订单调价失败,原因:支付订单已付款,不能调价");
ErrorCode ORDER_UPDATE_PRICE_FAIL_EQUAL = new ErrorCode(1007002007, "支付订单调价失败,原因:价格没有变化");
// ========== ORDER 模块(拓展单) 1007003000 ========== // ========== ORDER 模块(拓展单) 1007003000 ==========
ErrorCode ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在"); ErrorCode ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");

@ -31,4 +31,9 @@ public class PayOrderApiImpl implements PayOrderApi {
return PayOrderConvert.INSTANCE.convert2(order); return PayOrderConvert.INSTANCE.convert2(order);
} }
@Override
public void updatePayOrderPriceById(Long payOrderId, Integer payPrice) {
payOrderService.updatePayOrderPriceById(payOrderId, payPrice);
}
} }

@ -6,14 +6,15 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
import cn.iocoder.yudao.module.pay.framework.pay.wallet.WalletPayClient;
import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.app.PayAppService;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -25,12 +26,17 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType;
@Tag(name = "管理后台 - 支付订单") @Tag(name = "管理后台 - 支付订单")
@RestController @RestController
@ -70,13 +76,16 @@ public class PayOrderController {
@PostMapping("/submit") @PostMapping("/submit")
@Operation(summary = "提交支付订单") @Operation(summary = "提交支付订单")
public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) { public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
// 钱包支付需要 额外传 user_id 和 user_type // 1. 钱包支付事,需要额外传 user_id 和 user_type
if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) { if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) {
Map<String, String> channelExtras = reqVO.getChannelExtras() == null ? new HashMap<>(8) : reqVO.getChannelExtras(); Map<String, String> channelExtras = reqVO.getChannelExtras() == null ?
channelExtras.put("user_id", String.valueOf(WebFrameworkUtils.getLoginUserId())); Maps.newHashMapWithExpectedSize(2) : reqVO.getChannelExtras();
channelExtras.put("user_type", String.valueOf(WebFrameworkUtils.getLoginUserType())); channelExtras.put(WalletPayClient.USER_ID_KEY, String.valueOf(getLoginUserId()));
channelExtras.put(WalletPayClient.USER_TYPE_KEY, String.valueOf(getLoginUserType()));
reqVO.setChannelExtras(channelExtras); reqVO.setChannelExtras(channelExtras);
} }
// 2. 提交支付
PayOrderSubmitRespVO respVO = orderService.submitOrder(reqVO, getClientIP()); PayOrderSubmitRespVO respVO = orderService.submitOrder(reqVO, getClientIP());
return success(respVO); return success(respVO);
} }

@ -37,7 +37,7 @@ public class AppPayWalletController {
@Operation(summary = "获取钱包") @Operation(summary = "获取钱包")
@PreAuthenticated @PreAuthenticated
public CommonResult<AppPayWalletRespVO> getPayWallet() { public CommonResult<AppPayWalletRespVO> getPayWallet() {
PayWalletDO wallet = payWalletService.getOrCreatePayWallet(getLoginUserId(), UserTypeEnum.MEMBER.getValue()); PayWalletDO wallet = payWalletService.getOrCreateWallet(getLoginUserId(), UserTypeEnum.MEMBER.getValue());
return success(PayWalletConvert.INSTANCE.convert(wallet)); return success(PayWalletConvert.INSTANCE.convert(wallet));
} }

@ -11,9 +11,9 @@ public class AppPayWalletRespVO {
private Integer balance; private Integer balance;
@Schema(description = "累计支出, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") @Schema(description = "累计支出, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
private Long totalExpense; private Integer totalExpense;
@Schema(description = "累计充值, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000") @Schema(description = "累计充值, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
private Long totalRecharge; private Integer totalRecharge;
} }

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.convert.wallet;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionRespVO; import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionRespVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
@ -12,4 +13,7 @@ public interface PayWalletTransactionConvert {
PayWalletTransactionConvert INSTANCE = Mappers.getMapper(PayWalletTransactionConvert.class); PayWalletTransactionConvert INSTANCE = Mappers.getMapper(PayWalletTransactionConvert.class);
PageResult<AppPayWalletTransactionRespVO> convertPage(PageResult<PayWalletTransactionDO> page); PageResult<AppPayWalletTransactionRespVO> convertPage(PageResult<PayWalletTransactionDO> page);
PayWalletTransactionDO convert(CreateWalletTransactionBO bean);
} }

@ -45,10 +45,10 @@ public class PayWalletDO extends BaseDO {
/** /**
* *
*/ */
private Long totalExpense; private Integer totalExpense;
/** /**
* *
*/ */
private Long totalRecharge; private Integer totalRecharge;
} }

@ -3,8 +3,7 @@ package cn.iocoder.yudao.module.pay.dal.mysql.wallet;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
@ -16,52 +15,32 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
} }
/** /**
* * 退
* *
* @param bizType * @param price
* @param balance
* @param totalRecharge
* @param totalExpense
* @param price
* @param id id * @param id id
*/ */
default int updateWhenDecBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge, default int updateWhenConsumptionRefund(Integer price, Long id){
Long totalExpense, Integer price, Long id) { LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
PayWalletDO updateDO = new PayWalletDO().setBalance(balance - price); .setSql(" balance = balance + " + price
if(bizType == PayWalletBizTypeEnum.PAYMENT){ + ", total_expense = total_expense - " + price)
updateDO.setTotalExpense(totalExpense + price); .eq(PayWalletDO::getId, id);
} return update(null, lambdaUpdateWrapper);
if (bizType == PayWalletBizTypeEnum.RECHARGE_REFUND) {
updateDO.setTotalRecharge(totalRecharge - price);
}
return update(updateDO,
new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
.eq(PayWalletDO::getBalance, balance)
.ge(PayWalletDO::getBalance, price));
} }
/** /**
* *
* *
* @param bizType * @param price
* @param balance
* @param totalRecharge
* @param totalExpense
* @param price
* @param id id * @param id id
*/ */
default int updateWhenIncBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge, default int updateWhenConsumption(Integer price, Long id){
Long totalExpense, Integer price, Long id) { LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
PayWalletDO updateDO = new PayWalletDO().setBalance(balance + price); .setSql(" balance = balance - " + price
if (bizType == PayWalletBizTypeEnum.PAYMENT_REFUND) { + ", total_expense = total_expense + " + price)
updateDO.setTotalExpense(totalExpense - price); .eq(PayWalletDO::getId, id)
} .ge(PayWalletDO::getBalance, price); // cas 逻辑
if (bizType == PayWalletBizTypeEnum.RECHARGE) { return update(null, lambdaUpdateWrapper);
updateDO.setTotalExpense(totalRecharge + price);
}
return update(updateDO,
new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
.eq(PayWalletDO::getBalance, balance));
} }
} }

@ -37,13 +37,13 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.REFUND_NOT_FO
@Slf4j @Slf4j
public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
private PayWalletService wallService; public static final String USER_ID_KEY = "user_id";
public static final String USER_TYPE_KEY = "user_type";
private PayWalletService wallService;
private PayWalletTransactionService walletTransactionService; private PayWalletTransactionService walletTransactionService;
private PayOrderService orderService;
private PayOrderService payOrderService; private PayRefundService refundService;
private PayRefundService payRefundService;
public WalletPayClient(Long channelId, NonePayClientConfig config) { public WalletPayClient(Long channelId, NonePayClientConfig config) {
super(channelId, PayChannelEnum.WALLET.getCode(), config); super(channelId, PayChannelEnum.WALLET.getCode(), config);
@ -62,12 +62,12 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
@Override @Override
protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
try { try {
String userId = MapUtil.getStr(reqDTO.getChannelExtras(), "user_id"); Long userId = MapUtil.getLong(reqDTO.getChannelExtras(), USER_ID_KEY);
String userType = MapUtil.getStr(reqDTO.getChannelExtras(), "user_type"); Integer userType = MapUtil.getInt(reqDTO.getChannelExtras(), USER_TYPE_KEY);
Assert.notEmpty(userId, "用户 id 不能为空"); Assert.notNull(userId, "用户 id 不能为空");
Assert.notEmpty(userType, "用户类型不能为空"); Assert.notNull(userType, "用户类型不能为空");
PayWalletTransactionDO transaction = wallService.orderPay(Long.valueOf(userId), Integer.valueOf(userType), PayWalletTransactionDO transaction = wallService.orderPay(userId, userType, reqDTO.getOutTradeNo(),
reqDTO.getOutTradeNo(), reqDTO.getPrice()); reqDTO.getPrice());
return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(), return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
transaction.getCreateTime(), transaction.getCreateTime(),
reqDTO.getOutTradeNo(), transaction); reqDTO.getOutTradeNo(), transaction);
@ -92,10 +92,10 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
@Override @Override
protected PayOrderRespDTO doGetOrder(String outTradeNo) { protected PayOrderRespDTO doGetOrder(String outTradeNo) {
if (payOrderService == null) { if (orderService == null) {
payOrderService = SpringUtil.getBean(PayOrderService.class); orderService = SpringUtil.getBean(PayOrderService.class);
} }
PayOrderExtensionDO orderExtension = payOrderService.getOrderExtensionByNo(outTradeNo); PayOrderExtensionDO orderExtension = orderService.getOrderExtensionByNo(outTradeNo);
// 支付交易拓展单不存在, 返回关闭状态 // 支付交易拓展单不存在, 返回关闭状态
if (orderExtension == null) { if (orderExtension == null) {
return PayOrderRespDTO.closedOf(String.valueOf(ORDER_EXTENSION_NOT_FOUND.getCode()), return PayOrderRespDTO.closedOf(String.valueOf(ORDER_EXTENSION_NOT_FOUND.getCode()),
@ -147,10 +147,10 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
@Override @Override
protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) { protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
if (payRefundService == null) { if (refundService == null) {
payRefundService = SpringUtil.getBean(PayRefundService.class); refundService = SpringUtil.getBean(PayRefundService.class);
} }
PayRefundDO payRefund = payRefundService.getRefundByNo(outRefundNo); PayRefundDO payRefund = refundService.getRefundByNo(outRefundNo);
// 支付退款单不存在, 返回退款失败状态 // 支付退款单不存在, 返回退款失败状态
if (payRefund == null) { if (payRefund == null) {
return PayRefundRespDTO.failureOf(String.valueOf(REFUND_NOT_FOUND), REFUND_NOT_FOUND.getMsg(), return PayRefundRespDTO.failureOf(String.valueOf(REFUND_NOT_FOUND), REFUND_NOT_FOUND.getMsg(),

@ -33,7 +33,7 @@ public interface PayOrderService {
/** /**
* *
* *
* @param appId * @param appId
* @param merchantOrderId * @param merchantOrderId
* @return * @return
*/ */
@ -75,7 +75,7 @@ public interface PayOrderService {
* *
* *
* *
* @param reqVO * @param reqVO
* @param userIp IP * @param userIp IP
* @return * @return
*/ */
@ -93,11 +93,19 @@ public interface PayOrderService {
/** /**
* 退 * 退
* *
* @param id * @param id
* @param incrRefundPrice 退 * @param incrRefundPrice 退
*/ */
void updateOrderRefundPrice(Long id, Integer incrRefundPrice); void updateOrderRefundPrice(Long id, Integer incrRefundPrice);
/**
*
*
* @param payOrderId
* @param payPrice
*/
void updatePayOrderPriceById(Long payOrderId, Integer payPrice);
/** /**
* *
* *

@ -411,6 +411,18 @@ public class PayOrderServiceImpl implements PayOrderService {
} }
} }
@Override
public void updatePayOrderPriceById(Long payOrderId, Integer payPrice) {
// TODO @puhui999不能直接这样修改哈应该只有未支付状态的订单才可以改另外如果价格如果没变可以直接 return 哈;
PayOrderDO order = orderMapper.selectById(payOrderId);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
order.setPrice(payPrice);
orderMapper.updateById(order);
}
@Override @Override
public PayOrderExtensionDO getOrderExtension(Long id) { public PayOrderExtensionDO getOrderExtension(Long id) {
return orderExtensionMapper.selectById(id); return orderExtensionMapper.selectById(id);

@ -12,12 +12,14 @@ import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
public interface PayWalletService { public interface PayWalletService {
/** /**
* *
*
*
* *
* @param userId * @param userId
* @param userType * @param userType
*/ */
PayWalletDO getOrCreatePayWallet(Long userId, Integer userType); PayWalletDO getOrCreateWallet(Long userId, Integer userType);
/** /**
* *
@ -29,6 +31,14 @@ public interface PayWalletService {
*/ */
PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price); PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price);
/**
* 退
*
* @param outRefundNo 退
* @param refundPrice 退
* @param reason 退
*/
PayWalletTransactionDO orderRefund(String outRefundNo, Integer refundPrice, String reason);
/** /**
* *
@ -43,7 +53,6 @@ public interface PayWalletService {
PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType, PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,
Long bizId, PayWalletBizTypeEnum bizType, Integer price); Long bizId, PayWalletBizTypeEnum bizType, Integer price);
/** /**
* *
* *
@ -57,13 +66,4 @@ public interface PayWalletService {
PayWalletTransactionDO addWalletBalance(Long userId, Integer userType, PayWalletTransactionDO addWalletBalance(Long userId, Integer userType,
Long bizId, PayWalletBizTypeEnum bizType, Integer price); Long bizId, PayWalletBizTypeEnum bizType, Integer price);
/**
* 退
*
* @param outRefundNo 退
* @param refundPrice 退
* @param reason 退
*/
PayWalletTransactionDO orderRefund(String outRefundNo, Integer refundPrice, String reason);
} }

@ -6,10 +6,10 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper; import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper;
import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -18,7 +18,6 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.TOO_MANY_REQUESTS;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT; import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT;
@ -33,135 +32,55 @@ import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYM
@Slf4j @Slf4j
public class PayWalletServiceImpl implements PayWalletService { public class PayWalletServiceImpl implements PayWalletService {
/**
* no
*/
private static final String WALLET_PAY_NO_PREFIX = "WP";
/**
* 退 no
*/
private static final String WALLET_REFUND_NO_PREFIX = "WR";
@Resource
private PayWalletMapper payWalletMapper;
@Resource @Resource
private PayNoRedisDAO noRedisDAO; private PayWalletMapper walletMapper;
@Resource @Resource
private PayWalletTransactionService payWalletTransactionService; private PayWalletTransactionService walletTransactionService;
@Resource @Resource
@Lazy @Lazy
private PayOrderService payOrderService; private PayOrderService orderService;
@Resource @Resource
@Lazy @Lazy
private PayRefundService payRefundService; private PayRefundService refundService;
@Override @Override
public PayWalletDO getOrCreatePayWallet(Long userId, Integer userType) { public PayWalletDO getOrCreateWallet(Long userId, Integer userType) {
PayWalletDO payWalletDO = payWalletMapper.selectByUserIdAndType(userId, userType); PayWalletDO wallet = walletMapper.selectByUserIdAndType(userId, userType);
if (payWalletDO == null) { if (wallet == null) {
payWalletDO = new PayWalletDO(); wallet = new PayWalletDO().setUserId(userId).setUserType(userType)
payWalletDO.setUserId(userId); .setBalance(0).setTotalExpense(0).setTotalRecharge(0);
payWalletDO.setUserType(userType); wallet.setCreateTime(LocalDateTime.now());
payWalletDO.setBalance(0); walletMapper.insert(wallet);
payWalletDO.setTotalExpense(0L);
payWalletDO.setTotalRecharge(0L);
payWalletDO.setCreateTime(LocalDateTime.now());
payWalletMapper.insert(payWalletDO);
} }
return payWalletDO; return wallet;
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price) { public PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price) {
// 判断支付交易拓展单是否存 // 1. 判断支付交易拓展单是否存
PayOrderExtensionDO orderExtension = payOrderService.getOrderExtensionByNo(outTradeNo); PayOrderExtensionDO orderExtension = orderService.getOrderExtensionByNo(outTradeNo);
if (orderExtension == null) { if (orderExtension == null) {
throw exception(ORDER_EXTENSION_NOT_FOUND); throw exception(ORDER_EXTENSION_NOT_FOUND);
} }
// 2. 扣减余额
return reduceWalletBalance(userId, userType, orderExtension.getOrderId(), PAYMENT, price); return reduceWalletBalance(userId, userType, orderExtension.getOrderId(), PAYMENT, price);
} }
@Override
public PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,
Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
// 1.1 获取钱包
PayWalletDO payWallet = getOrCreatePayWallet(userId, userType);
// 1.2 判断余额是否足够
int afterBalance = payWallet.getBalance() - price;
if (afterBalance < 0) {
throw exception(WALLET_BALANCE_NOT_ENOUGH);
}
// 2.1 扣除余额
int number = payWalletMapper.updateWhenDecBalance(bizType,payWallet.getBalance(), payWallet.getTotalRecharge(),
payWallet.getTotalExpense(), price, payWallet.getId());
if (number == 0) {
throw exception(TOO_MANY_REQUESTS);
}
// 2.2 生成钱包流水
String walletNo = generateWalletNo(bizType);
PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
.setNo(walletNo).setPrice(-price).setBalance(afterBalance)
.setBizId(String.valueOf(bizId)).setBizType(bizType.getType()).setTitle(bizType.getDescription());
payWalletTransactionService.createWalletTransaction(walletTransaction);
return walletTransaction;
}
@Override
public PayWalletTransactionDO addWalletBalance(Long userId, Integer userType, Long bizId,
PayWalletBizTypeEnum bizType, Integer price) {
// 1.1 获取钱包
PayWalletDO payWallet = getOrCreatePayWallet(userId, userType);
// 2.1 增加余额
int number = payWalletMapper.updateWhenIncBalance(bizType, payWallet.getBalance(), payWallet.getTotalRecharge(),
payWallet.getTotalExpense(), price, payWallet.getId());
if (number == 0) {
throw exception(TOO_MANY_REQUESTS);
}
// 2.2 生成钱包流水
String walletNo = generateWalletNo(bizType);
PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
.setNo(walletNo).setPrice(price).setBalance(payWallet.getBalance()+price)
.setBizId(String.valueOf(bizId)).setBizType(bizType.getType())
.setTitle(bizType.getDescription());
payWalletTransactionService.createWalletTransaction(newWalletTransaction);
return newWalletTransaction;
}
private String generateWalletNo(PayWalletBizTypeEnum bizType) {
String no = "";
switch(bizType){
case PAYMENT :
no = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
break;
case PAYMENT_REFUND :
no = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
break;
default :
// TODO 待增加
}
return no;
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public PayWalletTransactionDO orderRefund(String outRefundNo, Integer refundPrice, String reason) { public PayWalletTransactionDO orderRefund(String outRefundNo, Integer refundPrice, String reason) {
// 1.1 判断退款单是否存在 // 1.1 判断退款单是否存在
PayRefundDO payRefund = payRefundService.getRefundByNo(outRefundNo); PayRefundDO payRefund = refundService.getRefundByNo(outRefundNo);
if (payRefund == null) { if (payRefund == null) {
throw exception(REFUND_NOT_FOUND); throw exception(REFUND_NOT_FOUND);
} }
// 1.2 校验是否可以退款 // 1.2 校验是否可以退款
Long walletId = validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), refundPrice); Long walletId = validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), refundPrice);
PayWalletDO wallet = walletMapper.selectById(walletId);
PayWalletDO payWallet = payWalletMapper.selectById(walletId); Assert.notNull(wallet, "钱包 {} 不存在", walletId);
Assert.notNull(payWallet, "钱包 {} 不存在", walletId); // 2. 增加余额
return addWalletBalance(payWallet.getUserId(), payWallet.getUserType(),payRefund.getId(), PAYMENT_REFUND, refundPrice); return addWalletBalance(wallet.getUserId(), wallet.getUserType(), payRefund.getId(), PAYMENT_REFUND, refundPrice);
} }
/** /**
@ -171,21 +90,76 @@ public class PayWalletServiceImpl implements PayWalletService {
* @param walletPayNo no * @param walletPayNo no
*/ */
private Long validateWalletCanRefund(Long refundId, String walletPayNo, Integer refundPrice) { private Long validateWalletCanRefund(Long refundId, String walletPayNo, Integer refundPrice) {
// 查询钱包支付交易 // 1. 校验钱包支付交易存在
PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getWalletTransactionByNo(walletPayNo); PayWalletTransactionDO walletTransaction = walletTransactionService.getWalletTransactionByNo(walletPayNo);
if (payWalletTransaction == null) { if (walletTransaction == null) {
throw exception(WALLET_TRANSACTION_NOT_FOUND); throw exception(WALLET_TRANSACTION_NOT_FOUND);
} }
// 原来的支付金额 // 原来的支付金额
int amount = - payWalletTransaction.getPrice(); // TODO @jason应该允许多次退款哈
int amount = - walletTransaction.getPrice();
if (refundPrice != amount) { if (refundPrice != amount) {
throw exception(WALLET_REFUND_AMOUNT_ERROR); throw exception(WALLET_REFUND_AMOUNT_ERROR);
} }
PayWalletTransactionDO refundTransaction = payWalletTransactionService.getWalletTransaction( PayWalletTransactionDO refundTransaction = walletTransactionService.getWalletTransaction(
String.valueOf(refundId), PAYMENT_REFUND); String.valueOf(refundId), PAYMENT_REFUND);
if (refundTransaction != null) { if (refundTransaction != null) {
throw exception(WALLET_REFUND_EXIST); throw exception(WALLET_REFUND_EXIST);
} }
return payWalletTransaction.getWalletId(); return walletTransaction.getWalletId();
}
@Override
public PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,
Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
// 1. 获取钱包
PayWalletDO payWallet = getOrCreateWallet(userId, userType);
// 2.1 扣除余额
int updateCounts = 0 ;
switch (bizType) {
case PAYMENT: {
updateCounts = walletMapper.updateWhenConsumption(price, payWallet.getId());
break;
}
case RECHARGE_REFUND: {
// TODO
break;
}
}
if (updateCounts == 0) {
throw exception(WALLET_BALANCE_NOT_ENOUGH);
}
// 2.2 生成钱包流水
Integer afterBalance = payWallet.getBalance() - price;
CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
.setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId))
.setBizType(bizType.getType()).setTitle(bizType.getDescription());
return walletTransactionService.createWalletTransaction(bo);
} }
@Override
public PayWalletTransactionDO addWalletBalance(Long userId, Integer userType,
Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
// 1. 获取钱包
PayWalletDO payWallet = getOrCreateWallet(userId, userType);
switch (bizType) {
case PAYMENT_REFUND: {
// 更新退款
walletMapper.updateWhenConsumptionRefund(price, payWallet.getId());
break;
}
case RECHARGE: {
//TODO
break;
}
}
// 2. 生成钱包流水
CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
.setPrice(price).setBalance(payWallet.getBalance()+price).setBizId(String.valueOf(bizId))
.setBizType(bizType.getType()).setTitle(bizType.getDescription());
return walletTransactionService.createWalletTransaction(bo);
}
} }

@ -4,6 +4,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO; import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
import javax.validation.Valid;
/** /**
* Service * Service
@ -25,10 +28,10 @@ public interface PayWalletTransactionService {
/** /**
* *
* *
* @param payWalletTransaction * @param bo bo
* @return id * @return do
*/ */
Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction); PayWalletTransactionDO createWalletTransaction(@Valid CreateWalletTransactionBO bo);
/** /**
* no * no
@ -45,4 +48,5 @@ public interface PayWalletTransactionService {
* @return * @return
*/ */
PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type); PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type);
} }

@ -2,10 +2,13 @@ package cn.iocoder.yudao.module.pay.service.wallet;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO; import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletTransactionConvert;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper; import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper;
import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -20,22 +23,31 @@ import javax.annotation.Resource;
@Slf4j @Slf4j
public class PayWalletTransactionServiceImpl implements PayWalletTransactionService { public class PayWalletTransactionServiceImpl implements PayWalletTransactionService {
/**
* no
*/
private static final String WALLET_NO_PREFIX = "W";
@Resource @Resource
private PayWalletService payWalletService; private PayWalletService payWalletService;
@Resource @Resource
private PayWalletTransactionMapper payWalletTransactionMapper; private PayWalletTransactionMapper payWalletTransactionMapper;
@Resource
private PayNoRedisDAO noRedisDAO;
@Override @Override
public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType, public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
AppPayWalletTransactionPageReqVO pageVO) { AppPayWalletTransactionPageReqVO pageVO) {
PayWalletDO wallet = payWalletService.getOrCreatePayWallet(userId, userType); PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType);
return payWalletTransactionMapper.selectPage(wallet.getId(), pageVO); return payWalletTransactionMapper.selectPage(wallet.getId(), pageVO);
} }
@Override @Override
public Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction) { public PayWalletTransactionDO createWalletTransaction(CreateWalletTransactionBO bo) {
payWalletTransactionMapper.insert(payWalletTransaction); PayWalletTransactionDO transaction = PayWalletTransactionConvert.INSTANCE.convert(bo)
return payWalletTransaction.getId(); .setNo(noRedisDAO.generate(WALLET_NO_PREFIX));
payWalletTransactionMapper.insert(transaction);
return transaction;
} }
@Override @Override
@ -47,4 +59,5 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
public PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type) { public PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type) {
return payWalletTransactionMapper.selectByBiz(bizId, type.getType()); return payWalletTransactionMapper.selectByBiz(bizId, type.getType());
} }
} }

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.pay.service.wallet.bo;
import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
import lombok.Data;
/**
* BO
*
* @author jason
*/
@Data
public class CreateWalletTransactionBO {
// TODO @jasonbo 的话,最好加个参数校验哈;
/**
*
*
*/
private Long walletId;
/**
*
*
*
*/
private Integer price;
/**
*
*/
private Integer balance;
/**
*
*
* {@link PayWalletBizTypeEnum#getType()}
*/
private Integer bizType;
/**
*
*/
private String bizId;
/**
*
*/
private String title;
}

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.api.social;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.api.social.dto.SocialUserUnbindReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
@ -27,8 +28,9 @@ public interface SocialUserApi {
* *
* *
* @param reqDTO * @param reqDTO
* @return openid
*/ */
void bindSocialUser(@Valid SocialUserBindReqDTO reqDTO); String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
/** /**
* *
@ -38,16 +40,17 @@ public interface SocialUserApi {
void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO); void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO);
/** /**
* *
* MemberUser AdminUser id *
* {@link ServiceException} * {@link ServiceException}
* *
* @param userType * @param userType
* @param type * @param type
* @param code * @param code
* @param state state * @param state state
* @return * @return
*/ */
Long getBindUserId(Integer userType, Integer type, String code, String state); SocialUserRespDTO getSocialUser(Integer userType, Integer type,
String code, String state);
} }

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.system.api.social.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Response DTO
*
* @author
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SocialUserRespDTO {
/**
* openid
*/
private String openid;
/**
*
*/
private Long userId;
}

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.system.api.social; package cn.iocoder.yudao.module.system.api.social;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.api.social.dto.SocialUserUnbindReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
import cn.iocoder.yudao.module.system.service.social.SocialUserService; import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -26,8 +27,8 @@ public class SocialUserApiImpl implements SocialUserApi {
} }
@Override @Override
public void bindSocialUser(SocialUserBindReqDTO reqDTO) { public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
socialUserService.bindSocialUser(reqDTO); return socialUserService.bindSocialUser(reqDTO);
} }
@Override @Override
@ -37,8 +38,8 @@ public class SocialUserApiImpl implements SocialUserApi {
} }
@Override @Override
public Long getBindUserId(Integer userType, Integer type, String code, String state) { public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
return socialUserService.getBindUserId(userType, type, code, state); return socialUserService.getSocialUser(userType, type, code, state);
} }
} }

@ -9,6 +9,7 @@ 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.logger.dto.LoginLogCreateReqDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.*; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
@ -155,14 +156,14 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@Override @Override
public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) {
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), SocialUserRespDTO socialUser = socialUserService.getSocialUser(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
reqVO.getCode(), reqVO.getState()); reqVO.getCode(), reqVO.getState());
if (userId == null) { if (socialUser == null) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND); throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
} }
// 获得用户 // 获得用户
AdminUserDO user = userService.getUser(userId); AdminUserDO user = userService.getUser(socialUser.getUserId());
if (user == null) { if (user == null) {
throw exception(USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.social;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.dal.dataobject.social.SocialUserDO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
@ -50,8 +51,9 @@ public interface SocialUserService {
* *
* *
* @param reqDTO * @param reqDTO
* @return openid
*/ */
void bindSocialUser(@Valid SocialUserBindReqDTO reqDTO); String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
/** /**
* *
@ -64,15 +66,16 @@ public interface SocialUserService {
void unbindSocialUser(Long userId, Integer userType, Integer type, String openid); void unbindSocialUser(Long userId, Integer userType, Integer type, String openid);
/** /**
* *
* MemberUser AdminUser id *
* {@link ServiceException} * {@link ServiceException}
* *
* @param userType * @param userType
* @param type * @param type
* @param code * @param code
* @param state state * @param state state
* @return * @return
*/ */
Long getBindUserId(Integer userType, Integer type, String code, String state); SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state);
} }

@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory; import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.dal.dataobject.social.SocialUserBindDO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
@ -98,7 +99,7 @@ public class SocialUserServiceImpl implements SocialUserService {
@Override @Override
@Transactional @Transactional
public void bindSocialUser(SocialUserBindReqDTO reqDTO) { public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
// 获得社交用户 // 获得社交用户
SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState()); SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState());
Assert.notNull(socialUser, "社交用户不能为空"); Assert.notNull(socialUser, "社交用户不能为空");
@ -115,6 +116,7 @@ public class SocialUserServiceImpl implements SocialUserService {
.userId(reqDTO.getUserId()).userType(reqDTO.getUserType()) .userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
.socialUserId(socialUser.getId()).socialType(socialUser.getType()).build(); .socialUserId(socialUser.getId()).socialType(socialUser.getType()).build();
socialUserBindMapper.insert(socialUserBind); socialUserBindMapper.insert(socialUserBind);
return socialUser.getOpenid();
} }
@Override @Override
@ -130,7 +132,7 @@ public class SocialUserServiceImpl implements SocialUserService {
} }
@Override @Override
public Long getBindUserId(Integer userType, Integer type, String code, String state) { public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
// 获得社交用户 // 获得社交用户
SocialUserDO socialUser = authSocialUser(type, code, state); SocialUserDO socialUser = authSocialUser(type, code, state);
Assert.notNull(socialUser, "社交用户不能为空"); Assert.notNull(socialUser, "社交用户不能为空");
@ -141,7 +143,7 @@ public class SocialUserServiceImpl implements SocialUserService {
if (socialUserBind == null) { if (socialUserBind == null) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND); throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
} }
return socialUserBind.getUserId(); return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId());
} }
/** /**

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.*; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
@ -235,8 +236,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class); AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
// mock 方法(绑定的用户编号) // mock 方法(绑定的用户编号)
Long userId = 1L; Long userId = 1L;
when(socialUserService.getBindUserId(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()), when(socialUserService.getSocialUser(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(userId); eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), userId));
// mock用户 // mock用户
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)); AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
when(userService.getUser(eq(userId))).thenReturn(user); when(userService.getUser(eq(userId))).thenReturn(user);

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory; import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.dal.dataobject.social.SocialUserBindDO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
@ -195,10 +196,11 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
.setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId())); .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId()));
// 调用 // 调用
socialUserService.bindSocialUser(reqDTO); String openid = socialUserService.bindSocialUser(reqDTO);
// 断言 // 断言
List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList(); List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList();
assertEquals(1, socialUserBinds.size()); assertEquals(1, socialUserBinds.size());
assertEquals(socialUser.getOpenid(), openid);
} }
@Test @Test
@ -232,25 +234,26 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetBindUserId() { public void testGetSocialUser() {
// 准备参数 // 准备参数
Integer userType = UserTypeEnum.ADMIN.getValue(); Integer userType = UserTypeEnum.ADMIN.getValue();
Integer type = SocialTypeEnum.GITEE.getType(); Integer type = SocialTypeEnum.GITEE.getType();
String code = "tudou"; String code = "tudou";
String state = "yuanma"; String state = "yuanma";
// mock 社交用户 // mock 社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state); SocialUserDO socialUserDO = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
socialUserMapper.insert(socialUser); socialUserMapper.insert(socialUserDO);
// mock 社交用户的绑定 // mock 社交用户的绑定
Long userId = randomLong(); Long userId = randomLong();
SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId) SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId)
.setSocialType(type).setSocialUserId(socialUser.getId()); .setSocialType(type).setSocialUserId(socialUserDO.getId());
socialUserBindMapper.insert(socialUserBind); socialUserBindMapper.insert(socialUserBind);
// 调用 // 调用
Long result = socialUserService.getBindUserId(userType, type, code, state); SocialUserRespDTO socialUser = socialUserService.getSocialUser(userType, type, code, state);
// 断言 // 断言
assertEquals(userId, result); assertEquals(userId, socialUser.getUserId());
assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid());
} }
} }

@ -173,8 +173,10 @@ wx:
key-prefix: wx # Redis Key 的前缀 key-prefix: wx # Redis Key 的前缀
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
appid: wx62056c0d5e8db250 # appid: wx62056c0d5e8db250
secret: 333ae72f41552af1e998fe1f54e1584a # secret: 333ae72f41552af1e998fe1f54e1584a
appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
secret: 6f270509224a7ae1296bbf1c8cb97aed
config-storage: config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取 type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀 key-prefix: wa # Redis Key 的前缀

Loading…
Cancel
Save