mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-09-08 22:21:54 +08:00
Merge branch 'feature/mall_product' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
Conflicts: yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
This commit is contained in:
@@ -60,4 +60,14 @@ public class ProductSkuRespDTO {
|
||||
*/
|
||||
private Double volume;
|
||||
|
||||
// TODO @puhui:这 2 字段,需要改下;firstBrokerageRecord、secondBrokerageRecord;和分佣保持一致;
|
||||
/**
|
||||
* 一级分销的佣金,单位:分
|
||||
*/
|
||||
private Integer subCommissionFirstPrice;
|
||||
/**
|
||||
* 二级分销的佣金,单位:分
|
||||
*/
|
||||
private Integer subCommissionSecondPrice;
|
||||
|
||||
}
|
||||
|
@@ -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 javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
// TODO @芋艿:这块要在看看
|
||||
@@ -40,17 +39,7 @@ public class BargainRecordCreateReqDTO {
|
||||
*/
|
||||
@NotNull(message = "订单编号不能为空")
|
||||
private Long orderId;
|
||||
// TODO @puhui999:spuName、picUrl、 之类字段不用传递;
|
||||
/**
|
||||
* 商品名字
|
||||
*/
|
||||
@NotEmpty(message = "商品名字不能为空")
|
||||
private String spuName;
|
||||
/**
|
||||
* 商品图片
|
||||
*/
|
||||
@NotEmpty(message = "商品图片不能为空")
|
||||
private String picUrl;
|
||||
|
||||
/**
|
||||
* 砍价商品单价
|
||||
*/
|
||||
@@ -61,17 +50,7 @@ public class BargainRecordCreateReqDTO {
|
||||
*/
|
||||
@NotNull(message = "商品原价不能为空")
|
||||
private Integer price;
|
||||
// TODO @puhui999:nickname、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.CombinationRecordRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
// TODO @芋艿:后面也再撸撸这几个接口
|
||||
@@ -51,13 +51,29 @@ public interface CombinationRecordApi {
|
||||
*/
|
||||
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 updateCombinationRecordStatus(CombinationRecordUpdateStatusReqDTO reqDTO);
|
||||
void updateRecordStatusToFailed(Long userId, Long orderId);
|
||||
|
||||
/**
|
||||
* 更新拼团状态为 进行中
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param orderId 订单编号
|
||||
* @param startTime 开始时间
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -55,6 +55,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_008_003, "秒杀活动已关闭,不能修改");
|
||||
ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_008_004, "秒杀活动未关闭或未结束,不能删除");
|
||||
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_008_005, "秒杀活动已关闭,不能重复关闭");
|
||||
ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "更新秒杀活动库存失败,原因秒杀库存不足");
|
||||
|
||||
// ========== 秒杀时段 1-013-009-000 ==========
|
||||
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1_013_009_000, "秒杀时段不存在");
|
||||
|
@@ -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.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.enums.combination.CombinationRecordStatusEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -43,12 +43,19 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCombinationRecordStatus(CombinationRecordUpdateStatusReqDTO reqDTO) {
|
||||
if (null == reqDTO.getStartTime()) {
|
||||
recordService.updateCombinationRecordStatusByUserIdAndOrderId(reqDTO);
|
||||
} else {
|
||||
recordService.updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(reqDTO);
|
||||
}
|
||||
public void updateRecordStatusToSuccess(Long userId, Long orderId) {
|
||||
recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.SUCCESS.getStatus(), userId, orderId);
|
||||
}
|
||||
|
||||
@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 @puhui999:productDOList 可以简化成 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);
|
||||
}
|
||||
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.app.combination;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO;
|
||||
@@ -16,8 +16,8 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
@@ -52,14 +52,13 @@ public class AppCombinationRecordController {
|
||||
@RequestParam(value = "activityId", required = false) Long activityId,
|
||||
@RequestParam("status") Integer status,
|
||||
@RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) {
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
List<AppCombinationRecordRespVO> list = new ArrayList<>();
|
||||
for (int i = 1; i <= count; i++) {
|
||||
AppCombinationRecordRespVO record = new AppCombinationRecordRespVO();
|
||||
record.setId((long) i);
|
||||
record.setNickname("用户" + i);
|
||||
record.setAvatar("头像" + i);
|
||||
record.setExpireTime(LocalDateTime.ofInstant(new Date().toInstant(), zoneId));
|
||||
record.setExpireTime(LocalDateTime.now());
|
||||
record.setUserSize(10);
|
||||
record.setUserCount(i);
|
||||
record.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
|
||||
@@ -74,14 +73,13 @@ public class AppCombinationRecordController {
|
||||
@Operation(summary = "获得拼团记录明细")
|
||||
@Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
|
||||
public CommonResult<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam("id") Long id) {
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
AppCombinationRecordDetailRespVO detail = new AppCombinationRecordDetailRespVO();
|
||||
// 团长
|
||||
AppCombinationRecordRespVO headRecord = new AppCombinationRecordRespVO();
|
||||
headRecord.setId(1L);
|
||||
headRecord.setNickname("用户" + 1);
|
||||
headRecord.setAvatar("头像" + 1);
|
||||
headRecord.setExpireTime((LocalDateTime.ofInstant(DateUtils.addTime(Duration.ofDays(1)).toInstant(), zoneId)));
|
||||
headRecord.setExpireTime(LocalDateTimeUtils.addTime(Duration.ofDays(1)));
|
||||
headRecord.setUserSize(10);
|
||||
headRecord.setUserCount(3);
|
||||
headRecord.setStatus(1);
|
||||
@@ -96,7 +94,7 @@ public class AppCombinationRecordController {
|
||||
record.setId((long) i);
|
||||
record.setNickname("用户" + i);
|
||||
record.setAvatar("头像" + i);
|
||||
record.setExpireTime(LocalDateTime.ofInstant(new Date().toInstant(), zoneId));
|
||||
record.setExpireTime(LocalDateTime.now());
|
||||
record.setUserSize(10);
|
||||
record.setUserCount(i);
|
||||
record.setStatus(1);
|
||||
|
@@ -9,6 +9,7 @@ import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// TODO 芋艿:把字段的顺序,和 do 顺序对齐下
|
||||
/**
|
||||
* 拼团记录 DO
|
||||
*
|
||||
@@ -27,34 +28,28 @@ import java.time.LocalDateTime;
|
||||
@AllArgsConstructor
|
||||
public class CombinationRecordDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号,主键自增
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 拼团活动编号
|
||||
*
|
||||
* 关联 {@link CombinationActivityDO#getId()}
|
||||
*/
|
||||
private Long activityId;
|
||||
/**
|
||||
* 拼团商品单价
|
||||
*
|
||||
* 冗余 {@link CombinationProductDO#getCombinationPrice()}
|
||||
*/
|
||||
private Integer combinationPrice;
|
||||
/**
|
||||
* SPU 编号
|
||||
*/
|
||||
private Long spuId;
|
||||
/**
|
||||
* SKU 编号
|
||||
*/
|
||||
private Long skuId;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
private Long orderId;
|
||||
/**
|
||||
* 团长编号
|
||||
*
|
||||
* 关联 {@link CombinationRecordDO#getId()}
|
||||
*/
|
||||
private Long headId;
|
||||
/**
|
||||
* 商品名字
|
||||
*/
|
||||
@@ -64,9 +59,14 @@ public class CombinationRecordDO extends BaseDO {
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 拼团商品单价
|
||||
* SKU 编号
|
||||
*/
|
||||
private Integer combinationPrice;
|
||||
private Long skuId;
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
@@ -75,6 +75,13 @@ public class CombinationRecordDO extends BaseDO {
|
||||
* 用户头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 团长编号
|
||||
*
|
||||
* 关联 {@link CombinationRecordDO#getId()}
|
||||
*/
|
||||
private Long headId;
|
||||
/**
|
||||
* 开团状态
|
||||
*
|
||||
@@ -82,23 +89,9 @@ public class CombinationRecordDO extends BaseDO {
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 是否虚拟成团
|
||||
* 订单编号
|
||||
*/
|
||||
private Boolean virtualGroup;
|
||||
/**
|
||||
* 过期时间,单位:小时
|
||||
*
|
||||
* 关联 {@link CombinationActivityDO#getLimitDuration()}
|
||||
*/
|
||||
private Integer expireTime;
|
||||
/**
|
||||
* 开始时间 (订单付款后开始的时间)
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
/**
|
||||
* 结束时间(成团时间/失败时间)
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
private Long orderId;
|
||||
/**
|
||||
* 开团需要人数
|
||||
*
|
||||
@@ -109,5 +102,24 @@ public class CombinationRecordDO extends BaseDO {
|
||||
* 已加入拼团人数
|
||||
*/
|
||||
private Integer userCount;
|
||||
/**
|
||||
* 是否虚拟成团
|
||||
*/
|
||||
private Boolean virtualGroup;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*
|
||||
* 基于 {@link CombinationRecordDO#getStartTime()} + {@link CombinationActivityDO#getLimitDuration()} 计算
|
||||
*/
|
||||
private LocalDateTime expireTime;
|
||||
/**
|
||||
* 开始时间 (订单付款后开始的时间)
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
/**
|
||||
* 结束时间(成团时间/失败时间)
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
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.CombinationRecordUpdateStatusReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
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.util.ObjectUtil;
|
||||
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.dal.dataobject.combination.CombinationActivityDO;
|
||||
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.*;
|
||||
|
||||
// TODO 芋艿:等拼团记录做完,完整 review 下
|
||||
|
||||
/**
|
||||
* 拼团记录 Service 实现类
|
||||
*
|
||||
@@ -38,27 +38,27 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
|
||||
@Override
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) {
|
||||
CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId());
|
||||
public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime) {
|
||||
CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
|
||||
// 更新状态
|
||||
recordDO.setStatus(reqDTO.getStatus());
|
||||
recordDO.setStatus(status);
|
||||
// 更新开始时间
|
||||
recordDO.setStartTime(reqDTO.getStartTime());
|
||||
recordDO.setStartTime(startTime);
|
||||
recordMapper.updateById(recordDO);
|
||||
|
||||
// 更新拼团参入人数
|
||||
List<CombinationRecordDO> recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), reqDTO.getStatus());
|
||||
List<CombinationRecordDO> recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), status);
|
||||
if (CollUtil.isNotEmpty(recordDOs)) {
|
||||
recordDOs.forEach(item -> {
|
||||
item.setUserCount(recordDOs.size());
|
||||
@@ -115,8 +115,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
// 2. 创建拼团记录
|
||||
CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO);
|
||||
record.setVirtualGroup(false);
|
||||
// TODO @puhui999:过期时间,应该是 Date 哈;
|
||||
record.setExpireTime(activity.getLimitDuration());
|
||||
record.setExpireTime(record.getStartTime().plusHours(activity.getLimitDuration()));
|
||||
record.setUserSize(activity.getUserSize());
|
||||
recordMapper.insert(record);
|
||||
}
|
||||
|
@@ -33,6 +33,20 @@ public interface SeckillActivityService {
|
||||
*/
|
||||
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. 校验秒杀时段是否存在
|
||||
* 2. 秒杀商品是否参加其它活动
|
||||
*
|
||||
* @param configIds 秒杀时段数组
|
||||
* @param spuId 商品 SPU 编号
|
||||
* @param configIds 秒杀时段数组
|
||||
* @param spuId 商品 SPU 编号
|
||||
* @param activityId 秒杀活动编号
|
||||
*/
|
||||
private void validateProductConflict(List<Long> configIds, Long spuId, Long activityId) {
|
||||
@@ -92,15 +92,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
if (activityId != null) { // 排除自己
|
||||
activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));
|
||||
}
|
||||
// TODO @puhui999:一个 spu,参与两个活动应该没关系,关键是活动时间不充能重叠;
|
||||
// 2.2 过滤出所有 spuId 有交集的活动,判断是否存在重叠
|
||||
List<SeckillActivityDO> activityDOs1 = filterList(activityList, s -> ObjectUtil.equal(s.getSpuId(), spuId));
|
||||
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)) {
|
||||
// 2.2 过滤出所有 configIds 有交集的活动,判断是否存在重叠
|
||||
List<SeckillActivityDO> conflictActivityList = filterList(activityList, s -> containsAny(s.getConfigIds(), configIds));
|
||||
if (isNotEmpty(conflictActivityList)) {
|
||||
throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +102,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
/**
|
||||
* 校验秒杀商品是否都存在
|
||||
*
|
||||
* @param spuId 商品 SPU 编号
|
||||
* @param spuId 商品 SPU 编号
|
||||
* @param products 秒杀商品
|
||||
*/
|
||||
private void validateProductExists(Long spuId, List<SeckillProductBaseVO> products) {
|
||||
@@ -150,11 +144,21 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
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 products 该活动的最新商品配置
|
||||
* @param products 该活动的最新商品配置
|
||||
*/
|
||||
private void updateSeckillProduct(SeckillActivityDO activity, List<SeckillProductBaseVO> products) {
|
||||
// 第一步,对比新老数据,获得添加、修改、删除的列表
|
||||
|
@@ -1,18 +1,9 @@
|
||||
package cn.iocoder.yudao.module.promotion.util;
|
||||
|
||||
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.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验商品 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.trade.api.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO;
|
||||
|
||||
/**
|
||||
* 分销 API 接口
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
public interface BrokerageApi {
|
||||
|
||||
/**
|
||||
* 获得分销用户
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 分销用户信息
|
||||
*/
|
||||
BrokerageUserDTO getBrokerageUser(Long userId);
|
||||
|
||||
/**
|
||||
* 绑定推广员
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param bindUserId 推广员编号
|
||||
* @param isNewUser 是否为新用户
|
||||
* @return 是否绑定
|
||||
*/
|
||||
boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser);
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package cn.iocoder.yudao.module.trade.api.brokerage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 分销用户 DTO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Data
|
||||
public class BrokerageUserDTO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
* <p>
|
||||
* 对应 MemberUserDO 的 id 字段
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 推广员编号
|
||||
* <p>
|
||||
* 关联 MemberUserDO 的 id 字段
|
||||
*/
|
||||
private Long bindUserId;
|
||||
/**
|
||||
* 推广员绑定时间
|
||||
*/
|
||||
private LocalDateTime bindUserTime;
|
||||
|
||||
/**
|
||||
* 推广资格
|
||||
*/
|
||||
private Boolean brokerageEnabled;
|
||||
/**
|
||||
* 成为分销员时间
|
||||
*/
|
||||
private LocalDateTime brokerageTime;
|
||||
|
||||
/**
|
||||
* 可用佣金
|
||||
*/
|
||||
private Integer price;
|
||||
/**
|
||||
* 冻结佣金
|
||||
*/
|
||||
private Integer frozenPrice;
|
||||
|
||||
}
|
@@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== Order 模块 1-011-000-000 ==========
|
||||
// ========== Order 模块 1-011-000-000 ==========
|
||||
ErrorCode ORDER_CREATE_SKU_NOT_FOUND = new ErrorCode(1_011_000_001, "商品 SKU 不存在");
|
||||
ErrorCode ORDER_CREATE_SPU_NOT_SALE = new ErrorCode(1_011_000_002, "商品 SPU 不可售卖");
|
||||
ErrorCode ORDER_CREATE_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1_011_000_004, "商品 SKU 库存不足");
|
||||
@@ -35,7 +35,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS = new ErrorCode(1_011_000_024, "交易订单发货失败,发货类型不是快递");
|
||||
ErrorCode ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_025, "交易订单取消失败,订单不是【待支付】状态");
|
||||
|
||||
// ========== After Sale 模块 1-011-000-100 ==========
|
||||
// ========== After Sale 模块 1-011-000-100 ==========
|
||||
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
|
||||
ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1_011_000_101, "申请退款金额错误");
|
||||
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1_011_000_102, "订单已关闭,无法申请售后");
|
||||
@@ -50,7 +50,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
|
||||
new ErrorCode(1_011_000_111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
|
||||
|
||||
// ========== Cart 模块 1-011-002-000 ==========
|
||||
// ========== Cart 模块 1-011-002-000 ==========
|
||||
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在");
|
||||
|
||||
// ========== Price 相关 1-011-003-000 ============
|
||||
@@ -58,7 +58,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY = new ErrorCode(1_011_003_001, "计算快递运费异常,收件人地址编号为空");
|
||||
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1_011_003_002, "计算快递运费异常,找不到对应的运费模板");
|
||||
|
||||
// ========== 物流 Express 模块 1-011-004-000 ==========
|
||||
// ========== 物流 Express 模块 1-011-004-000 ==========
|
||||
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在");
|
||||
ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1_011_004_001, "已经存在该编码的快递公司");
|
||||
ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1_011_004_002, "需要接入快递服务商,比如【快递100】");
|
||||
@@ -67,11 +67,21 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1_011_004_101, "快递查询接口异常");
|
||||
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1_011_004_102, "快递查询返回失败,原因:{}");
|
||||
|
||||
// ========== 物流 Template 模块 1-011-005-000 ==========
|
||||
// ========== 物流 Template 模块 1-011-005-000 ==========
|
||||
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1_011_005_000, "已经存在该运费模板名");
|
||||
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_011_005_001, "运费模板不存在");
|
||||
|
||||
// ========== 物流 PICK_UP 模块 1-011-006-000 ==========
|
||||
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1_011_006_000, "自提门店不存在");
|
||||
|
||||
// ========== 分销用户 模块 1011007000 ==========
|
||||
ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1011007000, "分销用户不存在");
|
||||
ErrorCode BROKERAGE_USER_FROZEN_PRICE_NOT_ENOUGH = new ErrorCode(1011007001, "用户冻结佣金({})数量不足");
|
||||
ErrorCode BROKERAGE_BIND_SELF = new ErrorCode(1011007002, "不能绑定自己");
|
||||
ErrorCode BROKERAGE_BIND_USER_NOT_ENABLED = new ErrorCode(1011007003, "绑定用户没有推广资格");
|
||||
ErrorCode BROKERAGE_BIND_CONDITION_ADMIN = new ErrorCode(1011007004, "仅可在后台绑定推广员");
|
||||
ErrorCode BROKERAGE_BIND_MODE_REGISTER = new ErrorCode(1011007005, "只有在注册时可以绑定");
|
||||
ErrorCode BROKERAGE_BIND_OVERRIDE = new ErrorCode(1011007006, "已绑定了推广人");
|
||||
ErrorCode BROKERAGE_BIND_LOOP = new ErrorCode(1011007007, "下级不能绑定自己的上级");
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 分销关系绑定模式枚举
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BrokerageBindModeEnum implements IntArrayValuable {
|
||||
|
||||
/**
|
||||
* 只要用户没有推广人,随时都可以绑定分销关系
|
||||
*/
|
||||
ANYTIME(1, "没有推广人"),
|
||||
/**
|
||||
* 仅新用户注册时才能绑定推广关系
|
||||
*/
|
||||
REGISTER(2, "新用户"),
|
||||
/**
|
||||
* 每次扫码都覆盖
|
||||
*/
|
||||
OVERRIDE(3, "扫码覆盖"),
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageBindModeEnum::getMode).toArray();
|
||||
|
||||
/**
|
||||
* 模式
|
||||
*/
|
||||
private final Integer mode;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 分佣模式枚举
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BrokerageEnabledConditionEnum implements IntArrayValuable {
|
||||
|
||||
/**
|
||||
* 所有用户都可以分销
|
||||
*/
|
||||
ALL(1, "人人分销"),
|
||||
/**
|
||||
* 仅可后台手动设置推广员
|
||||
*/
|
||||
ADMIN(2, "指定分销"),
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageEnabledConditionEnum::getCondition).toArray();
|
||||
|
||||
/**
|
||||
* 模式
|
||||
*/
|
||||
private final Integer condition;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 佣金记录业务类型枚举
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BrokerageRecordBizTypeEnum implements IntArrayValuable {
|
||||
|
||||
ORDER(1, "获得推广佣金", "获得推广佣金 {}", true),
|
||||
WITHDRAW(2, "提现申请", "提现申请扣除佣金 {}", false),
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageRecordBizTypeEnum::getType).toArray();
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private final String title;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String description;
|
||||
/**
|
||||
* 是否为增加佣金
|
||||
*/
|
||||
private final boolean add;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 佣金记录状态枚举
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BrokerageRecordStatusEnum implements IntArrayValuable {
|
||||
|
||||
WAIT_SETTLEMENT(0, "待结算"),
|
||||
SETTLEMENT(1, "已结算"),
|
||||
CANCEL(2, "已取消"),
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageRecordStatusEnum::getStatus).toArray();
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 佣金提现状态枚举
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BrokerageWithdrawStatusEnum implements IntArrayValuable {
|
||||
|
||||
AUDITING(0, "审核中"),
|
||||
AUDIT_SUCCESS(10, "审核通过"),
|
||||
WITHDRAW_SUCCESS(11, "提现成功"),
|
||||
AUDIT_FAIL(20, "审核不通过"),
|
||||
WITHDRAW_FAIL(21, "提现失败"),
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageWithdrawStatusEnum::getStatus).toArray();
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 佣金提现类型枚举
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BrokerageWithdrawTypeEnum implements IntArrayValuable {
|
||||
|
||||
WALLET(1, "钱包"),
|
||||
BANK(2, "银行卡"),
|
||||
WECHAT(3, "微信"),
|
||||
ALIPAY(4, "支付宝"),
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageWithdrawTypeEnum::getType).toArray();
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
@@ -54,6 +54,10 @@
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
|
||||
@@ -82,6 +86,11 @@
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
|
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.trade.api.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO;
|
||||
import cn.iocoder.yudao.module.trade.convert.brokerage.user.BrokerageUserConvert;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 分销 API 接口实现类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class BrokerageApiImpl implements BrokerageApi {
|
||||
|
||||
@Resource
|
||||
private BrokerageUserService brokerageUserService;
|
||||
|
||||
@Override
|
||||
public BrokerageUserDTO getBrokerageUser(Long userId) {
|
||||
return BrokerageUserConvert.INSTANCE.convertDTO(brokerageUserService.getBrokerageUser(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) {
|
||||
return brokerageUserService.bindBrokerageUser(userId, bindUserId, isNewUser);
|
||||
}
|
||||
|
||||
}
|
@@ -76,6 +76,11 @@ public class TradeAfterSaleController {
|
||||
public CommonResult<TradeAfterSaleDetailRespVO> getOrderDetail(@RequestParam("id") Long 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());
|
||||
// 查询订单项
|
||||
@@ -92,7 +97,11 @@ public class TradeAfterSaleController {
|
||||
TradeAfterSaleLogRespDTO respVO = new TradeAfterSaleLogRespDTO();
|
||||
respVO.setId((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.setOrderId((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 = "管理后台 - 交易订单的详情的订单项目")
|
||||
@Data
|
||||
|
@@ -0,0 +1,51 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordRespVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.brokerage.record.BrokerageRecordConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 佣金记录")
|
||||
@RestController
|
||||
@RequestMapping("/trade/brokerage-record")
|
||||
@Validated
|
||||
public class BrokerageRecordController {
|
||||
|
||||
@Resource
|
||||
private BrokerageRecordService brokerageRecordService;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得佣金记录")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')")
|
||||
public CommonResult<BrokerageRecordRespVO> getBrokerageRecord(@RequestParam("id") Integer id) {
|
||||
BrokerageRecordDO brokerageRecord = brokerageRecordService.getBrokerageRecord(id);
|
||||
return success(BrokerageRecordConvert.INSTANCE.convert(brokerageRecord));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得佣金记录分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')")
|
||||
public CommonResult<PageResult<BrokerageRecordRespVO>> getBrokerageRecordPage(@Valid BrokerageRecordPageReqVO pageVO) {
|
||||
PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(pageVO);
|
||||
return success(BrokerageRecordConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
/**
|
||||
* 佣金记录 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class BrokerageRecordBaseVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25973")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23353")
|
||||
@NotEmpty(message = "业务编号不能为空")
|
||||
private String bizId;
|
||||
|
||||
@Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "业务类型不能为空")
|
||||
private Integer bizType;
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "标题不能为空")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "28731")
|
||||
@NotNull(message = "金额不能为空")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "当前总佣金", requiredMode = Schema.RequiredMode.REQUIRED, example = "13226")
|
||||
@NotNull(message = "当前总佣金不能为空")
|
||||
private Integer totalPrice;
|
||||
|
||||
@Schema(description = "说明", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
|
||||
@NotNull(message = "说明不能为空")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "冻结时间(天)不能为空")
|
||||
private Integer frozenDays;
|
||||
|
||||
@Schema(description = "解冻时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime unfreezeTime;
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 佣金记录分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class BrokerageRecordPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "用户编号", example = "25973")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "业务类型", example = "1")
|
||||
private Integer bizType;
|
||||
|
||||
@Schema(description = "状态", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 佣金记录 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class BrokerageRecordRespVO extends BrokerageRecordBaseVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28896")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.convert.brokerage.user.BrokerageUserConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - 分销用户")
|
||||
@RestController
|
||||
@RequestMapping("/trade/brokerage-user")
|
||||
@Validated
|
||||
public class BrokerageUserController {
|
||||
|
||||
@Resource
|
||||
private BrokerageUserService brokerageUserService;
|
||||
@Resource
|
||||
private BrokerageRecordService brokerageRecordService;
|
||||
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@PutMapping("/update-brokerage-user")
|
||||
@Operation(summary = "修改推广员")
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-brokerage-user')")
|
||||
public CommonResult<Boolean> updateBrokerageUser(@Valid @RequestBody BrokerageUserUpdateBrokerageUserReqVO updateReqVO) {
|
||||
brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), updateReqVO.getBindUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/clear-brokerage-user")
|
||||
@Operation(summary = "清除推广员")
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:clear-brokerage-user')")
|
||||
public CommonResult<Boolean> clearBrokerageUser(@Valid @RequestBody BrokerageUserClearBrokerageUserReqVO updateReqVO) {
|
||||
brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), null);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-brokerage-enable")
|
||||
@Operation(summary = "修改推广资格")
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-brokerage-enable')")
|
||||
public CommonResult<Boolean> updateBrokerageEnabled(@Valid @RequestBody BrokerageUserUpdateBrokerageEnabledReqVO updateReqVO) {
|
||||
brokerageUserService.updateBrokerageUserEnabled(updateReqVO.getId(), updateReqVO.getEnabled());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得分销用户")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')")
|
||||
public CommonResult<BrokerageUserRespVO> getBrokerageUser(@RequestParam("id") Long id) {
|
||||
BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(id);
|
||||
return success(BrokerageUserConvert.INSTANCE.convert(brokerageUser));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得分销用户分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')")
|
||||
public CommonResult<PageResult<BrokerageUserRespVO>> getBrokerageUserPage(@Valid BrokerageUserPageReqVO pageVO) {
|
||||
// 分页查询
|
||||
PageResult<BrokerageUserDO> pageResult = brokerageUserService.getBrokerageUserPage(pageVO);
|
||||
|
||||
// 涉及到的用户
|
||||
Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
|
||||
// 查询用户信息
|
||||
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
|
||||
// 合计分佣订单
|
||||
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageRecordService.getUserBrokerageSummaryByUserId(userId,
|
||||
BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus()));
|
||||
// 合计推广用户数量
|
||||
Map<Long, Long> brokerageUserCountMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId));
|
||||
|
||||
// todo 合计提现
|
||||
|
||||
return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap, userOrderSummaryMap));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
/**
|
||||
* 分销用户 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class BrokerageUserBaseVO {
|
||||
|
||||
@Schema(description = "推广员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587")
|
||||
@NotNull(message = "推广员编号不能为空")
|
||||
private Long bindUserId;
|
||||
|
||||
@Schema(description = "推广员绑定时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime bindUserTime;
|
||||
|
||||
@Schema(description = "推广资格", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "推广资格不能为空")
|
||||
private Boolean brokerageEnabled;
|
||||
|
||||
@Schema(description = "成为分销员时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime brokerageTime;
|
||||
|
||||
@Schema(description = "可用佣金", requiredMode = Schema.RequiredMode.REQUIRED, example = "11089")
|
||||
@NotNull(message = "可用佣金不能为空")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "冻结佣金", requiredMode = Schema.RequiredMode.REQUIRED, example = "30916")
|
||||
@NotNull(message = "冻结佣金不能为空")
|
||||
private Integer frozenPrice;
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 分销用户 - 清除推广员 Request VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class BrokerageUserClearBrokerageUserReqVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 分销用户分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class BrokerageUserPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "推广员编号", example = "4587")
|
||||
private Long bindUserId;
|
||||
|
||||
@Schema(description = "推广资格")
|
||||
private Boolean brokerageEnabled;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 分销用户 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class BrokerageUserRespVO extends BrokerageUserBaseVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// ========== 用户信息 ==========
|
||||
|
||||
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
|
||||
private String avatar;
|
||||
@Schema(description = "用户昵称", example = "李四")
|
||||
private String nickname;
|
||||
|
||||
// ========== 推广信息 ==========
|
||||
|
||||
@Schema(description = "推广用户数量(一级)", example = "20019")
|
||||
private Integer brokerageUserCount;
|
||||
@Schema(description = "推广订单数量", example = "20019")
|
||||
private Integer brokerageOrderCount;
|
||||
@Schema(description = "推广订单金额", example = "20019")
|
||||
private Integer brokerageOrderPrice;
|
||||
|
||||
// ========== 提现信息 ==========
|
||||
|
||||
@Schema(description = "已提现金额", example = "20019")
|
||||
private Integer withdrawPrice;
|
||||
@Schema(description = "已提现次数", example = "20019")
|
||||
private Integer withdrawCount;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 分销用户 - 修改推广员 Request VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class BrokerageUserUpdateBrokerageEnabledReqVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "推广资格", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "推广资格不能为空")
|
||||
private Boolean enabled;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 分销用户 - 修改推广员 Request VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class BrokerageUserUpdateBrokerageUserReqVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "推广员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587")
|
||||
@NotNull(message = "推广员编号不能为空")
|
||||
private Long bindUserId;
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.config.TradeConfigConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 交易中心配置")
|
||||
@RestController
|
||||
@RequestMapping("/trade/config")
|
||||
@Validated
|
||||
public class TradeConfigController {
|
||||
|
||||
@Resource
|
||||
private TradeConfigService tradeConfigService;
|
||||
|
||||
@PutMapping("/save")
|
||||
@Operation(summary = "更新交易中心配置")
|
||||
@PreAuthorize("@ss.hasPermission('trade:config:save')")
|
||||
public CommonResult<Boolean> updateConfig(@Valid @RequestBody TradeConfigSaveReqVO updateReqVO) {
|
||||
tradeConfigService.saveTradeConfig(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得交易中心配置")
|
||||
@PreAuthorize("@ss.hasPermission('trade:config:query')")
|
||||
public CommonResult<TradeConfigRespVO> getConfig() {
|
||||
TradeConfigDO config = tradeConfigService.getTradeConfig();
|
||||
return success(TradeConfigConvert.INSTANCE.convert(config));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.config.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.PositiveOrZero;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 交易中心配置 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class TradeConfigBaseVO {
|
||||
|
||||
// ========== 分销相关 ==========
|
||||
|
||||
@Schema(description = "是否启用分佣", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否启用分佣不能为空")
|
||||
private Boolean brokerageEnabled;
|
||||
|
||||
@Schema(description = "分佣模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "分佣模式不能为空")
|
||||
@InEnum(value = BrokerageEnabledConditionEnum.class, message = "分佣模式必须是 {value}")
|
||||
private Integer brokerageEnabledCondition;
|
||||
|
||||
@Schema(description = "分销关系绑定模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "分销关系绑定模式不能为空")
|
||||
@InEnum(value = BrokerageBindModeEnum.class, message = "分销关系绑定模式必须是 {value}")
|
||||
private Integer brokerageBindMode;
|
||||
|
||||
@Schema(description = "分销海报图地址数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/yudao.jpg]")
|
||||
private List<String> brokeragePostUrls;
|
||||
|
||||
@Schema(description = "一级返佣比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "一级返佣比例不能为空")
|
||||
@Range(min = 0, max = 100, message = "一级返佣比例必须在 0 - 100 之间")
|
||||
private Integer brokerageFirstPercent;
|
||||
|
||||
@Schema(description = "二级返佣比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "二级返佣比例不能为空")
|
||||
@Range(min = 0, max = 100, message = "二级返佣比例必须在 0 - 100 之间")
|
||||
private Integer brokerageSecondPercent;
|
||||
|
||||
@Schema(description = "用户提现最低金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
|
||||
@NotNull(message = "用户提现最低金额不能为空")
|
||||
@PositiveOrZero(message = "用户提现最低金额不能是负数")
|
||||
private Integer brokerageWithdrawMinPrice;
|
||||
|
||||
@Schema(description = "提现银行", requiredMode = Schema.RequiredMode.REQUIRED, example = "[0, 1]")
|
||||
@NotEmpty(message = "提现银行不能为空")
|
||||
private List<Integer> brokerageBankNames;
|
||||
|
||||
@Schema(description = "佣金冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED, example = "7")
|
||||
@NotNull(message = "佣金冻结时间(天)不能为空")
|
||||
@PositiveOrZero(message = "佣金冻结时间不能是负数")
|
||||
private Integer brokerageFrozenDays;
|
||||
|
||||
@Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "[0, 1]")
|
||||
@NotNull(message = "提现方式不能为空")
|
||||
@InEnum(value = BrokerageWithdrawTypeEnum.class, message = "提现方式必须是 {value}")
|
||||
private List<Integer> brokerageWithdrawType;
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.config.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 交易中心配置 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TradeConfigRespVO extends TradeConfigBaseVO {
|
||||
|
||||
@Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.config.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 交易中心配置更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TradeConfigSaveReqVO extends TradeConfigBaseVO {
|
||||
|
||||
}
|
@@ -67,6 +67,11 @@ public class TradeOrderController {
|
||||
public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long 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);
|
||||
// orderLog
|
||||
|
@@ -26,24 +26,23 @@ public class TradeOrderDetailRespVO extends TradeOrderBaseVO {
|
||||
private MemberUserRespVO user;
|
||||
|
||||
/**
|
||||
* TODO 订单操作日志, 先模拟一波;返回 logs,简洁,然后复数哈
|
||||
* TODO 订单操作日志, 先模拟一波
|
||||
*/
|
||||
private List<OrderLog> orderLog;
|
||||
private List<OrderLog> logs;
|
||||
|
||||
// TODO @puhui999:swagger 注解
|
||||
@Schema(description = "管理后台 - 交易订单的操作日志")
|
||||
@Data
|
||||
public static class OrderLog {
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
@Schema(description = "操作详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单发货")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-06-01 10:50:20")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer userType;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - 交易订单的详情的订单项目")
|
||||
|
@@ -0,0 +1,55 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
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.AppBrokerageRecordRespVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@Tag(name = "用户 APP - 分销用户")
|
||||
@RestController
|
||||
@RequestMapping("/trade/brokerage-record")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppBrokerageRecordController {
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得分销记录分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageRecordRespVO>> getBrokerageRecordPage(@Valid AppBrokerageRecordPageReqVO pageReqVO) {
|
||||
AppBrokerageRecordRespVO vo1 = new AppBrokerageRecordRespVO()
|
||||
.setId(1L).setPrice(10).setTitle("收到钱").setCreateTime(LocalDateTime.now())
|
||||
.setFinishTime(LocalDateTime.now());
|
||||
AppBrokerageRecordRespVO vo2 = new AppBrokerageRecordRespVO()
|
||||
.setId(2L).setPrice(-20).setTitle("提现钱").setCreateTime(LocalDateTime.now())
|
||||
.setFinishTime(LocalDateTime.now());
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,134 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.*;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@Tag(name = "用户 APP - 分销用户")
|
||||
@RestController
|
||||
@RequestMapping("/trade/brokerage-user")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppBrokerageUserController {
|
||||
@Resource
|
||||
private BrokerageUserService brokerageUserService;
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得个人分销信息")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() {
|
||||
AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()
|
||||
.setBrokerageEnabled(true)
|
||||
.setPrice(2000)
|
||||
.setFrozenPrice(3000);
|
||||
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 =>
|
||||
@GetMapping("/get-summary")
|
||||
@Operation(summary = "获得个人分销统计")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
|
||||
AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
|
||||
.setYesterdayPrice(1)
|
||||
.setBrokeragePrice(2)
|
||||
.setFrozenPrice(3)
|
||||
.setWithdrawPrice(4)
|
||||
.setFirstBrokerageUserCount(166)
|
||||
.setSecondBrokerageUserCount(233);
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@GetMapping("/rank-page-by-user-count")
|
||||
@Operation(summary = "获得分销用户排行分页(基于用户量)")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageUserRankByUserCountRespVO>> getBrokerageUserRankPageByUserCount(AppBrokerageUserRankPageReqVO pageReqVO) {
|
||||
AppBrokerageUserRankByUserCountRespVO vo1 = new AppBrokerageUserRankByUserCountRespVO()
|
||||
.setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokerageUserCount(10);
|
||||
AppBrokerageUserRankByUserCountRespVO vo2 = new AppBrokerageUserRankByUserCountRespVO()
|
||||
.setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokerageUserCount(6);
|
||||
AppBrokerageUserRankByUserCountRespVO vo3 = new AppBrokerageUserRankByUserCountRespVO()
|
||||
.setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokerageUserCount(4);
|
||||
AppBrokerageUserRankByUserCountRespVO vo4 = new AppBrokerageUserRankByUserCountRespVO()
|
||||
.setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokerageUserCount(4);
|
||||
return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L));
|
||||
}
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@GetMapping("/rank-page-by-price")
|
||||
@Operation(summary = "获得分销用户排行分页(基于佣金)")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageUserRankByPriceRespVO>> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
|
||||
AppBrokerageUserRankByPriceRespVO vo1 = new AppBrokerageUserRankByPriceRespVO()
|
||||
.setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokeragePrice(10);
|
||||
AppBrokerageUserRankByPriceRespVO vo2 = new AppBrokerageUserRankByPriceRespVO()
|
||||
.setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokeragePrice(6);
|
||||
AppBrokerageUserRankByPriceRespVO vo3 = new AppBrokerageUserRankByPriceRespVO()
|
||||
.setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokeragePrice(4);
|
||||
AppBrokerageUserRankByPriceRespVO vo4 = new AppBrokerageUserRankByPriceRespVO()
|
||||
.setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokeragePrice(4);
|
||||
return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L));
|
||||
}
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@GetMapping("/child-summary-page")
|
||||
@Operation(summary = "获得下级分销统计分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageUserChildSummaryRespVO>> getBrokerageUserChildSummaryPage(
|
||||
AppBrokerageUserChildSummaryPageReqVO pageReqVO) {
|
||||
AppBrokerageUserChildSummaryRespVO vo1 = new AppBrokerageUserChildSummaryRespVO()
|
||||
.setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokeragePrice(10).setBrokeragePrice(20).setBrokerageOrderCount(30)
|
||||
.setBrokerageTime(LocalDateTime.now());
|
||||
AppBrokerageUserChildSummaryRespVO vo2 = new AppBrokerageUserChildSummaryRespVO()
|
||||
.setId(1L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
|
||||
.setBrokeragePrice(20).setBrokeragePrice(30).setBrokerageOrderCount(40)
|
||||
.setBrokerageTime(LocalDateTime.now());
|
||||
return success(new PageResult<>(asList(vo1, vo2), 10L));
|
||||
}
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@GetMapping("/get-rank-by-price")
|
||||
@Operation(summary = "获得分销用户排行(基于佣金)")
|
||||
@Parameter(name = "times", description = "时间段", required = true)
|
||||
public CommonResult<Integer> bindBrokerageUser(
|
||||
@RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) {
|
||||
return success(1);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawRespVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@Tag(name = "用户 APP - 分销提现")
|
||||
@RestController
|
||||
@RequestMapping("/trade/brokerage-withdraw")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppBrokerageWithdrawController {
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得分销提现分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageWithdrawRespVO>> getBrokerageWithdrawPage() {
|
||||
AppBrokerageWithdrawRespVO vo1 = new AppBrokerageWithdrawRespVO()
|
||||
.setId(1L).setStatus(10).setPrice(10).setStatusName("审批通过").setCreateTime(LocalDateTime.now());
|
||||
AppBrokerageWithdrawRespVO vo2 = new AppBrokerageWithdrawRespVO()
|
||||
.setId(2L).setStatus(0).setPrice(20).setStatusName("审批中").setCreateTime(LocalDateTime.now());
|
||||
return success(new PageResult<>(asList(vo1, vo2), 10L));
|
||||
}
|
||||
|
||||
// TODO 芋艿:临时 mock =>
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建分销提现")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> createBrokerageWithdraw(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) {
|
||||
return success(1L);
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "应用 App - 分销记录分页 Request VO")
|
||||
@Data
|
||||
public class AppBrokerageRecordPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@InEnum(value = BrokerageRecordBizTypeEnum.class, message = "业务类型必须是 {value}")
|
||||
private Integer bizType;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@InEnum(value = BrokerageRecordStatusEnum.class, message = "状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "用户 App - 分销记录 Response VO")
|
||||
@Data
|
||||
public class AppBrokerageRecordRespVO {
|
||||
|
||||
@Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分销标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户下单")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "分销金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "完成时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "应用 App - 绑定推广员 Request VO")
|
||||
@Data
|
||||
public class AppBrokerageUserBindReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "推广员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "推广员编号不能为空")
|
||||
private Long bindUserId;
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 下级分销统计分页 Request VO")
|
||||
@Data
|
||||
public class AppBrokerageUserChildSummaryPageReqVO extends PageParam {
|
||||
|
||||
public static final String SORT_FIELD_USER_COUNT = "userCount";
|
||||
public static final String SORT_FIELD_ORDER_COUNT = "orderCount";
|
||||
public static final String SORT_FIELD_PRICE = "price";
|
||||
|
||||
@Schema(description = "用户昵称", example = "李") // 模糊匹配
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "排序字段", example = "userCount")
|
||||
private SortingField sortingField;
|
||||
|
||||
@Schema(description = "下级的级别", example = "1") // 1 - 直接下级;2 - 间接下级
|
||||
private Integer level;
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "用户 App - 下级分销统计 Response VO")
|
||||
@Data
|
||||
public class AppBrokerageUserChildSummaryRespVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "佣金金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer brokeragePrice;
|
||||
|
||||
@Schema(description = "分销订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
|
||||
private Integer brokerageOrderCount;
|
||||
|
||||
@Schema(description = "分销用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "30")
|
||||
private Integer brokerageUserCount;
|
||||
|
||||
@Schema(description = "成为分销员时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime brokerageTime;
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 个人分销统计 Response VO")
|
||||
@Data
|
||||
public class AppBrokerageUserMySummaryRespVO {
|
||||
|
||||
@Schema(description = "昨天的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer yesterdayPrice;
|
||||
|
||||
@Schema(description = "提现的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer withdrawPrice;
|
||||
|
||||
@Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
|
||||
private Integer brokeragePrice;
|
||||
|
||||
@Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
|
||||
private Integer frozenPrice;
|
||||
|
||||
@Schema(description = "分销用户数量(一级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer firstBrokerageUserCount;
|
||||
|
||||
@Schema(description = "分销用户数量(二级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer secondBrokerageUserCount;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 分销排行用户(基于用户量) Response VO")
|
||||
@Data
|
||||
public class AppBrokerageUserRankByPriceRespVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer brokeragePrice;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 分销排行用户(基于用户量) Response VO")
|
||||
@Data
|
||||
public class AppBrokerageUserRankByUserCountRespVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "邀请用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer brokerageUserCount;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "应用 App - 分销用户排行 Request VO")
|
||||
@Data
|
||||
public class AppBrokerageUserRankPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "开始 + 结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@NotEmpty(message = "时间不能为空")
|
||||
private LocalDateTime[] times;
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 分销用户信息 Response VO")
|
||||
@Data
|
||||
public class AppBrokerageUserRespVO {
|
||||
|
||||
@Schema(description = "是否有分销资格", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean brokerageEnabled;
|
||||
|
||||
@Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
|
||||
private Integer frozenPrice;
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Schema(description = "用户 App - 分销提现创建 Request VO")
|
||||
@Data
|
||||
public class AppBrokerageWithdrawCreateReqVO {
|
||||
|
||||
@Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@InEnum(value = BrokerageWithdrawTypeEnum.class, message = "提现方式必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "提现金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
|
||||
@Min(value = 1, message = "提现金额不能小于 1")
|
||||
private Integer price;
|
||||
|
||||
// ========== 银行卡、微信、支付宝 提现相关字段 ==========
|
||||
|
||||
@Schema(description = "提现账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456789")
|
||||
@NotBlank(message = "提现账号不能为空", groups = {Bank.class, Wechat.class, Alipay.class})
|
||||
private String accountNo;
|
||||
|
||||
// ========== 微信、支付宝 提现相关字段 ==========
|
||||
|
||||
@Schema(description = "收款码的图片", example = "https://www.iocoder.cn/1.png")
|
||||
@URL(message = "收款码的图片,必须是一个 URL")
|
||||
private String accountQrCodeUrl;
|
||||
|
||||
// ========== 银行卡 提现相关字段 ==========
|
||||
|
||||
@Schema(description = "持卡人姓名", example = "张三")
|
||||
@NotBlank(message = "持卡人姓名不能为空", groups = {Bank.class})
|
||||
private String name;
|
||||
@Schema(description = "提现银行", example = "1")
|
||||
@NotBlank(message = "提现银行不能为空", groups = {Bank.class})
|
||||
private Integer bankName;
|
||||
@Schema(description = "开户地址", example = "海淀支行")
|
||||
private String bankAddress;
|
||||
|
||||
public interface Wallet {
|
||||
}
|
||||
|
||||
public interface Bank {
|
||||
}
|
||||
|
||||
public interface Wechat {
|
||||
}
|
||||
|
||||
public interface Alipay {
|
||||
}
|
||||
|
||||
public void validate(Validator validator) {
|
||||
if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(type)) {
|
||||
ValidationUtils.validate(validator, this, Wallet.class);
|
||||
} else if (BrokerageWithdrawTypeEnum.BANK.getType().equals(type)) {
|
||||
ValidationUtils.validate(validator, this, Bank.class);
|
||||
} else if (BrokerageWithdrawTypeEnum.WECHAT.getType().equals(type)) {
|
||||
ValidationUtils.validate(validator, this, Wechat.class);
|
||||
} else if (BrokerageWithdrawTypeEnum.ALIPAY.getType().equals(type)) {
|
||||
ValidationUtils.validate(validator, this, Alipay.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "用户 App - 分销提现 Response VO")
|
||||
@Data
|
||||
public class AppBrokerageWithdrawRespVO {
|
||||
|
||||
@Schema(description = "提现编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "提现状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "提现状态名", requiredMode = Schema.RequiredMode.REQUIRED, example = "审核中")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "提现金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "提现时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.config.vo.AppTradeConfigRespVO;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@Tag(name = "用户 App - 交易配置")
|
||||
@RestController
|
||||
@RequestMapping("/trade/config")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppTradeConfigController {
|
||||
|
||||
@GetMapping("/get")
|
||||
public CommonResult<AppTradeConfigRespVO> getTradeConfig() {
|
||||
AppTradeConfigRespVO respVO = new AppTradeConfigRespVO();
|
||||
respVO.setBrokeragePosterUrls(asList(
|
||||
"https://api.java.crmeb.net/crmebimage/product/2020/08/03/755bf516b1ca4b6db3bfeaa4dd5901cdh71kob20re.jpg",
|
||||
"https://api.java.crmeb.net/crmebimage/maintain/2021/03/01/406d729b84ed4ec9a2171bfcf6fd0634ughzbz9kfi.jpg",
|
||||
"https://api.java.crmeb.net/crmebimage/maintain/2021/03/01/efb1e4e7fe604fe1988b4213ce08cb11tdsyijtd2r.jpg"
|
||||
));
|
||||
respVO.setBrokerageFrozenDays(10);
|
||||
respVO.setBrokerageWithdrawMinPrice(100);
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.config.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 交易配置 Response VO")
|
||||
@Data
|
||||
public class AppTradeConfigRespVO {
|
||||
|
||||
@Schema(description = "分销海报地址数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> brokeragePosterUrls;
|
||||
|
||||
@Schema(description = "佣金冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer brokerageFrozenDays;
|
||||
|
||||
@Schema(description = "佣金提现最小金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer brokerageWithdrawMinPrice;
|
||||
|
||||
}
|
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.config;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
// TODO 芋艿:后续要实现下,配送配置
|
||||
// TODO 芋艿:后续要实现下,配送配置;后续融合到 AppTradeConfigRespVO 中
|
||||
@Schema(description = "用户 App - 配送配置 Response VO")
|
||||
@Data
|
||||
public class AppDeliveryConfigRespVO {
|
||||
|
@@ -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.security.core.annotations.PreAuthenticated;
|
||||
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.item.AppTradeOrderItemCommentCreateReqVO;
|
||||
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) {
|
||||
// 查询订单
|
||||
TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id);
|
||||
if (order == null) {
|
||||
return success(null);
|
||||
}
|
||||
// TODO @puhui999:这里建议改成,如果为 null,直接返回 success null;主要查询操作,尽量不要有非空的提示哈;交给前端处理;
|
||||
// if (order == null) {
|
||||
// return success(null, ORDER_NOT_FOUND.getMsg());
|
||||
// }
|
||||
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId());
|
||||
|
@@ -50,12 +50,18 @@ public class AppTradeOrderSettlementReqVO {
|
||||
private Long seckillActivityId;
|
||||
|
||||
// ========== 拼团活动相关字段 ==========
|
||||
// TODO @puhui999:是不是拼团记录的编号哈?
|
||||
@Schema(description = "拼团活动编号", example = "1024")
|
||||
private Long combinationActivityId;
|
||||
|
||||
@Schema(description = "拼团团长编号", example = "2048")
|
||||
private Long combinationHeadId;
|
||||
|
||||
// ========== 砍价活动相关字段 ==========
|
||||
// TODO @puhui999:是不是砍价记录的编号哈?
|
||||
@Schema(description = "砍价活动编号", example = "123")
|
||||
private Long bargainActivityId;
|
||||
|
||||
@Data
|
||||
@Schema(description = "用户 App - 商品项")
|
||||
@Valid
|
||||
|
@@ -78,7 +78,7 @@ public interface TradeAfterSaleConvert {
|
||||
// 处理订单信息
|
||||
respVO.setOrder(convert(order));
|
||||
// 处理售后日志
|
||||
respVO.setAfterSaleLog(convertList1(logs));
|
||||
respVO.setLogs(convertList1(logs));
|
||||
return respVO;
|
||||
}
|
||||
List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespDTO> list);
|
||||
|
@@ -0,0 +1,50 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.brokerage.record;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 佣金记录 Convert
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Mapper
|
||||
public interface BrokerageRecordConvert {
|
||||
|
||||
BrokerageRecordConvert INSTANCE = Mappers.getMapper(BrokerageRecordConvert.class);
|
||||
|
||||
BrokerageRecordRespVO convert(BrokerageRecordDO bean);
|
||||
|
||||
List<BrokerageRecordRespVO> convertList(List<BrokerageRecordDO> list);
|
||||
|
||||
PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> page);
|
||||
|
||||
// TODO @疯狂:可能 title 不是很固化,会存在类似:沐晴成功购买《XXX JVM 实战》
|
||||
default BrokerageRecordDO convert(BrokerageUserDO user, BrokerageRecordBizTypeEnum bizType, String bizId,
|
||||
Integer brokerageFrozenDays, int brokeragePrice, LocalDateTime unfreezeTime,
|
||||
String title) {
|
||||
brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0);
|
||||
// 不冻结时,佣金直接就是结算状态
|
||||
Integer status = brokerageFrozenDays > 0
|
||||
? BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus()
|
||||
: BrokerageRecordStatusEnum.SETTLEMENT.getStatus();
|
||||
return new BrokerageRecordDO().setUserId(user.getId())
|
||||
.setBizType(bizType.getType()).setBizId(bizId)
|
||||
.setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice())
|
||||
.setTitle(title)
|
||||
.setDescription(StrUtil.format(bizType.getDescription(), String.valueOf(brokeragePrice / 100.0)))
|
||||
.setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.brokerage.user;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 分销用户 Convert
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Mapper
|
||||
public interface BrokerageUserConvert {
|
||||
|
||||
BrokerageUserConvert INSTANCE = Mappers.getMapper(BrokerageUserConvert.class);
|
||||
|
||||
BrokerageUserRespVO convert(BrokerageUserDO bean);
|
||||
|
||||
List<BrokerageUserRespVO> convertList(List<BrokerageUserDO> list);
|
||||
|
||||
PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> page);
|
||||
|
||||
default PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> pageResult,
|
||||
Map<Long, MemberUserRespDTO> userMap,
|
||||
Map<Long, Long> brokerageUserCountMap,
|
||||
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap) {
|
||||
PageResult<BrokerageUserRespVO> result = convertPage(pageResult);
|
||||
for (BrokerageUserRespVO vo : result.getList()) {
|
||||
// 用户信息
|
||||
Optional.ofNullable(userMap.get(vo.getId())).ifPresent(
|
||||
user -> vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
|
||||
// 推广用户数量(一级)
|
||||
vo.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, vo.getId(), 0));
|
||||
// 推广订单数量、推广订单金额
|
||||
Optional<UserBrokerageSummaryBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(vo.getId()));
|
||||
vo.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryBO::getCount).orElse(0))
|
||||
.setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryBO::getPrice).orElse(0));
|
||||
// todo 已提现次数、已提现金额
|
||||
vo.setWithdrawCount(0);
|
||||
vo.setWithdrawPrice(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BrokerageUserDTO convertDTO(BrokerageUserDO brokerageUser);
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.config;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* 交易中心配置 Convert
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Mapper
|
||||
public interface TradeConfigConvert {
|
||||
|
||||
TradeConfigConvert INSTANCE = Mappers.getMapper(TradeConfigConvert.class);
|
||||
|
||||
TradeConfigDO convert(TradeConfigSaveReqVO bean);
|
||||
|
||||
TradeConfigRespVO convert(TradeConfigDO bean);
|
||||
|
||||
}
|
@@ -7,11 +7,13 @@ import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
|
||||
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
|
||||
@@ -93,6 +95,7 @@ public interface TradeOrderConvert {
|
||||
items.forEach(item -> item.setIncrCount(-item.getIncrCount()));
|
||||
return new ProductSkuUpdateStockReqDTO(items);
|
||||
}
|
||||
|
||||
List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
|
||||
|
||||
@Mappings({
|
||||
@@ -151,9 +154,10 @@ public interface TradeOrderConvert {
|
||||
TradeOrderDetailRespVO.OrderLog orderLog = new TradeOrderDetailRespVO.OrderLog();
|
||||
orderLog.setContent("订单操作" + i);
|
||||
orderLog.setCreateTime(LocalDateTime.now());
|
||||
orderLog.setUserType(i % 2 == 0 ? 2 : 1);
|
||||
orderLogs.add(orderLog);
|
||||
}
|
||||
orderVO.setOrderLog(orderLogs);
|
||||
orderVO.setLogs(orderLogs);
|
||||
return orderVO;
|
||||
}
|
||||
|
||||
@@ -273,4 +277,10 @@ public interface TradeOrderConvert {
|
||||
|
||||
TradeOrderDO convert(TradeOrderRemarkReqVO reqVO);
|
||||
|
||||
default BrokerageAddReqBO convert(TradeOrderItemDO item, ProductSkuRespDTO sku) {
|
||||
return new BrokerageAddReqBO().setBizId(String.valueOf(item.getId()))
|
||||
.setBasePrice(item.getPayPrice() * item.getCount())
|
||||
.setFirstFixedPrice(sku.getSubCommissionFirstPrice())
|
||||
.setSecondFixedPrice(sku.getSubCommissionSecondPrice());
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,82 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 佣金记录 DO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@TableName("trade_brokerage_record")
|
||||
@KeySequence("trade_brokerage_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BrokerageRecordDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Integer id;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 业务编号
|
||||
*/
|
||||
private String bizId;
|
||||
/**
|
||||
* 业务类型
|
||||
* <p>
|
||||
* 枚举 {@link BrokerageRecordBizTypeEnum}
|
||||
*/
|
||||
private Integer bizType;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 说明
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
private Integer price;
|
||||
/**
|
||||
* 当前总佣金
|
||||
*/
|
||||
private Integer totalPrice;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
* <p>
|
||||
* 枚举 {@link BrokerageRecordStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 冻结时间(天)
|
||||
*/
|
||||
private Integer frozenDays;
|
||||
/**
|
||||
* 解冻时间
|
||||
*/
|
||||
private LocalDateTime unfreezeTime;
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 分销用户 DO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@TableName("trade_brokerage_user")
|
||||
@KeySequence("trade_brokerage_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BrokerageUserDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
* <p>
|
||||
* 对应 MemberUserDO 的 id 字段
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 推广员编号
|
||||
* <p>
|
||||
* 关联 MemberUserDO 的 id 字段
|
||||
*/
|
||||
private Long bindUserId;
|
||||
/**
|
||||
* 推广员绑定时间
|
||||
*/
|
||||
private LocalDateTime bindUserTime;
|
||||
|
||||
/**
|
||||
* 是否有分销资格
|
||||
*/
|
||||
private Boolean brokerageEnabled;
|
||||
/**
|
||||
* 成为分销员时间
|
||||
*/
|
||||
private LocalDateTime brokerageTime;
|
||||
|
||||
/**
|
||||
* 可用佣金
|
||||
*/
|
||||
private Integer brokeragePrice;
|
||||
/**
|
||||
* 冻结佣金
|
||||
*/
|
||||
private Integer frozenPrice;
|
||||
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 交易中心配置 DO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@TableName(value = "trade_config", autoResultMap = true)
|
||||
@KeySequence("trade_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TradeConfigDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 自增主键
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
// ========== 分销相关 ==========
|
||||
|
||||
/**
|
||||
* 是否启用分佣
|
||||
*/
|
||||
private Boolean brokerageEnabled;
|
||||
/**
|
||||
* 分佣模式
|
||||
* <p>
|
||||
* 枚举 {@link BrokerageEnabledConditionEnum 对应的类}
|
||||
*/
|
||||
private Integer brokerageEnabledCondition;
|
||||
/**
|
||||
* 分销关系绑定模式
|
||||
* <p>
|
||||
* 枚举 {@link BrokerageBindModeEnum 对应的类}
|
||||
*/
|
||||
private Integer brokerageBindMode;
|
||||
/**
|
||||
* 分销海报图地址数组
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> brokeragePostUrls;
|
||||
/**
|
||||
* 一级返佣比例
|
||||
*/
|
||||
private Integer brokerageFirstPercent;
|
||||
/**
|
||||
* 二级返佣比例
|
||||
*/
|
||||
private Integer brokerageSecondPercent;
|
||||
/**
|
||||
* 用户提现最低金额
|
||||
*/
|
||||
private Integer brokerageWithdrawMinPrice;
|
||||
/**
|
||||
* 提现银行
|
||||
*/
|
||||
@TableField(typeHandler = IntegerListTypeHandler.class)
|
||||
private List<Integer> brokerageBankNames;
|
||||
/**
|
||||
* 佣金冻结时间(天)
|
||||
*/
|
||||
private Integer brokerageFrozenDays;
|
||||
/**
|
||||
* 提现方式
|
||||
* <p>
|
||||
* 枚举 {@link BrokerageWithdrawTypeEnum 对应的类}
|
||||
*/
|
||||
@TableField(typeHandler = IntegerListTypeHandler.class)
|
||||
private List<Integer> brokerageWithdrawType;
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.brokerage.record;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 佣金记录 Mapper
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Mapper
|
||||
public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
|
||||
|
||||
default PageResult<BrokerageRecordDO> selectPage(BrokerageRecordPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<BrokerageRecordDO>()
|
||||
.eqIfPresent(BrokerageRecordDO::getUserId, reqVO.getUserId())
|
||||
.eqIfPresent(BrokerageRecordDO::getBizType, reqVO.getBizType())
|
||||
.eqIfPresent(BrokerageRecordDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(BrokerageRecordDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(BrokerageRecordDO::getId));
|
||||
}
|
||||
|
||||
default List<BrokerageRecordDO> selectListByStatusAndUnfreezeTimeLt(Integer status, LocalDateTime unfreezeTime) {
|
||||
return selectList(new LambdaQueryWrapper<BrokerageRecordDO>()
|
||||
.eq(BrokerageRecordDO::getStatus, status)
|
||||
.lt(BrokerageRecordDO::getUnfreezeTime, unfreezeTime));
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(Integer id, Integer status, BrokerageRecordDO updateObj) {
|
||||
return update(updateObj, new LambdaQueryWrapper<BrokerageRecordDO>()
|
||||
.eq(BrokerageRecordDO::getId, id)
|
||||
.eq(BrokerageRecordDO::getStatus, status));
|
||||
}
|
||||
|
||||
default BrokerageRecordDO selectByBizTypeAndBizId(Integer bizType, String bizId) {
|
||||
return selectOne(BrokerageRecordDO::getBizType, bizType,
|
||||
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}")
|
||||
UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId,
|
||||
@Param("bizType") Integer bizType,
|
||||
@Param("status") Integer status);
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.brokerage.user;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 分销用户 Mapper
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Mapper
|
||||
public interface BrokerageUserMapper extends BaseMapperX<BrokerageUserDO> {
|
||||
|
||||
default PageResult<BrokerageUserDO> selectPage(BrokerageUserPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<BrokerageUserDO>()
|
||||
.eqIfPresent(BrokerageUserDO::getBindUserId, reqVO.getBindUserId())
|
||||
.eqIfPresent(BrokerageUserDO::getBrokerageEnabled, reqVO.getBrokerageEnabled())
|
||||
.betweenIfPresent(BrokerageUserDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(BrokerageUserDO::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户可用佣金(增加)
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param incrCount 增加佣金(正数)
|
||||
*/
|
||||
default void updatePriceIncr(Long id, Integer incrCount) {
|
||||
Assert.isTrue(incrCount > 0);
|
||||
LambdaUpdateWrapper<BrokerageUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<BrokerageUserDO>()
|
||||
.setSql(" price = price + " + incrCount)
|
||||
.eq(BrokerageUserDO::getId, id);
|
||||
update(null, lambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户可用佣金(减少)
|
||||
* 注意:理论上佣金可能已经提现,这时会扣出负数,确保平台不会造成损失
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param incrCount 增加佣金(负数)
|
||||
*/
|
||||
default void updatePriceDecr(Long id, Integer incrCount) {
|
||||
Assert.isTrue(incrCount < 0);
|
||||
LambdaUpdateWrapper<BrokerageUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<BrokerageUserDO>()
|
||||
.setSql(" price = price + " + incrCount) // 负数,所以使用 + 号
|
||||
.eq(BrokerageUserDO::getId, id);
|
||||
update(null, lambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户冻结佣金(增加)
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param incrCount 增加冻结佣金(正数)
|
||||
*/
|
||||
default void updateFrozenPriceIncr(Long id, Integer incrCount) {
|
||||
Assert.isTrue(incrCount > 0);
|
||||
LambdaUpdateWrapper<BrokerageUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<BrokerageUserDO>()
|
||||
.setSql(" frozen_price = frozen_price + " + incrCount)
|
||||
.eq(BrokerageUserDO::getId, id);
|
||||
update(null, lambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户冻结佣金(减少)
|
||||
* 注意:理论上冻结佣金可能已经解冻,这时会扣出负数,确保平台不会造成损失
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param incrCount 减少冻结佣金(负数)
|
||||
*/
|
||||
default void updateFrozenPriceDecr(Long id, Integer incrCount) {
|
||||
Assert.isTrue(incrCount < 0);
|
||||
LambdaUpdateWrapper<BrokerageUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<BrokerageUserDO>()
|
||||
.setSql(" frozen_price = frozen_price + " + incrCount) // 负数,所以使用 + 号
|
||||
.eq(BrokerageUserDO::getId, id);
|
||||
update(null, lambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户冻结佣金(减少), 更新用户佣金(增加)
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param incrCount 减少冻结佣金(负数)
|
||||
* @return 更新条数
|
||||
*/
|
||||
default int updateFrozenPriceDecrAndPriceIncr(Long id, Integer incrCount) {
|
||||
Assert.isTrue(incrCount < 0);
|
||||
LambdaUpdateWrapper<BrokerageUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<BrokerageUserDO>()
|
||||
.setSql(" frozen_price = frozen_price + " + incrCount + // 负数,所以使用 + 号
|
||||
", price = price + " + -incrCount) // 负数,所以使用 - 号
|
||||
.eq(BrokerageUserDO::getId, id)
|
||||
.ge(BrokerageUserDO::getFrozenPrice, -incrCount); // cas 逻辑
|
||||
return update(null, lambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
default void updateBindUserIdAndBindUserTimeToNull(Long id) {
|
||||
update(null, new LambdaUpdateWrapper<BrokerageUserDO>()
|
||||
.eq(BrokerageUserDO::getId, id)
|
||||
.set(BrokerageUserDO::getBindUserId, null).set(BrokerageUserDO::getBindUserTime, null));
|
||||
}
|
||||
|
||||
default void updateEnabledFalseAndBrokerageTimeToNull(Long id) {
|
||||
update(null, new LambdaUpdateWrapper<BrokerageUserDO>()
|
||||
.eq(BrokerageUserDO::getId, id)
|
||||
.set(BrokerageUserDO::getBrokerageEnabled, false).set(BrokerageUserDO::getBrokerageTime, null));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 交易中心配置 Mapper
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Mapper
|
||||
public interface TradeConfigMapper extends BaseMapperX<TradeConfigDO> {
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.trade.job.brokerage;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 佣金解冻 Job
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Component
|
||||
@TenantJob
|
||||
public class BrokerageRecordUnfreezeJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private BrokerageRecordService brokerageRecordService;
|
||||
|
||||
@Override
|
||||
public String execute(String param) {
|
||||
int count = brokerageRecordService.unfreezeRecord();
|
||||
return StrUtil.format("解冻佣金 {} 个", count);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位文件,无特殊用途
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade.job;
|
@@ -90,12 +90,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
|
||||
@Override
|
||||
public TradeAfterSaleDO getAfterSale(Long id) {
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
|
||||
// TODO @puhui999;读不到,不要这里报错哈;交给前端报错;一般是读取信息不到,message 提示,然后 close tab;
|
||||
if (afterSale == null) {
|
||||
throw exception(AFTER_SALE_NOT_FOUND);
|
||||
}
|
||||
return afterSale;
|
||||
return tradeAfterSaleMapper.selectById(id);
|
||||
}
|
||||
|
||||
// TODO 芋艿:拼团失败,要不要发起售后的方式退款?还是走取消逻辑?
|
||||
|
@@ -0,0 +1,39 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.bo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 佣金 增加 Request BO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BrokerageAddReqBO {
|
||||
|
||||
/**
|
||||
* 业务编号
|
||||
*/
|
||||
@NotBlank(message = "业务编号不能为空")
|
||||
private String bizId;
|
||||
/**
|
||||
* 佣金基数
|
||||
*/
|
||||
@NotNull(message = "佣金基数不能为空")
|
||||
private Integer basePrice;
|
||||
/**
|
||||
* 一级佣金(固定)
|
||||
*/
|
||||
private Integer firstFixedPrice;
|
||||
/**
|
||||
* 二级佣金(固定)
|
||||
*/
|
||||
private Integer secondFixedPrice;
|
||||
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.bo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户佣金合计 BO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserBrokerageSummaryBO {
|
||||
|
||||
/**
|
||||
* 佣金数量
|
||||
*/
|
||||
private Integer count;
|
||||
/**
|
||||
* 佣金总额
|
||||
*/
|
||||
private Integer price;
|
||||
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.record;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 佣金记录 Service 接口
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
public interface BrokerageRecordService {
|
||||
|
||||
/**
|
||||
* 获得佣金记录
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 佣金记录
|
||||
*/
|
||||
BrokerageRecordDO getBrokerageRecord(Integer id);
|
||||
|
||||
/**
|
||||
* 获得佣金记录分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 佣金记录分页
|
||||
*/
|
||||
PageResult<BrokerageRecordDO> getBrokerageRecordPage(BrokerageRecordPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 增加佣金
|
||||
*
|
||||
* @param userId 会员编号
|
||||
* @param bizType 业务类型
|
||||
* @param list 请求参数列表
|
||||
*/
|
||||
void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, @Valid List<BrokerageAddReqBO> list);
|
||||
|
||||
/**
|
||||
* 取消佣金:将佣金记录,状态修改为已失效
|
||||
*
|
||||
* @param userId 会员编号
|
||||
* @param bizType 业务类型
|
||||
* @param bizId 业务编号
|
||||
*/
|
||||
void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId);
|
||||
|
||||
/**
|
||||
* 解冻佣金:将待结算的佣金记录,状态修改为已结算
|
||||
*
|
||||
* @return 解冻佣金的数量
|
||||
*/
|
||||
int unfreezeRecord();
|
||||
|
||||
/**
|
||||
* 汇总用户佣金
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param bizType 业务类型
|
||||
* @param status 佣金状态
|
||||
* @return 用户佣金汇总
|
||||
*/
|
||||
UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status);
|
||||
}
|
@@ -0,0 +1,236 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.record;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.brokerage.record.BrokerageRecordConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.record.BrokerageRecordMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 佣金记录 Service 实现类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class BrokerageRecordServiceImpl implements BrokerageRecordService {
|
||||
|
||||
@Resource
|
||||
private BrokerageRecordMapper brokerageRecordMapper;
|
||||
@Resource
|
||||
private TradeConfigService tradeConfigService;
|
||||
@Resource
|
||||
private BrokerageUserService brokerageUserService;
|
||||
|
||||
@Override
|
||||
public BrokerageRecordDO getBrokerageRecord(Integer id) {
|
||||
return brokerageRecordMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<BrokerageRecordDO> getBrokerageRecordPage(BrokerageRecordPageReqVO pageReqVO) {
|
||||
return brokerageRecordMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, List<BrokerageAddReqBO> list) {
|
||||
TradeConfigDO memberConfig = tradeConfigService.getTradeConfig();
|
||||
// 0 未启用分销功能
|
||||
if (memberConfig == null || !BooleanUtil.isTrue(memberConfig.getBrokerageEnabled())) {
|
||||
log.warn("[addBrokerage][增加佣金失败:brokerageEnabled 未配置,userId({})", userId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1.1 获得一级推广人
|
||||
BrokerageUserDO firstUser = brokerageUserService.getBindBrokerageUser(userId);
|
||||
if (firstUser == null || !BooleanUtil.isTrue(firstUser.getBrokerageEnabled())) {
|
||||
return;
|
||||
}
|
||||
// 1.2 计算一级分佣
|
||||
addBrokerage(firstUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageFirstPercent(), BrokerageAddReqBO::getFirstFixedPrice, bizType);
|
||||
|
||||
// 2.1 获得二级推广员
|
||||
if (firstUser.getBindUserId() == null) {
|
||||
return;
|
||||
}
|
||||
BrokerageUserDO secondUser = brokerageUserService.getBrokerageUser(firstUser.getBindUserId());
|
||||
if (secondUser == null || !BooleanUtil.isTrue(secondUser.getBrokerageEnabled())) {
|
||||
return;
|
||||
}
|
||||
// 2.2 计算二级分佣
|
||||
addBrokerage(secondUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageSecondPercent(), BrokerageAddReqBO::getSecondFixedPrice, bizType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId) {
|
||||
// TODO @疯狂:userId 加进去查询,会不会更好一点?万一穿错参数;
|
||||
BrokerageRecordDO record = brokerageRecordMapper.selectByBizTypeAndBizId(bizType.getType(), bizId);
|
||||
if (record == null || ObjectUtil.notEqual(record.getUserId(), userId)) {
|
||||
log.error("[cancelBrokerage][userId({})][bizId({}) 更新为已失效失败:记录不存在]", userId, bizId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 更新佣金记录为已失效
|
||||
BrokerageRecordDO updateObj = new BrokerageRecordDO().setStatus(BrokerageRecordStatusEnum.CANCEL.getStatus());
|
||||
int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
|
||||
if (updateRows == 0) {
|
||||
log.error("[cancelBrokerage][record({}) 更新为已失效失败]", record.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 更新用户的佣金
|
||||
if (BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus().equals(record.getStatus())) {
|
||||
brokerageUserService.updateUserFrozenPrice(userId, -record.getPrice());
|
||||
} else if (BrokerageRecordStatusEnum.SETTLEMENT.getStatus().equals(record.getStatus())) {
|
||||
brokerageUserService.updateUserPrice(userId, -record.getPrice());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算佣金
|
||||
*
|
||||
* @param basePrice 佣金基数
|
||||
* @param percent 佣金比例
|
||||
* @param fixedPrice 固定佣金
|
||||
* @return 佣金
|
||||
*/
|
||||
int calculatePrice(Integer basePrice, Integer percent, Integer fixedPrice) {
|
||||
// 1. 优先使用固定佣金
|
||||
if (fixedPrice != null && fixedPrice > 0) {
|
||||
return ObjectUtil.defaultIfNull(fixedPrice, 0);
|
||||
}
|
||||
// 2. 根据比例计算佣金
|
||||
if (basePrice != null && basePrice > 0 && percent != null && percent > 0) {
|
||||
return MoneyUtils.calculateRatePriceFloor(basePrice, Double.valueOf(percent));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加用户佣金
|
||||
*
|
||||
* @param user 用户
|
||||
* @param list 佣金增加参数列表
|
||||
* @param brokerageFrozenDays 冻结天数
|
||||
* @param brokeragePercent 佣金比例
|
||||
* @param fixedPriceFun 固定佣金 // TODO 疯狂:这里是不是可以直接传递 fixedPrice 呀?
|
||||
* @param bizType 业务类型
|
||||
*/
|
||||
private void addBrokerage(BrokerageUserDO user, List<BrokerageAddReqBO> list, Integer brokerageFrozenDays,
|
||||
Integer brokeragePercent, Function<BrokerageAddReqBO, Integer> fixedPriceFun,
|
||||
BrokerageRecordBizTypeEnum bizType) {
|
||||
// 1.1 处理冻结时间
|
||||
LocalDateTime unfreezeTime = null;
|
||||
if (brokerageFrozenDays != null && brokerageFrozenDays > 0) {
|
||||
unfreezeTime = LocalDateTime.now().plusDays(brokerageFrozenDays);
|
||||
}
|
||||
// 1.2 计算分佣
|
||||
int totalBrokerage = 0;
|
||||
List<BrokerageRecordDO> records = new ArrayList<>();
|
||||
for (BrokerageAddReqBO item : list) {
|
||||
int brokeragePerItem = calculatePrice(item.getBasePrice(), brokeragePercent, fixedPriceFun.apply(item));
|
||||
if (brokeragePerItem <= 0) {
|
||||
continue;
|
||||
}
|
||||
records.add(BrokerageRecordConvert.INSTANCE.convert(user, bizType, item.getBizId(),
|
||||
brokerageFrozenDays, brokeragePerItem, unfreezeTime, bizType.getTitle()));
|
||||
totalBrokerage += brokeragePerItem;
|
||||
}
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return;
|
||||
}
|
||||
// 1.3 保存佣金记录
|
||||
brokerageRecordMapper.insertBatch(records);
|
||||
|
||||
// 2. 更新用户佣金
|
||||
if (brokerageFrozenDays != null && brokerageFrozenDays > 0) { // 更新用户冻结佣金
|
||||
brokerageUserService.updateUserFrozenPrice(user.getId(), totalBrokerage);
|
||||
} else { // 更新用户可用佣金
|
||||
brokerageUserService.updateUserPrice(user.getId(), totalBrokerage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int unfreezeRecord() {
|
||||
// 1. 查询待结算的佣金记录
|
||||
List<BrokerageRecordDO> records = brokerageRecordMapper.selectListByStatusAndUnfreezeTimeLt(
|
||||
BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus(), LocalDateTime.now());
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. 遍历执行
|
||||
int count = 0;
|
||||
for (BrokerageRecordDO record : records) {
|
||||
try {
|
||||
boolean success = getSelf().unfreezeRecord(record);
|
||||
if (success) {
|
||||
count++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status) {
|
||||
UserBrokerageSummaryBO summaryBO = brokerageRecordMapper.selectCountAndSumPriceByUserIdAndBizTypeAndStatus(userId, bizType, status);
|
||||
return summaryBO != null ? summaryBO : new UserBrokerageSummaryBO(0, 0);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean unfreezeRecord(BrokerageRecordDO record) {
|
||||
// 更新记录状态
|
||||
BrokerageRecordDO updateObj = new BrokerageRecordDO()
|
||||
.setStatus(BrokerageRecordStatusEnum.SETTLEMENT.getStatus())
|
||||
.setUnfreezeTime(LocalDateTime.now());
|
||||
int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
|
||||
if (updateRows == 0) {
|
||||
log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新用户冻结佣金
|
||||
brokerageUserService.updateFrozenPriceDecrAndPriceIncr(record.getUserId(), -record.getPrice());
|
||||
log.info("[unfreezeRecord][record({}) 更新为已结算成功]", record.getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
* @return 自己
|
||||
*/
|
||||
private BrokerageRecordServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分销用户 Service 接口
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
public interface BrokerageUserService {
|
||||
|
||||
/**
|
||||
* 获得分销用户
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 分销用户
|
||||
*/
|
||||
BrokerageUserDO getBrokerageUser(Long id);
|
||||
|
||||
/**
|
||||
* 获得分销用户列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 分销用户列表
|
||||
*/
|
||||
List<BrokerageUserDO> getBrokerageUserList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得分销用户分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 分销用户分页
|
||||
*/
|
||||
PageResult<BrokerageUserDO> getBrokerageUserPage(BrokerageUserPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 修改推广员编号
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param bindUserId 推广员编号
|
||||
*/
|
||||
void updateBrokerageUserId(Long id, Long bindUserId);
|
||||
|
||||
/**
|
||||
* 修改推广资格
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param enabled 推广资格
|
||||
*/
|
||||
void updateBrokerageUserEnabled(Long id, Boolean enabled);
|
||||
|
||||
/**
|
||||
* 获得用户的推广人
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @return 用户的推广人
|
||||
*/
|
||||
BrokerageUserDO getBindBrokerageUser(Long id);
|
||||
|
||||
/**
|
||||
* 更新用户佣金
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param price 用户可用佣金
|
||||
*/
|
||||
void updateUserPrice(Long id, Integer price);
|
||||
|
||||
/**
|
||||
* 更新用户冻结佣金
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param frozenPrice 用户冻结佣金
|
||||
*/
|
||||
void updateUserFrozenPrice(Long id, Integer frozenPrice);
|
||||
|
||||
/**
|
||||
* 更新用户冻结佣金(减少),更新用户佣金(增加)
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param frozenPrice 减少冻结佣金(负数)
|
||||
*/
|
||||
void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice);
|
||||
|
||||
// TODO @疯狂:这个后面可能要支持下,二级
|
||||
/**
|
||||
* 获得推广用户数量(一级)
|
||||
*
|
||||
* @param bindUserId 绑定的推广员编号
|
||||
* @return 推广用户数量
|
||||
*/
|
||||
Long getBrokerageUserCountByBindUserId(Long bindUserId);
|
||||
|
||||
/**
|
||||
* 【会员】绑定推广员
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param bindUserId 推广员编号
|
||||
* @param isNewUser 是否为新用户
|
||||
* @return 是否绑定
|
||||
*/
|
||||
boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser);
|
||||
|
||||
}
|
@@ -0,0 +1,222 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.user;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.user.BrokerageUserMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 分销用户 Service 实现类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
|
||||
@Resource
|
||||
private BrokerageUserMapper brokerageUserMapper;
|
||||
|
||||
@Resource
|
||||
private TradeConfigService tradeConfigService;
|
||||
|
||||
@Override
|
||||
public BrokerageUserDO getBrokerageUser(Long id) {
|
||||
return brokerageUserMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BrokerageUserDO> getBrokerageUserList(Collection<Long> ids) {
|
||||
return brokerageUserMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<BrokerageUserDO> getBrokerageUserPage(BrokerageUserPageReqVO pageReqVO) {
|
||||
return brokerageUserMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokerageUserId(Long id, Long bindUserId) {
|
||||
// 校验存在
|
||||
validateBrokerageUserExists(id);
|
||||
|
||||
// 情况一:清除推广员
|
||||
if (bindUserId == null) {
|
||||
// 清除推广员
|
||||
brokerageUserMapper.updateBindUserIdAndBindUserTimeToNull(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况二:修改推广员
|
||||
// TODO @疯狂:要复用一些 validateCanBindUser 的校验哈;
|
||||
brokerageUserMapper.updateById(new BrokerageUserDO().setId(id)
|
||||
.setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokerageUserEnabled(Long id, Boolean enabled) {
|
||||
// 校验存在
|
||||
validateBrokerageUserExists(id);
|
||||
if (BooleanUtil.isTrue(enabled)) {
|
||||
// 开通推广资格
|
||||
brokerageUserMapper.updateById(new BrokerageUserDO().setId(id)
|
||||
.setBrokerageEnabled(true).setBrokerageTime(LocalDateTime.now()));
|
||||
} else {
|
||||
// 取消推广资格
|
||||
brokerageUserMapper.updateEnabledFalseAndBrokerageTimeToNull(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateBrokerageUserExists(Long id) {
|
||||
if (brokerageUserMapper.selectById(id) == null) {
|
||||
throw exception(BROKERAGE_USER_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrokerageUserDO getBindBrokerageUser(Long id) {
|
||||
return Optional.ofNullable(id)
|
||||
.map(this::getBrokerageUser)
|
||||
.map(BrokerageUserDO::getBindUserId)
|
||||
.map(this::getBrokerageUser)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserPrice(Long id, Integer price) {
|
||||
if (price > 0) {
|
||||
brokerageUserMapper.updatePriceIncr(id, price);
|
||||
} else if (price < 0) {
|
||||
brokerageUserMapper.updatePriceDecr(id, price);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserFrozenPrice(Long id, Integer frozenPrice) {
|
||||
if (frozenPrice > 0) {
|
||||
brokerageUserMapper.updateFrozenPriceIncr(id, frozenPrice);
|
||||
} else if (frozenPrice < 0) {
|
||||
brokerageUserMapper.updateFrozenPriceDecr(id, frozenPrice);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice) {
|
||||
Assert.isTrue(frozenPrice < 0);
|
||||
int updateRows = brokerageUserMapper.updateFrozenPriceDecrAndPriceIncr(id, frozenPrice);
|
||||
if (updateRows == 0) {
|
||||
throw exception(BROKERAGE_USER_FROZEN_PRICE_NOT_ENOUGH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getBrokerageUserCountByBindUserId(Long bindUserId) {
|
||||
// TODO @疯狂:mapper 封装下哈;不直接在 service 调用这种基础 mapper 的基础方法
|
||||
return brokerageUserMapper.selectCount(BrokerageUserDO::getBindUserId, bindUserId);
|
||||
}
|
||||
|
||||
// TODO @疯狂:因为现在 user 会存在使用验证码直接注册,所以 isNewUser 不太好传递;我们是不是可以约定绑定的时间,createTime 在 30 秒内,就认为新用户;
|
||||
@Override
|
||||
public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) {
|
||||
// TODO @疯狂:userId 为空,搞到参数校验里哇;
|
||||
if (userId == null) {
|
||||
throw exception(0);
|
||||
}
|
||||
|
||||
// 1. 获得分销用户
|
||||
boolean isNewBrokerageUser = false;
|
||||
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
|
||||
if (brokerageUser == null) { // 分销用户不存在的情况:1. 新注册;2. 旧数据;3. 分销功能关闭后又打开
|
||||
isNewBrokerageUser = true;
|
||||
brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setBrokeragePrice(0).setFrozenPrice(0);
|
||||
}
|
||||
|
||||
// 2.1 校验能否绑定
|
||||
boolean validated = validateCanBindUser(brokerageUser, bindUserId, isNewUser);
|
||||
if (!validated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2.2 绑定用户
|
||||
if (isNewBrokerageUser) {
|
||||
Integer enabledCondition = tradeConfigService.getTradeConfig().getBrokerageEnabledCondition();
|
||||
if (BrokerageEnabledConditionEnum.ALL.getCondition().equals(enabledCondition)) { // 人人分销:用户默认就有分销资格
|
||||
// TODO @疯狂:应该设置下 brokerageTime,而不是 bindUserTime
|
||||
brokerageUser.setBrokerageEnabled(true).setBindUserTime(LocalDateTime.now());
|
||||
}
|
||||
// TODO @疯狂:这里是不是要设置 bindUserId、bindUserTime 字段哈;
|
||||
brokerageUserMapper.insert(brokerageUser);
|
||||
} else {
|
||||
brokerageUserMapper.updateById(new BrokerageUserDO().setId(userId)
|
||||
.setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO @疯狂:validate 方法,一般不返回 true false,而是抛出异常;如果要返回 true false 这种,方法名字可以改成 isUserCanBind
|
||||
private boolean validateCanBindUser(BrokerageUserDO user, Long bindUserId, Boolean isNewUser) {
|
||||
// TODO @疯狂:bindUserId 为空,搞到参数校验里哇;
|
||||
if (bindUserId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 校验分销功能是否启用
|
||||
TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
|
||||
if (tradeConfig == null || BooleanUtil.isFalse(tradeConfig.getBrokerageEnabled())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 校验绑定自己
|
||||
if (Objects.equals(user.getId(), bindUserId)) {
|
||||
throw exception(BROKERAGE_BIND_SELF);
|
||||
}
|
||||
|
||||
// 校验要绑定的用户有无推广资格
|
||||
BrokerageUserDO bindUser = brokerageUserMapper.selectById(bindUserId);
|
||||
if (bindUser == null || BooleanUtil.isFalse(bindUser.getBrokerageEnabled())) {
|
||||
throw exception(BROKERAGE_BIND_USER_NOT_ENABLED);
|
||||
}
|
||||
|
||||
// 校验分佣模式:仅可后台手动设置推广员
|
||||
if (BrokerageEnabledConditionEnum.ADMIN.getCondition().equals(tradeConfig.getBrokerageEnabledCondition())) {
|
||||
throw exception(BROKERAGE_BIND_CONDITION_ADMIN);
|
||||
}
|
||||
|
||||
// 校验分销关系绑定模式
|
||||
if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) {
|
||||
if (!BooleanUtil.isTrue(isNewUser)) {
|
||||
throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定
|
||||
}
|
||||
} else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) {
|
||||
if (user.getBindUserId() != null) {
|
||||
throw exception(BROKERAGE_BIND_OVERRIDE); // 已绑定了推广人
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @疯狂:这块是不是一直查询到根节点,中间不允许出现自己;就是不能形成环。虽然目前是 2 级,但是未来可能会改多级; = = 环的话,就会存在问题哈
|
||||
// A->B->A:下级不能绑定自己的上级, A->B->C->A可以!!
|
||||
if (Objects.equals(user.getId(), bindUser.getBindUserId())) {
|
||||
throw exception(BROKERAGE_BIND_LOOP);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.trade.service.config;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 交易中心配置 Service 接口
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
public interface TradeConfigService {
|
||||
|
||||
/**
|
||||
* 更新交易中心配置
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void saveTradeConfig(@Valid TradeConfigSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 获得交易中心配置
|
||||
*
|
||||
* @return 交易中心配置
|
||||
*/
|
||||
TradeConfigDO getTradeConfig();
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.trade.service.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.config.TradeConfigConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.config.TradeConfigMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 交易中心配置 Service 实现类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class TradeConfigServiceImpl implements TradeConfigService {
|
||||
|
||||
@Resource
|
||||
private TradeConfigMapper tradeConfigMapper;
|
||||
|
||||
@Override
|
||||
public void saveTradeConfig(TradeConfigSaveReqVO saveReqVO) {
|
||||
// 存在,则进行更新
|
||||
TradeConfigDO dbConfig = getTradeConfig();
|
||||
if (dbConfig != null) {
|
||||
tradeConfigMapper.updateById(TradeConfigConvert.INSTANCE.convert(saveReqVO).setId(dbConfig.getId()));
|
||||
return;
|
||||
}
|
||||
// 不存在,则进行插入
|
||||
tradeConfigMapper.insert(TradeConfigConvert.INSTANCE.convert(saveReqVO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeConfigDO getTradeConfig() {
|
||||
List<TradeConfigDO> list = tradeConfigMapper.selectList();
|
||||
return CollectionUtils.getFirst(list);
|
||||
}
|
||||
|
||||
}
|
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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.dto.ProductCommentCreateReqDTO;
|
||||
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.combination.CombinationRecordApi;
|
||||
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.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.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
|
||||
@@ -46,10 +47,14 @@ 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.mysql.order.TradeOrderItemMapper;
|
||||
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.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.*;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.cart.CartService;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
|
||||
import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
|
||||
@@ -71,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.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.trade.enums.ErrorCodeConstants.*;
|
||||
|
||||
@@ -89,6 +94,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
private TradeOrderMapper tradeOrderMapper;
|
||||
@Resource
|
||||
private TradeOrderItemMapper tradeOrderItemMapper;
|
||||
@Resource
|
||||
private TradeOrderNoRedisDAO orderNoRedisDAO;
|
||||
|
||||
@Resource
|
||||
private CartService cartService;
|
||||
@@ -112,12 +119,18 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
@Resource
|
||||
private BargainRecordApi bargainRecordApi;
|
||||
@Resource
|
||||
private SeckillActivityApi seckillActivityApi;
|
||||
@Resource
|
||||
private BargainActivityApi bargainActivityApi;
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
@Resource
|
||||
private MemberLevelApi memberLevelApi;
|
||||
@Resource
|
||||
private MemberPointApi memberPointApi;
|
||||
@Resource
|
||||
private BrokerageRecordService brokerageRecordService;
|
||||
@Resource
|
||||
private ProductCommentApi productCommentApi;
|
||||
|
||||
@Resource
|
||||
@@ -190,22 +203,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
// TODO @puhui999:这个逻辑,先抽个小方法;未来要通过设计模式,把这些拼团之类的逻辑,抽象出去
|
||||
// 拼团
|
||||
if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
|
||||
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));
|
||||
createCombinationRecord(userId, createReqVO, orderItems, order);
|
||||
}
|
||||
// 3.2 秒杀的特殊逻辑
|
||||
// TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除
|
||||
if (Objects.equals(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) {
|
||||
|
||||
}
|
||||
@@ -215,6 +215,22 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
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:订单超时,自动取消;
|
||||
|
||||
/**
|
||||
@@ -241,8 +257,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
address = validateAddress(userId, createReqVO.getAddressId());
|
||||
}
|
||||
TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
|
||||
String no = orderNoRedisDAO.generate(TradeOrderNoRedisDAO.TRADE_ORDER_NO_PREFIX);
|
||||
order.setType(validateActivity(createReqVO));
|
||||
order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @puhui999: 参考支付订单,的 no 生成哈;
|
||||
order.setNo(no);
|
||||
order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
|
||||
order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
|
||||
@@ -294,19 +311,23 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
|
||||
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems,
|
||||
TradePriceCalculateRespBO calculateRespBO) {
|
||||
// 下单时扣减商品库存
|
||||
// TODO @puhui999:扣库存,需要前置;
|
||||
Integer count = getSumValue(orderItems, TradeOrderItemDO::getCount, Integer::sum);
|
||||
// 1)如果是秒杀商品:额外扣减秒杀的库存;
|
||||
// 2)如果是拼团活动:额外扣减拼团的库存;
|
||||
// 3)如果是砍价活动:额外扣减砍价的库存;
|
||||
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));
|
||||
|
||||
// 删除购物车商品
|
||||
Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
|
||||
if (CollUtil.isNotEmpty(cartIds)) {
|
||||
cartService.deleteCart(userId, cartIds);
|
||||
if (Objects.equals(TradeOrderTypeEnum.SECKILL.getType(), tradeOrderDO.getType())) {
|
||||
SeckillActivityUpdateStockReqDTO updateStockReqDTO = new SeckillActivityUpdateStockReqDTO();
|
||||
updateStockReqDTO.setActivityId(createReqVO.getSeckillActivityId());
|
||||
updateStockReqDTO.setCount(count);
|
||||
updateStockReqDTO.setItems(CollectionUtils.convertList(orderItems, item -> {
|
||||
SeckillActivityUpdateStockReqDTO.Item item1 = new SeckillActivityUpdateStockReqDTO.Item();
|
||||
item1.setSpuId(item.getSpuId());
|
||||
item1.setSkuId(item.getSkuId());
|
||||
item1.setCount(item.getCount());
|
||||
return item1;
|
||||
}));
|
||||
seckillActivityApi.updateSeckillStock(updateStockReqDTO);
|
||||
}
|
||||
|
||||
// 2)如果是砍价活动:额外扣减砍价的库存;
|
||||
bargainActivityApi.updateBargainActivityStock(createReqVO.getBargainActivityId(), count);
|
||||
// 扣减积分 TODO 芋艿:待实现,需要前置;
|
||||
// 这个是不是应该放到支付成功之后?如果支付后的话,可能积分可以重复使用哈。资源类,都要预扣
|
||||
|
||||
@@ -316,6 +337,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
.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);
|
||||
|
||||
@@ -352,8 +382,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
// 1、拼团活动
|
||||
if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
|
||||
// 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录
|
||||
combinationRecordApi.updateCombinationRecordStatus(new CombinationRecordUpdateStatusReqDTO().setUserId(order.getUserId())
|
||||
.setOrderId(order.getId()).setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()).setStartTime(LocalDateTime.now()));
|
||||
combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now());
|
||||
}
|
||||
// TODO 芋艿:发送订单变化的消息
|
||||
|
||||
@@ -365,6 +394,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
getSelf().addUserPointAsync(order.getUserId(), order.getPayPrice(), order.getId());
|
||||
// 增加用户经验
|
||||
getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId());
|
||||
// 增加用户佣金
|
||||
getSelf().addBrokerageAsync(order.getUserId(), order.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -466,11 +497,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
*/
|
||||
private TradeOrderDO validateOrderDeliverable(Long 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())) {
|
||||
throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
|
||||
@@ -536,20 +562,35 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO @puhui999:考虑事务性
|
||||
public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) {
|
||||
// 校验交易订单
|
||||
TradeOrderDO order = validateOrderExists(reqVO.getId());
|
||||
if (order.getPayStatus()) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_PAID);
|
||||
}
|
||||
// TODO @puhui999:如果改价,需要校验下是否真的变化;
|
||||
if (ObjectUtil.equal(order.getAdjustPrice(), reqVO.getAdjustPrice())) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_EQUAL);
|
||||
}
|
||||
|
||||
// 更新
|
||||
// TODO @puhui999:TradeOrderItemDO 需要做 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);
|
||||
update.setPayPrice(update.getPayPrice() + update.getAdjustPrice());
|
||||
// TODO @芋艿:改价时,赠送的积分,要不要做改动???
|
||||
tradeOrderMapper.updateById(update);
|
||||
// 更新支付订单
|
||||
payOrderApi.updatePayOrderPriceById(order.getPayOrderId(), update.getPayPrice());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -629,12 +670,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
.setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
|
||||
}
|
||||
|
||||
// TODO 芋艿:未来如果有分佣,需要更新相关分佣订单为已失效
|
||||
|
||||
// 扣减用户积分
|
||||
getSelf().reduceUserPointAsync(order.getUserId(), orderRefundPrice, afterSaleId);
|
||||
// 扣减用户经验
|
||||
getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId);
|
||||
// 更新分佣记录为已失效
|
||||
getSelf().cancelBrokerageAsync(order.getUserId(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -742,6 +783,20 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
memberPointApi.addPoint(userId, -refundPrice, bizType, String.valueOf(afterSaleId));
|
||||
}
|
||||
|
||||
|
||||
@Async
|
||||
protected void addBrokerageAsync(Long userId, Long orderId) {
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId);
|
||||
List<BrokerageAddReqBO> list = convertList(orderItems,
|
||||
item -> TradeOrderConvert.INSTANCE.convert(item, productSkuApi.getSku(item.getSkuId())));
|
||||
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, list);
|
||||
}
|
||||
|
||||
@Async
|
||||
protected void cancelBrokerageAsync(Long userId, Long orderItemId) {
|
||||
brokerageRecordService.cancelBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItemId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
|
@@ -0,0 +1,117 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.record;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.record.BrokerageRecordMapper;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.randomEle;
|
||||
import static cn.hutool.core.util.RandomUtil.randomInt;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomInteger;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
// TODO @芋艿:单测后续看看
|
||||
/**
|
||||
* {@link BrokerageRecordServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Import(BrokerageRecordServiceImpl.class)
|
||||
public class BrokerageRecordServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private BrokerageRecordServiceImpl brokerageRecordService;
|
||||
@Resource
|
||||
private BrokerageRecordMapper brokerageRecordMapper;
|
||||
|
||||
@MockBean
|
||||
private TradeConfigService tradeConfigService;
|
||||
@MockBean
|
||||
private BrokerageUserService brokerageUserService;
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetBrokerageRecordPage() {
|
||||
// mock 数据
|
||||
BrokerageRecordDO dbBrokerageRecord = randomPojo(BrokerageRecordDO.class, o -> { // 等会查询到
|
||||
o.setUserId(null);
|
||||
o.setBizType(null);
|
||||
o.setStatus(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
brokerageRecordMapper.insert(dbBrokerageRecord);
|
||||
// 测试 userId 不匹配
|
||||
brokerageRecordMapper.insert(cloneIgnoreId(dbBrokerageRecord, o -> o.setUserId(null)));
|
||||
// 测试 bizType 不匹配
|
||||
brokerageRecordMapper.insert(cloneIgnoreId(dbBrokerageRecord, o -> o.setBizType(null)));
|
||||
// 测试 status 不匹配
|
||||
brokerageRecordMapper.insert(cloneIgnoreId(dbBrokerageRecord, o -> o.setStatus(null)));
|
||||
// 测试 createTime 不匹配
|
||||
brokerageRecordMapper.insert(cloneIgnoreId(dbBrokerageRecord, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
BrokerageRecordPageReqVO reqVO = new BrokerageRecordPageReqVO();
|
||||
reqVO.setUserId(null);
|
||||
reqVO.setBizType(null);
|
||||
reqVO.setStatus(null);
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbBrokerageRecord, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculatePrice_useFixedPrice() {
|
||||
// mock 数据
|
||||
Integer payPrice = randomInteger();
|
||||
Integer percent = randomInt(1, 101);
|
||||
Integer fixedPrice = randomInt();
|
||||
// 调用
|
||||
int brokerage = brokerageRecordService.calculatePrice(payPrice, percent, fixedPrice);
|
||||
// 断言
|
||||
assertEquals(brokerage, fixedPrice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculatePrice_usePercent() {
|
||||
// mock 数据
|
||||
Integer payPrice = randomInteger();
|
||||
Integer percent = randomInt(1, 101);
|
||||
Integer fixedPrice = randomEle(new Integer[]{0, null});
|
||||
System.out.println("fixedPrice=" + fixedPrice);
|
||||
// 调用
|
||||
int brokerage = brokerageRecordService.calculatePrice(payPrice, percent, fixedPrice);
|
||||
// 断言
|
||||
assertEquals(brokerage, NumberUtil.div(NumberUtil.mul(payPrice, percent), 100, 0, RoundingMode.DOWN).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculatePrice_equalsZero() {
|
||||
// mock 数据
|
||||
Integer payPrice = null;
|
||||
Integer percent = null;
|
||||
Integer fixedPrice = null;
|
||||
// 调用
|
||||
int brokerage = brokerageRecordService.calculatePrice(payPrice, percent, fixedPrice);
|
||||
// 断言
|
||||
assertEquals(brokerage, 0);
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.user.BrokerageUserMapper;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
// TODO @芋艿:单测后续看看
|
||||
/**
|
||||
* {@link BrokerageUserServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Import(BrokerageUserServiceImpl.class)
|
||||
public class BrokerageUserServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private BrokerageUserServiceImpl brokerageUserService;
|
||||
|
||||
@Resource
|
||||
private BrokerageUserMapper brokerageUserMapper;
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetBrokerageUserPage() {
|
||||
// mock 数据
|
||||
BrokerageUserDO dbBrokerageUser = randomPojo(BrokerageUserDO.class, o -> { // 等会查询到
|
||||
o.setBindUserId(null);
|
||||
o.setBrokerageEnabled(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
brokerageUserMapper.insert(dbBrokerageUser);
|
||||
// 测试 brokerageUserId 不匹配
|
||||
brokerageUserMapper.insert(cloneIgnoreId(dbBrokerageUser, o -> o.setBindUserId(null)));
|
||||
// 测试 brokerageEnabled 不匹配
|
||||
brokerageUserMapper.insert(cloneIgnoreId(dbBrokerageUser, o -> o.setBrokerageEnabled(null)));
|
||||
// 测试 createTime 不匹配
|
||||
brokerageUserMapper.insert(cloneIgnoreId(dbBrokerageUser, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
BrokerageUserPageReqVO reqVO = new BrokerageUserPageReqVO();
|
||||
reqVO.setBindUserId(null);
|
||||
reqVO.setBrokerageEnabled(null);
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<BrokerageUserDO> pageResult = brokerageUserService.getBrokerageUserPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbBrokerageUser, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
@@ -2,3 +2,5 @@ DELETE FROM trade_order;
|
||||
DELETE FROM trade_order_item;
|
||||
DELETE FROM trade_after_sale;
|
||||
DELETE FROM trade_after_sale_log;
|
||||
DELETE FROM trade_brokerage_user;
|
||||
DELETE FROM trade_brokerage_record;
|
||||
|
@@ -125,3 +125,42 @@ CREATE TABLE IF NOT EXISTS "trade_after_sale_log" (
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '交易售后日志';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "trade_brokerage_user"
|
||||
(
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"bind_user_id" bigint NOT NULL,
|
||||
"bind_user_time" varchar,
|
||||
"brokerage_enabled" bit NOT NULL,
|
||||
"brokerage_time" varchar,
|
||||
"price" int NOT NULL,
|
||||
"frozen_price" int NOT NULL,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '分销用户';
|
||||
CREATE TABLE IF NOT EXISTS "trade_brokerage_record"
|
||||
(
|
||||
"id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"user_id" bigint NOT NULL,
|
||||
"biz_id" varchar NOT NULL,
|
||||
"biz_type" varchar NOT NULL,
|
||||
"title" varchar NOT NULL,
|
||||
"price" int NOT NULL,
|
||||
"total_price" int NOT NULL,
|
||||
"description" varchar NOT NULL,
|
||||
"status" varchar NOT NULL,
|
||||
"frozen_days" int NOT NULL,
|
||||
"unfreeze_time" varchar,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint not null default '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '佣金记录';
|
Reference in New Issue
Block a user