mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-12-02 19:58:44 +08:00
Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product
# Conflicts: # yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
This commit is contained in:
@@ -10,11 +10,11 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.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.enums.brokerage.BrokerageWithdrawStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -88,27 +88,24 @@ public class BrokerageUserController {
|
||||
// 分页查询
|
||||
PageResult<BrokerageUserDO> pageResult = brokerageUserService.getBrokerageUserPage(pageVO);
|
||||
|
||||
// 涉及到的用户
|
||||
Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
|
||||
// 查询用户信息
|
||||
Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
|
||||
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
|
||||
// TODO @疯狂:看看下面两个 getBrokerageUserCountByBindUserId、getWithdrawSummaryByUserId 有没可能一次性出结果,不然 n 次有点太花性能了;
|
||||
// 合计分佣订单
|
||||
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageRecordService.getUserBrokerageSummaryByUserId(userId,
|
||||
BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus()));
|
||||
// 合计推广用户数量
|
||||
// 合计分佣的推广订单
|
||||
Map<Long, UserBrokerageSummaryRespBO> brokerageOrderSummaryMap = brokerageRecordService.getUserBrokerageSummaryMapByUserId(
|
||||
userIds, BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus());
|
||||
// 合计分佣的推广用户
|
||||
// TODO @疯狂:转成 map 批量读取
|
||||
Map<Long, Long> brokerageUserCountMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, null));
|
||||
// 合计提现
|
||||
Map<Long, UserWithdrawSummaryBO> withdrawMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageWithdrawService.getWithdrawSummaryByUserId(userId, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS));
|
||||
// 合计分佣的提现
|
||||
// TODO @疯狂:如果未来支持了打款这个动作,可能 status 会不对;
|
||||
Map<Long, BrokerageWithdrawSummaryRespBO> withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId(
|
||||
userIds, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS);
|
||||
// 拼接返回
|
||||
return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap,
|
||||
userOrderSummaryMap, withdrawMap));
|
||||
brokerageOrderSummaryMap, withdrawMap));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,25 +21,25 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
|
||||
|
||||
// ========== 用户信息 ==========
|
||||
|
||||
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
|
||||
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.png")
|
||||
private String avatar;
|
||||
@Schema(description = "用户昵称", example = "李四")
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||
private String nickname;
|
||||
|
||||
// ========== 推广信息 ==========
|
||||
|
||||
@Schema(description = "推广用户数量", example = "20019")
|
||||
@Schema(description = "推广用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
private Integer brokerageUserCount;
|
||||
@Schema(description = "推广订单数量", example = "20019")
|
||||
@Schema(description = "推广订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
private Integer brokerageOrderCount;
|
||||
@Schema(description = "推广订单金额", example = "20019")
|
||||
@Schema(description = "推广订单金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
private Integer brokerageOrderPrice;
|
||||
|
||||
// ========== 提现信息 ==========
|
||||
|
||||
@Schema(description = "已提现金额", example = "20019")
|
||||
@Schema(description = "已提现金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
private Integer withdrawPrice;
|
||||
@Schema(description = "已提现次数", example = "20019")
|
||||
@Schema(description = "已提现次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
|
||||
private Integer withdrawCount;
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,19 @@ import java.util.List;
|
||||
*/
|
||||
@Data
|
||||
public class TradeConfigBaseVO {
|
||||
|
||||
// ========== 售后相关 ==========
|
||||
|
||||
@Schema(description = "售后的退款理由", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "售后的退款理由不能为空")
|
||||
private List<String> afterSaleRefundReasons;
|
||||
|
||||
@Schema(description = "售后的退货理由", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "售后的退货理由不能为空")
|
||||
private List<String> afterSaleReturnReasons;
|
||||
|
||||
// ========== 配送相关 ==========
|
||||
|
||||
/**
|
||||
* 是否启用全场包邮
|
||||
*/
|
||||
@@ -80,7 +93,7 @@ public class TradeConfigBaseVO {
|
||||
private Integer brokerageFrozenDays;
|
||||
|
||||
@Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "[0, 1]")
|
||||
@NotNull(message = "提现方式不能为空")
|
||||
@NotEmpty(message = "提现方式不能为空")
|
||||
@InEnum(value = BrokerageWithdrawTypeEnum.class, message = "提现方式必须是 {value}")
|
||||
private List<Integer> brokerageWithdrawTypes;
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ public class TradeOrderController {
|
||||
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);
|
||||
// orderLog
|
||||
// TODO @puhui999:orderLog
|
||||
// 拼接数据
|
||||
MemberUserRespDTO user = memberUserApi.getUser(order.getUserId());
|
||||
return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, user));
|
||||
@@ -120,4 +120,6 @@ public class TradeOrderController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// TODO :核销逻辑
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSa
|
||||
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.annotations.AfterSaleLog;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.util.AfterSaleLogUtils;
|
||||
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
|
||||
@@ -21,9 +20,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
@@ -58,20 +54,9 @@ public class AppTradeAfterSaleController {
|
||||
return success(afterSaleService.getApplyingAfterSaleCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@GetMapping(value = "/get-reason-list")
|
||||
@Operation(summary = "获得售后原因")
|
||||
@Parameter(name = "way", description = "售后类型", required = true, example = "10")
|
||||
public CommonResult<List<String>> getAfterSaleReasonList(@RequestParam("way") Integer way) {
|
||||
if (Objects.equals(TradeAfterSaleWayEnum.REFUND.getWay(), way)) {
|
||||
return success(Arrays.asList("不想要了", "商品质量问题", "商品描述不符"));
|
||||
}
|
||||
return success(Arrays.asList("不想要了", "商品质量问题", "商品描述不符", "商品错发漏发", "商品包装破损"));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/create")
|
||||
@Operation(summary = "申请售后")
|
||||
@AfterSaleLog(id = "#info.data", content = "'申请售后:售后编号['+#info.data+'],订单编号['+#createReqVO.orderItemId+'], '", operateType = AfterSaleOperateTypeEnum.APPLY)
|
||||
@AfterSaleLog(id = "#info.data", content = "'申请售后:售后编号['+#info.data+'],订单编号['+#createReqVO.orderItemId+'], '", operateType = AfterSaleOperateTypeEnum.MEMBER_CREATE)
|
||||
public CommonResult<Long> createAfterSale(@RequestBody AppTradeAfterSaleCreateReqVO createReqVO) {
|
||||
AfterSaleLogUtils.setBeforeStatus(0);
|
||||
AfterSaleLogUtils.setAfterStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
|
||||
|
||||
@@ -45,7 +45,7 @@ public class AppBrokerageRecordController {
|
||||
@GetMapping("/get-product-brokerage-price")
|
||||
@Operation(summary = "获得商品的分销金额")
|
||||
public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
|
||||
return success(brokerageRecordService.calculateProductBrokeragePrice(spuId, getLoginUserId()));
|
||||
return success(brokerageRecordService.calculateProductBrokeragePrice(getLoginUserId(), spuId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -27,6 +27,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -41,6 +42,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppBrokerageUserController {
|
||||
|
||||
@Resource
|
||||
private BrokerageUserService brokerageUserService;
|
||||
@Resource
|
||||
@@ -68,31 +70,32 @@ public class AppBrokerageUserController {
|
||||
@Operation(summary = "绑定推广员")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
|
||||
MemberUserRespDTO user = memberUserApi.getUser(getLoginUserId());
|
||||
return success(brokerageUserService.bindBrokerageUser(user.getId(), reqVO.getBindUserId(), user.getCreateTime()));
|
||||
return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/get-summary")
|
||||
@Operation(summary = "获得个人分销统计")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
|
||||
Long userId = getLoginUserId();
|
||||
// TODO @疯狂:后面这种,要不也改成 convert;感觉 controller 这样更容易看到整体;核心其实是 86、8/87、9/90、9/91 这阶段
|
||||
// 统计 yesterdayPrice、withdrawPrice、firstBrokerageUserCount、secondBrokerageUserCount 字段
|
||||
// 查询当前登录用户信息
|
||||
BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(getLoginUserId());
|
||||
// 统计用户昨日的佣金
|
||||
LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday);
|
||||
LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday);
|
||||
AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
|
||||
.setYesterdayPrice(brokerageRecordService.getSummaryPriceByUserId(userId, BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime))
|
||||
.setWithdrawPrice(Optional.ofNullable(brokerageWithdrawService.getWithdrawSummaryByUserId(userId, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS))
|
||||
.map(UserWithdrawSummaryBO::getPrice).orElse(0))
|
||||
.setBrokeragePrice(0).setFrozenPrice(0)
|
||||
.setFirstBrokerageUserCount(brokerageUserService.getBrokerageUserCountByBindUserId(userId, 1))
|
||||
.setSecondBrokerageUserCount(brokerageUserService.getBrokerageUserCountByBindUserId(userId, 2));
|
||||
// 设置 brokeragePrice、frozenPrice 字段
|
||||
Optional.ofNullable(brokerageUserService.getBrokerageUser(userId))
|
||||
.ifPresent(user -> respVO.setBrokeragePrice(user.getBrokeragePrice()).setFrozenPrice(user.getFrozenPrice()));
|
||||
return success(respVO);
|
||||
Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(brokerageUser.getId(),
|
||||
BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime);
|
||||
// 统计用户提现的佣金
|
||||
Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryListByUserId(Collections.singleton(brokerageUser.getId()),
|
||||
BrokerageWithdrawStatusEnum.AUDIT_SUCCESS).stream()
|
||||
.findFirst().map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0);
|
||||
// 统计分销用户数量(一级)
|
||||
Long firstBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(brokerageUser.getId(), 1);
|
||||
// 统计分销用户数量(二级)
|
||||
Long secondBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(brokerageUser.getId(), 2);
|
||||
|
||||
// 拼接返回
|
||||
return success(BrokerageUserConvert.INSTANCE.convert(yesterdayPrice, withdrawPrice, firstBrokerageUserCount, secondBrokerageUserCount, brokerageUser));
|
||||
}
|
||||
|
||||
@GetMapping("/rank-page-by-user-count")
|
||||
|
||||
@@ -3,7 +3,6 @@ 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.system.api.dict.DictDataApi;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawRespVO;
|
||||
@@ -32,14 +31,10 @@ public class AppBrokerageWithdrawController {
|
||||
@Resource
|
||||
private BrokerageWithdrawService brokerageWithdrawService;
|
||||
|
||||
@Resource
|
||||
private DictDataApi dictDataApi;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得分销提现分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageWithdrawRespVO>> getBrokerageWithdrawPage(AppBrokerageWithdrawPageReqVO pageReqVO) {
|
||||
// 分页查询
|
||||
PageResult<BrokerageWithdrawDO> pageResult = brokerageWithdrawService.getBrokerageWithdrawPage(
|
||||
BrokerageWithdrawConvert.INSTANCE.convert(pageReqVO, getLoginUserId()));
|
||||
return success(BrokerageWithdrawConvert.INSTANCE.convertPage03(pageResult));
|
||||
@@ -49,7 +44,7 @@ public class AppBrokerageWithdrawController {
|
||||
@Operation(summary = "创建分销提现")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> createBrokerageWithdraw(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) {
|
||||
return success(brokerageWithdrawService.createBrokerageWithdraw(createReqVO, getLoginUserId()));
|
||||
return success(brokerageWithdrawService.createBrokerageWithdraw(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ 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;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "用户 App - 下级分销统计分页 Request VO")
|
||||
@Data
|
||||
@@ -19,7 +22,9 @@ public class AppBrokerageUserChildSummaryPageReqVO extends PageParam {
|
||||
@Schema(description = "排序字段", example = "userCount")
|
||||
private SortingField sortingField;
|
||||
|
||||
@Schema(description = "下级的级别", example = "1") // 1 - 直接下级;2 - 间接下级
|
||||
@Schema(description = "下级的级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 1 - 直接下级;2 - 间接下级
|
||||
@NotNull(message = "下级的级别不能为空")
|
||||
@Range(min = 1, max = 2, message = "下级的级别只能是 {min} 或者 {max}")
|
||||
private Integer level;
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,16 @@ import java.util.List;
|
||||
@Data
|
||||
public class AppTradeConfigRespVO {
|
||||
|
||||
// ========== 售后相关 ==========
|
||||
|
||||
@Schema(description = "售后的退款理由", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> afterSaleRefundReasons;
|
||||
|
||||
@Schema(description = "售后的退货理由", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> afterSaleReturnReasons;
|
||||
|
||||
// ========== 分销相关 ==========
|
||||
|
||||
@Schema(description = "分销海报地址数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> brokeragePosterUrls;
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import lombok.Data;
|
||||
@Data
|
||||
public class AppDeliveryConfigRespVO {
|
||||
|
||||
@Schema(description = "腾讯地图 KEY", required = true, example = "123456")
|
||||
@Schema(description = "腾讯地图 KEY", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
|
||||
private String tencentLbsKey;
|
||||
|
||||
@Schema(description = "是否开启自提", required = true, example = "true")
|
||||
@Schema(description = "是否开启自提", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean pickUpEnable;
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import lombok.Data;
|
||||
@Data
|
||||
public class AppDeliveryExpressRespVO {
|
||||
|
||||
@Schema(description = "编号", required = true, example = "1")
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "门店名称", required = true, example = "顺丰")
|
||||
@Schema(description = "门店名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "顺丰")
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ public class AppTradeOrderController {
|
||||
}
|
||||
|
||||
// TODO @芋艿:如果拼团活动、秒杀活动、砍价活动时,是不是要额外在返回活动之类的信息;
|
||||
// TODO @puhui999:需要的
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得交易订单")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
@@ -140,7 +141,7 @@ public class AppTradeOrderController {
|
||||
@Operation(summary = "确认交易订单收货")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
public CommonResult<Boolean> receiveOrder(@RequestParam("id") Long id) {
|
||||
tradeOrderUpdateService.receiveOrder(getLoginUserId(), id);
|
||||
tradeOrderUpdateService.receiveOrderByMember(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ public class AppTradeOrderController {
|
||||
@Operation(summary = "取消交易订单")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
public CommonResult<Boolean> cancelOrder(@RequestParam("id") Long id) {
|
||||
tradeOrderUpdateService.cancelOrder(getLoginUserId(), id);
|
||||
tradeOrderUpdateService.cancelOrderByMember(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@@ -173,7 +174,7 @@ public class AppTradeOrderController {
|
||||
@PostMapping("/item/create-comment")
|
||||
@Operation(summary = "创建交易订单项的评价")
|
||||
public CommonResult<Long> createOrderItemComment(@RequestBody AppTradeOrderItemCommentCreateReqVO createReqVO) {
|
||||
return success(tradeOrderUpdateService.createOrderItemComment(getLoginUserId(), createReqVO));
|
||||
return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ public class AppTradeOrderSettlementReqVO {
|
||||
@Schema(description = "优惠劵编号", example = "1024")
|
||||
private Long couponId;
|
||||
|
||||
@Schema(description = "是否使用积分", required = true, example = "true")
|
||||
@Schema(description = "是否使用积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否使用积分不能为空")
|
||||
private Boolean pointStatus;
|
||||
|
||||
// ========== 配送相关相关字段 ==========
|
||||
@Schema(description = "配送方式", example = "1")
|
||||
@Schema(description = "配送方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确")
|
||||
private Integer deliveryType;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
@Data
|
||||
public class AppTradeOrderSettlementRespVO {
|
||||
|
||||
@Schema(description = "交易类型", required = true, example = "1") // 对应 TradeOrderTypeEnum 枚举
|
||||
@Schema(description = "交易类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 对应 TradeOrderTypeEnum 枚举
|
||||
private Integer type = 1; // TODO 芋艿:改成计算
|
||||
|
||||
@Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@@ -37,6 +37,8 @@ public class AppTradeOrderSettlementRespVO {
|
||||
|
||||
// ========== SPU 信息 ==========
|
||||
|
||||
@Schema(description = "品类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long categoryId;
|
||||
@Schema(description = "SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long spuId;
|
||||
@Schema(description = "SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "Apple iPhone 12")
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.brokerage;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserMySummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
@@ -30,29 +34,27 @@ public interface BrokerageUserConvert {
|
||||
|
||||
List<BrokerageUserRespVO> convertList(List<BrokerageUserDO> list);
|
||||
|
||||
PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> page, Map<Long, MemberUserRespDTO> userMap, Map<Long, Long> brokerageUserCountMap, Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap);
|
||||
PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> page, Map<Long, MemberUserRespDTO> userMap, Map<Long, Long> brokerageUserCountMap, Map<Long, UserBrokerageSummaryRespBO> userOrderSummaryMap);
|
||||
|
||||
default PageResult<BrokerageUserRespVO> convertPage(PageResult<BrokerageUserDO> pageResult,
|
||||
Map<Long, MemberUserRespDTO> userMap,
|
||||
Map<Long, Long> brokerageUserCountMap,
|
||||
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap,
|
||||
Map<Long, UserWithdrawSummaryBO> withdrawMap) {
|
||||
Map<Long, UserBrokerageSummaryRespBO> userOrderSummaryMap,
|
||||
Map<Long, BrokerageWithdrawSummaryRespBO> withdrawMap) {
|
||||
PageResult<BrokerageUserRespVO> result = convertPage(pageResult, userMap, brokerageUserCountMap, userOrderSummaryMap);
|
||||
for (BrokerageUserRespVO userVO : result.getList()) {
|
||||
// 用户信息
|
||||
copyTo(userMap.get(userVO.getId()), userVO);
|
||||
|
||||
// 推广用户数量
|
||||
userVO.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, userVO.getId(), 0));
|
||||
// 推广订单数量、推广订单金额
|
||||
Optional<UserBrokerageSummaryBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(userVO.getId()));
|
||||
userVO.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryBO::getCount).orElse(0))
|
||||
.setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryBO::getPrice).orElse(0));
|
||||
Optional<UserBrokerageSummaryRespBO> orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(userVO.getId()));
|
||||
userVO.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryRespBO::getCount).orElse(0))
|
||||
.setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryRespBO::getPrice).orElse(0));
|
||||
// 已提现次数、已提现金额
|
||||
Optional<UserWithdrawSummaryBO> withdrawSummaryOptional = Optional.ofNullable(withdrawMap.get(userVO.getId()));
|
||||
userVO.setWithdrawCount(withdrawSummaryOptional.map(UserWithdrawSummaryBO::getCount).orElse(0))
|
||||
.setWithdrawPrice(withdrawSummaryOptional.map(UserWithdrawSummaryBO::getPrice).orElse(0));
|
||||
userVO.setWithdrawCount(0).setWithdrawPrice(0);
|
||||
Optional<BrokerageWithdrawSummaryRespBO> withdrawSummaryOptional = Optional.ofNullable(withdrawMap.get(userVO.getId()));
|
||||
userVO.setWithdrawCount(withdrawSummaryOptional.map(BrokerageWithdrawSummaryRespBO::getCount).orElse(0))
|
||||
.setWithdrawPrice(withdrawSummaryOptional.map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -71,4 +73,25 @@ public interface BrokerageUserConvert {
|
||||
|
||||
void copyTo(MemberUserRespDTO from, @MappingTarget AppBrokerageUserRankByUserCountRespVO to);
|
||||
|
||||
default AppBrokerageUserMySummaryRespVO convert(Integer yesterdayPrice, Integer withdrawPrice,
|
||||
Long firstBrokerageUserCount, Long secondBrokerageUserCount,
|
||||
BrokerageUserDO brokerageUser) {
|
||||
AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
|
||||
.setYesterdayPrice(ObjUtil.defaultIfNull(yesterdayPrice, 0))
|
||||
.setWithdrawPrice(ObjUtil.defaultIfNull(withdrawPrice, 0))
|
||||
.setBrokeragePrice(0).setFrozenPrice(0)
|
||||
.setFirstBrokerageUserCount(ObjUtil.defaultIfNull(firstBrokerageUserCount, 0L))
|
||||
.setSecondBrokerageUserCount(ObjUtil.defaultIfNull(secondBrokerageUserCount, 0L));
|
||||
// 设置 brokeragePrice、frozenPrice 字段
|
||||
Optional.ofNullable(brokerageUser)
|
||||
.ifPresent(user -> respVO.setBrokeragePrice(user.getBrokeragePrice()).setFrozenPrice(user.getFrozenPrice()));
|
||||
return respVO;
|
||||
}
|
||||
|
||||
default void copyTo(IPage<AppBrokerageUserChildSummaryRespVO> pageResult, Map<Long, MemberUserRespDTO> userMap) {
|
||||
for (AppBrokerageUserChildSummaryRespVO vo : pageResult.getRecords()) {
|
||||
Optional.ofNullable(userMap.get(vo.getId())).ifPresent(user ->
|
||||
vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public interface TradeOrderConvert {
|
||||
|
||||
default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) {
|
||||
List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
|
||||
new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));
|
||||
new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(item.getCount()));
|
||||
return new ProductSkuUpdateStockReqDTO(items);
|
||||
}
|
||||
default ProductSkuUpdateStockReqDTO convertNegative(List<AppTradeOrderSettlementReqVO.Item> list) {
|
||||
@@ -110,7 +110,7 @@ public interface TradeOrderConvert {
|
||||
createReqDTO.setSubject(subject);
|
||||
createReqDTO.setBody(subject); // TODO 芋艿:临时写死
|
||||
// 订单相关字段
|
||||
createReqDTO.setPrice(order.getPayPrice()).setExpireTime(addTime(orderProperties.getExpireTime()));
|
||||
createReqDTO.setPrice(order.getPayPrice()).setExpireTime(addTime(orderProperties.getPayExpireTime()));
|
||||
return createReqDTO;
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ public interface TradeOrderConvert {
|
||||
TradeOrderProperties tradeOrderProperties,
|
||||
DeliveryExpressDO express) {
|
||||
AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);
|
||||
orderVO.setPayExpireTime(addTime(tradeOrderProperties.getExpireTime()));
|
||||
orderVO.setPayExpireTime(addTime(tradeOrderProperties.getPayExpireTime()));
|
||||
if (StrUtil.isNotEmpty(order.getPayChannelCode())) {
|
||||
orderVO.setPayChannelName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CHANNEL_CODE, order.getPayChannelCode()));
|
||||
}
|
||||
|
||||
@@ -35,6 +35,19 @@ public class TradeConfigDO extends BaseDO {
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
// ========== 售后相关 ==========
|
||||
|
||||
/**
|
||||
* 售后的退款理由
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> afterSaleRefundReasons;
|
||||
/**
|
||||
* 售后的退货理由
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> afterSaleReturnReasons;
|
||||
|
||||
// ========== 配送相关 ==========
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,6 +105,8 @@ public class TradeOrderDO extends BaseDO {
|
||||
*/
|
||||
private Boolean commentStatus;
|
||||
|
||||
// TODO @疯狂:加一个推广人编号;
|
||||
|
||||
// ========== 价格 + 支付基本信息 ==========
|
||||
|
||||
// 价格文档 - 淘宝:https://open.taobao.com/docV3.htm?docId=108471&docType=1
|
||||
|
||||
@@ -146,18 +146,20 @@ public class TradeOrderItemDO extends BaseDO {
|
||||
private Integer pointPrice;
|
||||
/**
|
||||
* 使用的积分
|
||||
*
|
||||
* 目的:用于后续取消或者售后订单时,需要归还赠送
|
||||
*/
|
||||
private Integer usePoint;
|
||||
/**
|
||||
* 赠送的积分
|
||||
*
|
||||
* 目的:用于后续取消或者售后订单时,需要扣减赠送
|
||||
*/
|
||||
private Integer givePoint;
|
||||
/**
|
||||
* VIP 减免金额,单位:分
|
||||
*/
|
||||
private Integer vipPrice;
|
||||
// TODO @芋艿:如果商品 vip 折扣时,到底是新增一个 vipPrice 记录优惠记录,还是 vipDiscountPrice,记录 vip 的优惠;还是直接使用 vipPrice;
|
||||
// 目前 crmeb 的选择,单独一个 vipPrice 记录优惠价格;感觉不一定合理,可以在看看有赞的;
|
||||
|
||||
// ========== 售后基本信息 ==========
|
||||
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
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.vo.record.BrokerageRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.yulichang.toolkit.MPJWrappers;
|
||||
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.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 佣金记录 Mapper
|
||||
@@ -52,11 +56,28 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
|
||||
BrokerageRecordDO::getUserId, userId);
|
||||
}
|
||||
|
||||
@Select("SELECT COUNT(1), SUM(price) FROM trade_brokerage_record " +
|
||||
"WHERE user_id = #{userId} AND biz_type = #{bizType} AND status = #{status} AND deleted = FALSE")
|
||||
UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId,
|
||||
@Param("bizType") Integer bizType,
|
||||
@Param("status") Integer status);
|
||||
default List<UserBrokerageSummaryRespBO> selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(Collection<Long> userIds,
|
||||
Integer bizType,
|
||||
Integer status) {
|
||||
List<Map<String, Object>> list = selectMaps(MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
|
||||
.select(BrokerageRecordDO::getUserId)
|
||||
.selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryRespBO::getCount)
|
||||
.selectSum(BrokerageRecordDO::getPrice)
|
||||
.in(BrokerageRecordDO::getUserId, userIds)
|
||||
.eq(BrokerageRecordDO::getBizId, bizType)
|
||||
.eq(BrokerageRecordDO::getStatus, status)
|
||||
.groupBy(BrokerageRecordDO::getUserId));
|
||||
return BeanUtil.copyToList(list, UserBrokerageSummaryRespBO.class);
|
||||
// selectJoinList有BUG,会与租户插件冲突:解析SQL时,发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW
|
||||
// return selectJoinList(UserBrokerageSummaryBO.class, MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
|
||||
// .select(BrokerageRecordDO::getUserId)
|
||||
// .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)
|
||||
// .selectSum(BrokerageRecordDO::getPrice)
|
||||
// .in(BrokerageRecordDO::getUserId, userIds)
|
||||
// .eq(BrokerageRecordDO::getBizId, bizType)
|
||||
// .eq(BrokerageRecordDO::getStatus, status)
|
||||
// .groupBy(BrokerageRecordDO::getUserId));
|
||||
}
|
||||
|
||||
@Select("SELECT SUM(price) FROM trade_brokerage_record " +
|
||||
"WHERE user_id = #{userId} AND biz_type = #{bizType} " +
|
||||
|
||||
@@ -2,10 +2,10 @@ package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||
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.vo.user.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
@@ -139,8 +139,11 @@ public interface BrokerageUserMapper extends BaseMapperX<BrokerageUserDO> {
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
IPage<AppBrokerageUserChildSummaryRespVO> selectSummaryPageByUserId(Page<?> page,
|
||||
@Param("param") AppBrokerageUserChildSummaryPageReqVO param,
|
||||
@Param("userId") Long userId);
|
||||
@Param("ids") List<Long> ids,
|
||||
@Param("bizType") Integer bizType,
|
||||
@Param("status") Integer status,
|
||||
@Param("bindUserIds") List<Long> bindUserIds,
|
||||
@Param("sortingField") SortingField sortingField);
|
||||
|
||||
default List<BrokerageUserDO> selectListByBindUserId(Long bindUserId) {
|
||||
return selectList(BrokerageUserDO::getBindUserId, bindUserId);
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
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.vo.withdraw.BrokerageWithdrawPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 佣金提现 Mapper
|
||||
@@ -37,9 +41,23 @@ public interface BrokerageWithdrawMapper extends BaseMapperX<BrokerageWithdrawDO
|
||||
.eq(BrokerageWithdrawDO::getStatus, status));
|
||||
}
|
||||
|
||||
@Select("SELECT COUNT(1) AS count, SUM(price) AS price FROM trade_brokerage_withdraw " +
|
||||
"WHERE user_id = #{userId} AND status = #{status} AND deleted = FALSE")
|
||||
UserWithdrawSummaryBO selectCountAndSumPriceByUserIdAndStatus(@Param("userId") Long userId,
|
||||
@Param("status") Integer status);
|
||||
default List<BrokerageWithdrawSummaryRespBO> selectCountAndSumPriceByUserIdAndStatus(Collection<Long> userIds, Integer status) {
|
||||
List<Map<String, Object>> list = selectMaps(new MPJLambdaWrapper<BrokerageWithdrawDO>()
|
||||
.select(BrokerageWithdrawDO::getUserId)
|
||||
.selectCount(BrokerageWithdrawDO::getId, BrokerageWithdrawSummaryRespBO::getCount)
|
||||
.selectSum(BrokerageWithdrawDO::getPrice)
|
||||
.in(BrokerageWithdrawDO::getUserId, userIds)
|
||||
.eq(BrokerageWithdrawDO::getStatus, status)
|
||||
.groupBy(BrokerageWithdrawDO::getUserId));
|
||||
return BeanUtil.copyToList(list, BrokerageWithdrawSummaryRespBO.class);
|
||||
// selectJoinList有BUG,会与租户插件冲突:解析SQL时,发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW
|
||||
// return selectJoinList(UserWithdrawSummaryBO.class, new MPJLambdaWrapper<BrokerageWithdrawDO>()
|
||||
// .select(BrokerageWithdrawDO::getUserId)
|
||||
// .selectCount(BrokerageWithdrawDO::getId, UserWithdrawSummaryBO::getCount)
|
||||
// .selectSum(BrokerageWithdrawDO::getPrice)
|
||||
// .in(BrokerageWithdrawDO::getUserId, userIds)
|
||||
// .eq(BrokerageWithdrawDO::getStatus, status)
|
||||
// .groupBy(BrokerageWithdrawDO::getUserId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,4 +38,10 @@ public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
|
||||
.eq(TradeOrderItemDO::getUserId, loginUserId));
|
||||
}
|
||||
|
||||
default List<TradeOrderItemDO> selectListByOrderIdAndCommentStatus(Long orderId, Boolean commentStatus) {
|
||||
return selectList(new LambdaQueryWrapperX<TradeOrderItemDO>()
|
||||
.eq(TradeOrderItemDO::getOrderId, orderId)
|
||||
.eq(TradeOrderItemDO::getCommentStatus, commentStatus));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
@@ -58,4 +60,25 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
.eq(TradeOrderDO::getId, orderId)
|
||||
.eq(TradeOrderDO::getUserId, loginUserId));
|
||||
}
|
||||
|
||||
default List<TradeOrderDO> selectListByStatusAndCreateTimeLt(Integer status, LocalDateTime createTime) {
|
||||
return selectList(new LambdaUpdateWrapper<TradeOrderDO>()
|
||||
.eq(TradeOrderDO::getStatus, status)
|
||||
.lt(TradeOrderDO::getCreateTime, createTime));
|
||||
}
|
||||
|
||||
default List<TradeOrderDO> selectListByStatusAndDeliveryTimeLt(Integer status, LocalDateTime deliveryTime) {
|
||||
return selectList(new LambdaUpdateWrapper<TradeOrderDO>()
|
||||
.eq(TradeOrderDO::getStatus, status)
|
||||
.lt(TradeOrderDO::getDeliveryTime, deliveryTime));
|
||||
}
|
||||
|
||||
default List<TradeOrderDO> selectListByStatusAndReceiveTimeLt(Integer status, LocalDateTime receive,
|
||||
Boolean commentStatus) {
|
||||
return selectList(new LambdaUpdateWrapper<TradeOrderDO>()
|
||||
.eq(TradeOrderDO::getStatus, status)
|
||||
.lt(TradeOrderDO::getReceiveTime, receive)
|
||||
.eq(TradeOrderDO::getCommentStatus, commentStatus));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ public @interface AfterSaleLog {
|
||||
/**
|
||||
* 售后 ID
|
||||
*/
|
||||
String id();
|
||||
@Deprecated
|
||||
String id() default "";
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
@@ -31,6 +32,7 @@ public @interface AfterSaleLog {
|
||||
/**
|
||||
* 日志内容
|
||||
*/
|
||||
@Deprecated
|
||||
String content() default "";
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.aop;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLog;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.annotations.AfterSaleLog;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO;
|
||||
@@ -96,7 +95,7 @@ public class AfterSaleLogAspect {
|
||||
String id = MapUtil.getStr(spelMap, afterSaleLogPoint.id());
|
||||
result.put(ID, id);
|
||||
// 操作类型
|
||||
String operateType = afterSaleLogPoint.operateType().description();
|
||||
String operateType = afterSaleLogPoint.operateType().getContent();
|
||||
result.put(OPERATE_TYPE, operateType);
|
||||
// 日志内容
|
||||
String content = MapUtil.getStr(spelMap, afterSaleLogPoint.content());
|
||||
|
||||
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.util;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.aop.AfterSaleLogAspect;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志工具类
|
||||
* 目前主要的作用,是提供给业务代码,记录操作明细和拓展字段
|
||||
@@ -19,4 +21,13 @@ public class AfterSaleLogUtils {
|
||||
AfterSaleLogAspect.setAfterStatus(status);
|
||||
}
|
||||
|
||||
public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus) {
|
||||
setAfterSaleInfo(id, beforeStatus, afterStatus, null);
|
||||
}
|
||||
|
||||
public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus,
|
||||
Map<String, Object> exts) {
|
||||
// TODO 待实现
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,18 @@ public class TradeOrderProperties {
|
||||
* 支付超时时间
|
||||
*/
|
||||
@NotNull(message = "支付超时时间不能为空")
|
||||
private Duration expireTime;
|
||||
private Duration payExpireTime;
|
||||
|
||||
/**
|
||||
* 收货超时时间
|
||||
*/
|
||||
@NotNull(message = "收货超时时间不能为空")
|
||||
private Duration receiveExpireTime;
|
||||
|
||||
/**
|
||||
* 评论超时时间
|
||||
*/
|
||||
@NotNull(message = "评论超时时间不能为空")
|
||||
private Duration commentExpireTime;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.trade.framework.order.core.annotations;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
|
||||
@@ -31,6 +31,16 @@ import static java.util.Collections.emptyMap;
|
||||
@Slf4j
|
||||
public class TradeOrderLogAspect {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 目前的使用场景:支付回调时,需要强制设置下用户编号
|
||||
*/
|
||||
private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private static final ThreadLocal<Integer> USER_TYPE = new ThreadLocal<>();
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@@ -112,4 +122,9 @@ public class TradeOrderLogAspect {
|
||||
EXTS.set(exts);
|
||||
}
|
||||
|
||||
public static void setUserInfo(Long userId, Integer userType) {
|
||||
USER_ID.set(userId);
|
||||
USER_TYPE.set(userType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,4 +20,8 @@ public class TradeOrderLogUtils {
|
||||
TradeOrderLogAspect.setOrderInfo(id, beforeStatus, afterStatus, exts);
|
||||
}
|
||||
|
||||
public static void setUserInfo(Long userId, Integer userType) {
|
||||
TradeOrderLogAspect.setUserInfo(userId, userType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ import javax.annotation.Resource;
|
||||
* @author owen
|
||||
*/
|
||||
@Component
|
||||
@TenantJob
|
||||
public class BrokerageRecordUnfreezeJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private BrokerageRecordService brokerageRecordService;
|
||||
|
||||
@Override
|
||||
@TenantJob
|
||||
public String execute(String param) {
|
||||
int count = brokerageRecordService.unfreezeRecord();
|
||||
return StrUtil.format("解冻佣金 {} 个", count);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.trade.job.order;
|
||||
|
||||
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.order.TradeOrderUpdateService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 交易订单的自动过期 Job
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class TradeOrderAutoCancelJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private TradeOrderUpdateService tradeOrderUpdateService;
|
||||
|
||||
@Override
|
||||
@TenantJob
|
||||
public String execute(String param) {
|
||||
int count = tradeOrderUpdateService.cancelOrderBySystem();
|
||||
return String.format("过期订单 %s 个", count);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.trade.job.order;
|
||||
|
||||
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.order.TradeOrderUpdateService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 交易订单的自动评论 Job
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class TradeOrderAutoCommentJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private TradeOrderUpdateService tradeOrderUpdateService;
|
||||
|
||||
@Override
|
||||
@TenantJob
|
||||
public String execute(String param) {
|
||||
int count = tradeOrderUpdateService.createOrderItemCommentBySystem();
|
||||
return String.format("评论订单 %s 个", count);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.trade.job.order;
|
||||
|
||||
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.order.TradeOrderUpdateService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 交易订单的自动收货 Job
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class TradeOrderAutoReceiveJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private TradeOrderUpdateService tradeOrderUpdateService;
|
||||
|
||||
@Override
|
||||
@TenantJob
|
||||
public String execute(String param) {
|
||||
int count = tradeOrderUpdateService.receiveOrderBySystem();
|
||||
return String.format("自动收货 %s 个", count);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* 占位文件,无特殊用途
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade.job;
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.iocoder.yudao.module.trade.service.aftersale;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
@@ -17,18 +17,23 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSa
|
||||
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleLogDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
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.aftersale.TradeAfterSaleLogMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.annotations.AfterSaleLog;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service.AfterSaleLogService;
|
||||
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.util.AfterSaleLogUtils;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -61,6 +66,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
private TradeOrderUpdateService tradeOrderUpdateService;
|
||||
@Resource
|
||||
private TradeOrderQueryService tradeOrderQueryService;
|
||||
@Resource
|
||||
private DeliveryExpressService deliveryExpressService;
|
||||
|
||||
@Resource
|
||||
private TradeAfterSaleMapper tradeAfterSaleMapper;
|
||||
@@ -85,7 +92,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
|
||||
@Override
|
||||
public TradeAfterSaleDO getAfterSale(Long userId, Long id) {
|
||||
return tradeAfterSaleMapper.selectByIdAndUserId(id, userId);
|
||||
return tradeAfterSaleMapper.selectByIdAndUserId(id, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,10 +100,9 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
return tradeAfterSaleMapper.selectById(id);
|
||||
}
|
||||
|
||||
// TODO 芋艿:拼团失败,要不要发起售后的方式退款?还是走取消逻辑?
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_CREATE)
|
||||
public Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO) {
|
||||
// 第一步,前置校验
|
||||
TradeOrderItemDO tradeOrderItem = validateOrderItemApplicable(userId, createReqVO);
|
||||
@@ -119,12 +125,10 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
if (orderItem == null) {
|
||||
throw exception(ORDER_ITEM_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 已申请售后,不允许再发起售后申请
|
||||
if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) {
|
||||
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED);
|
||||
}
|
||||
|
||||
// 申请的退款金额,不能超过商品的价格
|
||||
if (createReqVO.getRefundPrice() > orderItem.getPayPrice()) {
|
||||
throw exception(AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR);
|
||||
@@ -163,17 +167,14 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
afterSale.setOrderNo(order.getNo()); // 记录 orderNo 订单流水,方便后续检索
|
||||
afterSale.setType(TradeOrderStatusEnum.isCompleted(order.getStatus())
|
||||
? TradeAfterSaleTypeEnum.AFTER_SALE.getType() : TradeAfterSaleTypeEnum.IN_SALE.getType());
|
||||
// TODO 退还积分
|
||||
tradeAfterSaleMapper.insert(afterSale);
|
||||
|
||||
// 更新交易订单项的售后状态
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(orderItem.getId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
afterSale.getId(), null);
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCreate(orderItem.getId(), afterSale.getId());
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(orderItem.getUserId(), UserTypeEnum.MEMBER.getValue(),
|
||||
afterSale, null, afterSale.getStatus());
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), null,
|
||||
TradeAfterSaleStatusEnum.APPLY.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
return afterSale;
|
||||
@@ -181,6 +182,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_AGREE_APPLY)
|
||||
public void agreeAfterSale(Long userId, Long id) {
|
||||
// 校验售后单存在,并状态未审批
|
||||
TradeAfterSaleDO afterSale = validateAfterSaleAuditable(id);
|
||||
@@ -194,14 +196,14 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
.setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), newStatus);
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus);
|
||||
|
||||
// TODO 发送售后消息
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_DISAGREE_APPLY)
|
||||
public void disagreeAfterSale(Long userId, TradeAfterSaleDisagreeReqVO auditReqVO) {
|
||||
// 校验售后单存在,并状态未审批
|
||||
TradeAfterSaleDO afterSale = validateAfterSaleAuditable(auditReqVO.getId());
|
||||
@@ -213,14 +215,12 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
.setAuditReason(auditReqVO.getAuditReason()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), newStatus);
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus);
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,6 +249,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_DELIVERY)
|
||||
public void deliveryAfterSale(Long userId, AppTradeAfterSaleDeliveryReqVO deliveryReqVO) {
|
||||
// 校验售后单存在,并状态未退货
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(deliveryReqVO.getId());
|
||||
@@ -258,6 +259,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus())) {
|
||||
throw exception(AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE);
|
||||
}
|
||||
DeliveryExpressDO express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
|
||||
|
||||
// 更新售后单的物流信息
|
||||
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus(), new TradeAfterSaleDO()
|
||||
@@ -266,14 +268,17 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
.setDeliveryTime(LocalDateTime.now()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.MEMBER.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus());
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
|
||||
TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus(),
|
||||
MapUtil.<String, Object>builder().put("expressName", express.getName())
|
||||
.put("logisticsNo", deliveryReqVO.getLogisticsNo()).build());
|
||||
|
||||
// TODO 发送售后消息
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_AGREE_RECEIVE)
|
||||
public void receiveAfterSale(Long userId, Long id) {
|
||||
// 校验售后单存在,并状态为已退货
|
||||
TradeAfterSaleDO afterSale = validateAfterSaleReceivable(id);
|
||||
@@ -283,14 +288,15 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
.setStatus(TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus()).setReceiveTime(LocalDateTime.now()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus());
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
|
||||
TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_DISAGREE_RECEIVE)
|
||||
public void refuseAfterSale(Long userId, TradeAfterSaleRefuseReqVO refuseReqVO) {
|
||||
// 校验售后单存在,并状态为已退货
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(refuseReqVO.getId());
|
||||
@@ -307,14 +313,14 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
.setReceiveReason(refuseReqVO.getRefuseMemo()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus());
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
|
||||
TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus(),
|
||||
MapUtil.of("reason", refuseReqVO.getRefuseMemo()));
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,6 +342,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_REFUND)
|
||||
public void refundAfterSale(Long userId, String userIp, Long id) {
|
||||
// 校验售后单的状态,并状态待退款
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
|
||||
@@ -354,15 +361,13 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
.setStatus(TradeAfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.COMPLETE.getStatus());
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
|
||||
TradeAfterSaleStatusEnum.COMPLETE.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【已完成】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(),
|
||||
null, afterSale.getRefundPrice());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleSuccess(afterSale.getOrderItemId(), afterSale.getRefundPrice());
|
||||
}
|
||||
|
||||
private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) {
|
||||
@@ -380,6 +385,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_CANCEL)
|
||||
public void cancelAfterSale(Long userId, Long id) {
|
||||
// 校验售后单的状态,并状态待退款
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
|
||||
@@ -397,14 +404,13 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
.setStatus(TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.MEMBER.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus());
|
||||
AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
|
||||
TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,11 +8,15 @@ import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokera
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.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 cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 佣金记录 Service 接口
|
||||
@@ -57,6 +61,19 @@ public interface BrokerageRecordService {
|
||||
*/
|
||||
void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title);
|
||||
|
||||
/**
|
||||
* 减少佣金【只针对自己】
|
||||
*
|
||||
* @param userId 会员编号
|
||||
* @param bizType 业务类型
|
||||
* @param bizId 业务编号
|
||||
* @param brokeragePrice 佣金
|
||||
* @param title 标题
|
||||
*/
|
||||
default void reduceBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) {
|
||||
addBrokerage(userId, bizType, bizId, -brokeragePrice, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消佣金:将佣金记录,状态修改为已失效
|
||||
*
|
||||
@@ -74,14 +91,29 @@ public interface BrokerageRecordService {
|
||||
int unfreezeRecord();
|
||||
|
||||
/**
|
||||
* 汇总用户佣金
|
||||
* 按照 userId,汇总每个用户的佣金
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userIds 用户编号
|
||||
* @param bizType 业务类型
|
||||
* @param status 佣金状态
|
||||
* @return 用户佣金汇总
|
||||
* @return 用户佣金汇总 List
|
||||
*/
|
||||
UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status);
|
||||
List<UserBrokerageSummaryRespBO> getUserBrokerageSummaryListByUserId(Collection<Long> userIds,
|
||||
Integer bizType, Integer status);
|
||||
|
||||
/**
|
||||
* 按照 userId,汇总每个用户的佣金
|
||||
*
|
||||
* @param userIds 用户编号
|
||||
* @param bizType 业务类型
|
||||
* @param status 佣金状态
|
||||
* @return 用户佣金汇总 Map
|
||||
*/
|
||||
default Map<Long, UserBrokerageSummaryRespBO> getUserBrokerageSummaryMapByUserId(Collection<Long> userIds,
|
||||
Integer bizType, Integer status) {
|
||||
return convertMap(getUserBrokerageSummaryListByUserId(userIds, bizType, status),
|
||||
UserBrokerageSummaryRespBO::getUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户佣金合计
|
||||
@@ -100,7 +132,8 @@ public interface BrokerageRecordService {
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 排行榜分页
|
||||
*/
|
||||
PageResult<AppBrokerageUserRankByPriceRespVO> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO);
|
||||
PageResult<AppBrokerageUserRankByPriceRespVO> getBrokerageUserChildSummaryPageByPrice(
|
||||
AppBrokerageUserRankPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获取用户的排名(基于佣金总数)
|
||||
@@ -114,9 +147,10 @@ public interface BrokerageRecordService {
|
||||
/**
|
||||
* 计算商品被购买后,推广员可以得到的佣金
|
||||
*
|
||||
* @param spuId 商品编号
|
||||
* @param userId 用户编号
|
||||
* @param spuId 商品编号
|
||||
* @return 用户佣金
|
||||
*/
|
||||
AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long spuId, Long userId);
|
||||
AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.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.bo.UserBrokerageSummaryRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -36,10 +36,7 @@ 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.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
|
||||
@@ -234,25 +231,55 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解冻单条佣金记录
|
||||
*
|
||||
* @param record 佣金记录
|
||||
* @return 解冻是否成功
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
@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);
|
||||
public List<UserBrokerageSummaryRespBO> getUserBrokerageSummaryListByUserId(Collection<Long> userIds,
|
||||
Integer bizType, Integer status) {
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getSummaryPriceByUserId(Long userId, Integer bizType, LocalDateTime beginTime, LocalDateTime endTime) {
|
||||
return brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId, bizType, beginTime, endTime);
|
||||
return brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId, bizType,
|
||||
beginTime, endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<AppBrokerageUserRankByPriceRespVO> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
|
||||
IPage<AppBrokerageUserRankByPriceRespVO> pageResult = brokerageRecordMapper.selectSummaryPricePageGroupByUserId(MyBatisUtils.buildPage(pageReqVO),
|
||||
IPage<AppBrokerageUserRankByPriceRespVO> pageResult = brokerageRecordMapper.selectSummaryPricePageGroupByUserId(
|
||||
MyBatisUtils.buildPage(pageReqVO),
|
||||
BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),
|
||||
ArrayUtil.get(pageReqVO.getTimes(), 0), ArrayUtil.get(pageReqVO.getTimes(), 1));
|
||||
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
|
||||
}
|
||||
|
||||
// TODO @疯狂:这个要不我们先做精准的?先查询自己的推广金额,然后查询比该金额多的有多少人?
|
||||
@Override
|
||||
public Integer getUserRankByPrice(Long userId, LocalDateTime[] times) {
|
||||
AppBrokerageUserRankPageReqVO pageParam = new AppBrokerageUserRankPageReqVO().setTimes(times);
|
||||
@@ -289,26 +316,8 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
|
||||
brokerageRecordMapper.insert(record);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long spuId, Long userId) {
|
||||
public AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId) {
|
||||
// 1. 构建默认的返回值
|
||||
AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO().setEnabled(false)
|
||||
.setBrokerageMinPrice(0).setBrokerageMaxPrice(0);
|
||||
@@ -338,7 +347,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
|
||||
if (BooleanUtil.isTrue(spu.getSubCommissionType())) {
|
||||
fixedMinPrice = getMinValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
|
||||
fixedMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
|
||||
// 3.2 全局分佣模式(根据商品价格比例计算)
|
||||
// 3.2 全局分佣模式(根据商品价格比例计算)
|
||||
} else {
|
||||
spuMinPrice = getMinValue(skuList, ProductSkuRespDTO::getPrice);
|
||||
spuMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getPrice);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
|
||||
@@ -10,7 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokera
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -108,24 +106,9 @@ public interface BrokerageUserService {
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param bindUserId 推广员编号
|
||||
* @param registerTime 用户注册时间
|
||||
* @return 是否绑定
|
||||
*/
|
||||
default boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime) {
|
||||
// 注册时间在30秒内的,都算新用户
|
||||
boolean isNewUser = LocalDateTimeUtils.afterNow(registerTime.minusSeconds(30));
|
||||
return bindBrokerageUser(userId, bindUserId, isNewUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【会员】绑定推广员
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param bindUserId 推广员编号
|
||||
* @param isNewUser 是否为新用户
|
||||
* @return 是否绑定
|
||||
*/
|
||||
boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser);
|
||||
boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId);
|
||||
|
||||
/**
|
||||
* 获取用户是否有分销资格
|
||||
|
||||
@@ -4,18 +4,25 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageUserConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.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.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -23,10 +30,7 @@ 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 java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
@@ -47,6 +51,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
@Resource
|
||||
private TradeConfigService tradeConfigService;
|
||||
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Override
|
||||
public BrokerageUserDO getBrokerageUser(Long id) {
|
||||
return brokerageUserMapper.selectById(id);
|
||||
@@ -154,7 +161,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) {
|
||||
public boolean bindBrokerageUser(Long userId, Long bindUserId) {
|
||||
// 1. 获得分销用户
|
||||
boolean isNewBrokerageUser = false;
|
||||
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
|
||||
@@ -164,7 +171,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
}
|
||||
|
||||
// 2.1 校验是否能绑定用户
|
||||
boolean validated = isUserCanBind(brokerageUser, isNewUser);
|
||||
boolean validated = isUserCanBind(brokerageUser);
|
||||
if (!validated) {
|
||||
return false;
|
||||
}
|
||||
@@ -218,11 +225,25 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
|
||||
@Override
|
||||
public PageResult<AppBrokerageUserChildSummaryRespVO> getBrokerageUserChildSummaryPage(AppBrokerageUserChildSummaryPageReqVO pageReqVO, Long userId) {
|
||||
IPage<AppBrokerageUserChildSummaryRespVO> pageResult = brokerageUserMapper.selectSummaryPageByUserId(MyBatisUtils.buildPage(pageReqVO), pageReqVO, userId);
|
||||
// 1.1 根据昵称过滤用户
|
||||
List<Long> ids = StrUtil.isBlank(pageReqVO.getNickname())
|
||||
? Collections.emptyList()
|
||||
: convertList(memberUserApi.getUserListByNickname(pageReqVO.getNickname()), MemberUserRespDTO::getId);
|
||||
// 1.2 生成推广员编号列表
|
||||
List<Long> bindUserIds = buildBindUserIdsByLevel(userId, pageReqVO.getLevel());
|
||||
// 2. 分页查询
|
||||
IPage<AppBrokerageUserChildSummaryRespVO> pageResult = brokerageUserMapper.selectSummaryPageByUserId(
|
||||
MyBatisUtils.buildPage(pageReqVO), ids, BrokerageRecordBizTypeEnum.ORDER.getType(),
|
||||
BrokerageRecordStatusEnum.SETTLEMENT.getStatus(), bindUserIds, pageReqVO.getSortingField()
|
||||
);
|
||||
// 3. 拼接数据并返回
|
||||
List<Long> userIds = convertList(pageResult.getRecords(), AppBrokerageUserChildSummaryRespVO::getId);
|
||||
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
|
||||
BrokerageUserConvert.INSTANCE.copyTo(pageResult, userMap);
|
||||
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
|
||||
}
|
||||
|
||||
private boolean isUserCanBind(BrokerageUserDO user, Boolean isNewUser) {
|
||||
private boolean isUserCanBind(BrokerageUserDO user) {
|
||||
// 校验分销功能是否启用
|
||||
TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
|
||||
if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
|
||||
@@ -236,8 +257,8 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
|
||||
// 校验分销关系绑定模式
|
||||
if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) {
|
||||
// TODO @疯狂:是不是把 isNewUser 挪到这里好点呀?
|
||||
if (!BooleanUtil.isTrue(isNewUser)) {
|
||||
// 判断是否为新用户:注册时间在 30 秒内的,都算新用户
|
||||
if (!isNewRegisterUser(user.getId())) {
|
||||
throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定
|
||||
}
|
||||
} else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) {
|
||||
@@ -245,14 +266,29 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
throw exception(BROKERAGE_BIND_OVERRIDE); // 已绑定了推广人
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为新用户
|
||||
* <p>
|
||||
* 标准:注册时间在 30 秒内的,都算新用户
|
||||
* <p>
|
||||
* 疑问:为什么通过这样的方式实现?
|
||||
* 回答:因为注册在 member 模块,希望它和 trade 模块解耦,所以只能用这种约定的逻辑。
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 是否新用户
|
||||
*/
|
||||
private boolean isNewRegisterUser(Long userId) {
|
||||
MemberUserRespDTO user = memberUserApi.getUser(userId);
|
||||
return user != null && LocalDateTimeUtils.beforeNow(user.getCreateTime().plusSeconds(30));
|
||||
}
|
||||
|
||||
private void validateCanBindUser(BrokerageUserDO user, Long bindUserId) {
|
||||
// 校验要绑定的用户有无推广资格
|
||||
BrokerageUserDO bindUser = brokerageUserMapper.selectById(bindUserId);
|
||||
if (bindUser == null || !BooleanUtil.isTrue(bindUser.getBrokerageEnabled())) {
|
||||
if (bindUser == null || BooleanUtil.isFalse(bindUser.getBrokerageEnabled())) {
|
||||
throw exception(BROKERAGE_BIND_USER_NOT_ENABLED);
|
||||
}
|
||||
|
||||
@@ -283,6 +319,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
* @return 绑定用户编号列表
|
||||
*/
|
||||
private List<Long> buildBindUserIdsByLevel(Long bindUserId, Integer level) {
|
||||
if (bindUserId == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Assert.isTrue(level == null || level <= 2, "目前只支持 level 小于等于 2");
|
||||
List<Long> bindUserIds = CollUtil.newArrayList();
|
||||
if (level == null || level == 1) {
|
||||
|
||||
@@ -5,7 +5,14 @@ import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.Brok
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 佣金提现 Service 接口
|
||||
@@ -42,19 +49,32 @@ public interface BrokerageWithdrawService {
|
||||
/**
|
||||
* 【会员】创建佣金提现
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @param userId 会员用户编号
|
||||
* @param createReqVO 创建信息
|
||||
* @return 佣金提现编号
|
||||
*/
|
||||
Long createBrokerageWithdraw(AppBrokerageWithdrawCreateReqVO createReqVO, Long userId);
|
||||
Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 汇总用户提现
|
||||
* 按照 userId,汇总每个用户的提现
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param status 提现状态
|
||||
* @return 用户提现汇总
|
||||
* @param userIds 用户编号
|
||||
* @param status 提现状态
|
||||
* @return 用户提现汇总 List
|
||||
*/
|
||||
UserWithdrawSummaryBO getWithdrawSummaryByUserId(Long userId, BrokerageWithdrawStatusEnum status);
|
||||
List<BrokerageWithdrawSummaryRespBO> getWithdrawSummaryListByUserId(Collection<Long> userIds,
|
||||
BrokerageWithdrawStatusEnum status);
|
||||
|
||||
/**
|
||||
* 按照 userId,汇总每个用户的提现
|
||||
*
|
||||
* @param userIds 用户编号
|
||||
* @param status 提现状态
|
||||
* @return 用户提现汇总 Map
|
||||
*/
|
||||
default Map<Long, BrokerageWithdrawSummaryRespBO> getWithdrawSummaryMapByUserId(Set<Long> userIds,
|
||||
BrokerageWithdrawStatusEnum status) {
|
||||
return convertMap(getWithdrawSummaryListByUserId(userIds, status), BrokerageWithdrawSummaryRespBO::getUserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
@@ -17,7 +18,7 @@ import cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -26,6 +27,9 @@ import org.springframework.validation.annotation.Validated;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Validator;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -65,29 +69,27 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
|
||||
}
|
||||
|
||||
// 2. 更新
|
||||
BrokerageWithdrawDO updateObj = new BrokerageWithdrawDO()
|
||||
.setStatus(status.getStatus())
|
||||
.setAuditReason(auditReason)
|
||||
.setAuditTime(LocalDateTime.now());
|
||||
int rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDITING.getStatus(), updateObj);
|
||||
int rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDITING.getStatus(),
|
||||
new BrokerageWithdrawDO().setStatus(status.getStatus()).setAuditReason(auditReason).setAuditTime(LocalDateTime.now()));
|
||||
if (rows == 0) {
|
||||
throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING);
|
||||
}
|
||||
|
||||
String templateCode = MessageTemplateConstants.BROKERAGE_WITHDRAW_AUDIT_APPROVE;
|
||||
String templateCode;
|
||||
if (BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.equals(status)) {
|
||||
templateCode = MessageTemplateConstants.BROKERAGE_WITHDRAW_AUDIT_APPROVE;
|
||||
// 3.1 通过时佣金转余额
|
||||
if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) {
|
||||
// todo
|
||||
// todo 疯狂:
|
||||
}
|
||||
// TODO 疯狂:调用转账接口
|
||||
} else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) {
|
||||
templateCode = MessageTemplateConstants.BROKERAGE_WITHDRAW_AUDIT_REJECT;
|
||||
|
||||
// 3.2 驳回时需要退还用户佣金
|
||||
brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT,
|
||||
String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle());
|
||||
} else {
|
||||
throw new IllegalArgumentException("不支持的提现状态");
|
||||
throw new IllegalArgumentException("不支持的提现状态:" + status);
|
||||
}
|
||||
|
||||
// 4. 通知用户
|
||||
@@ -96,10 +98,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
|
||||
.put("price", MoneyUtils.fenToYuanStr(withdraw.getPrice()))
|
||||
.put("reason", withdraw.getAuditReason())
|
||||
.build();
|
||||
NotifySendSingleToUserReqDTO reqDTO = new NotifySendSingleToUserReqDTO()
|
||||
.setUserId(withdraw.getUserId())
|
||||
.setTemplateCode(templateCode).setTemplateParams(templateParams);
|
||||
notifyMessageSendApi.sendSingleMessageToMember(reqDTO);
|
||||
notifyMessageSendApi.sendSingleMessageToMember(new NotifySendSingleToUserReqDTO()
|
||||
.setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams));
|
||||
}
|
||||
|
||||
private BrokerageWithdrawDO validateBrokerageWithdrawExists(Integer id) {
|
||||
@@ -122,29 +122,32 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createBrokerageWithdraw(AppBrokerageWithdrawCreateReqVO createReqVO, Long userId) {
|
||||
// 校验提现金额
|
||||
public Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO) {
|
||||
// 1.1 校验提现金额
|
||||
TradeConfigDO tradeConfig = validateWithdrawPrice(createReqVO.getPrice());
|
||||
// 校验提现参数
|
||||
// 1.2 校验提现参数
|
||||
createReqVO.validate(validator);
|
||||
|
||||
// 计算手续费
|
||||
// 2.1 计算手续费
|
||||
Integer feePrice = calculateFeePrice(createReqVO.getPrice(), tradeConfig.getBrokerageWithdrawFeePercent());
|
||||
// 创建佣金提现记录
|
||||
// 2.2 创建佣金提现记录
|
||||
BrokerageWithdrawDO withdraw = BrokerageWithdrawConvert.INSTANCE.convert(createReqVO, userId, feePrice);
|
||||
brokerageWithdrawMapper.insert(withdraw);
|
||||
|
||||
// 创建用户佣金记录
|
||||
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.WITHDRAW, String.valueOf(withdraw.getId()),
|
||||
-createReqVO.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW.getTitle());
|
||||
|
||||
// 3. 创建用户佣金记录
|
||||
// 注意,佣金是否充足,reduceBrokerage 已经进行校验
|
||||
brokerageRecordService.reduceBrokerage(userId, BrokerageRecordBizTypeEnum.WITHDRAW, String.valueOf(withdraw.getId()),
|
||||
createReqVO.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW.getTitle());
|
||||
return withdraw.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserWithdrawSummaryBO getWithdrawSummaryByUserId(Long userId, BrokerageWithdrawStatusEnum status) {
|
||||
UserWithdrawSummaryBO summaryBO = brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userId, status.getStatus());
|
||||
return summaryBO != null ? summaryBO : new UserWithdrawSummaryBO(0, 0);
|
||||
public List<BrokerageWithdrawSummaryRespBO> getWithdrawSummaryListByUserId(Collection<Long> userIds,
|
||||
BrokerageWithdrawStatusEnum status) {
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,14 +5,19 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户佣金提现合计 BO
|
||||
* 佣金提现合计 BO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserWithdrawSummaryBO {
|
||||
public class BrokerageWithdrawSummaryRespBO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 提现次数
|
||||
@@ -12,10 +12,14 @@ import lombok.NoArgsConstructor;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserBrokerageSummaryBO {
|
||||
public class UserBrokerageSummaryRespBO {
|
||||
|
||||
/**
|
||||
* 佣金数量
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 推广数量
|
||||
*/
|
||||
private Integer count;
|
||||
/**
|
||||
@@ -26,7 +26,7 @@ import static java.util.Collections.emptyList;
|
||||
/**
|
||||
* 购物车 Service 实现类
|
||||
*
|
||||
* // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息;目前不支持的原因,前端界面需要前端 pr 支持下;
|
||||
* // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息;目前不支持的原因,前端界面需要前端 pr 支持下;例如说:会员价格;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 交易订单【写】Service 接口
|
||||
*
|
||||
@@ -60,15 +62,29 @@ public interface TradeOrderUpdateService {
|
||||
* @param userId 用户编号
|
||||
* @param id 订单编号
|
||||
*/
|
||||
void receiveOrder(Long userId, Long id);
|
||||
void receiveOrderByMember(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 【会员】取消订单
|
||||
* 【系统】自动收货交易订单
|
||||
*
|
||||
* @return 收货数量
|
||||
*/
|
||||
int receiveOrderBySystem();
|
||||
|
||||
/**
|
||||
* 【会员】取消交易订单
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param id 订单编号
|
||||
*/
|
||||
void cancelOrder(Long userId, Long id);
|
||||
void cancelOrderByMember(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 【系统】自动取消订单
|
||||
*
|
||||
* @return 取消数量
|
||||
*/
|
||||
int cancelOrderBySystem();
|
||||
|
||||
/**
|
||||
* 【会员】删除订单
|
||||
@@ -102,35 +118,42 @@ public interface TradeOrderUpdateService {
|
||||
// =================== Order Item ===================
|
||||
|
||||
/**
|
||||
* 更新交易订单项的售后状态
|
||||
* 当售后申请后,更新交易订单项的售后状态
|
||||
*
|
||||
* @param id 交易订单项编号
|
||||
* @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
|
||||
* @param newAfterSaleStatus 目标售后状态
|
||||
* @param id 交易订单项编号
|
||||
* @param afterSaleId 售后单编号
|
||||
*/
|
||||
default void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus) {
|
||||
updateOrderItemAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, null, null);
|
||||
}
|
||||
void updateOrderItemWhenAfterSaleCreate(@NotNull Long id, @NotNull Long afterSaleId);
|
||||
|
||||
/**
|
||||
* 更新交易订单项的售后状态
|
||||
* 当售后完成后,更新交易订单项的售后状态
|
||||
*
|
||||
* @param id 交易订单项编号
|
||||
* @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
|
||||
* @param newAfterSaleStatus 目标售后状态
|
||||
* @param afterSaleId 售后单编号;当订单项发起售后时,必须传递该字段
|
||||
* @param refundPrice 退款金额;当订单项退款成功时,必须传递该值
|
||||
* @param id 交易订单项编号
|
||||
* @param refundPrice 退款金额
|
||||
*/
|
||||
void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
|
||||
Long afterSaleId, Integer refundPrice);
|
||||
void updateOrderItemWhenAfterSaleSuccess(@NotNull Long id, @NotNull Integer refundPrice);
|
||||
|
||||
/**
|
||||
* 创建订单项的评论
|
||||
* 当售后取消(用户取消、管理员驳回、管理员拒绝收货)后,更新交易订单项的售后状态
|
||||
*
|
||||
* @param id 交易订单项编号
|
||||
*/
|
||||
void updateOrderItemWhenAfterSaleCancel(@NotNull Long id);
|
||||
|
||||
/**
|
||||
* 【会员】创建订单项的评论
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param createReqVO 创建请求
|
||||
* @return 得到评价 id
|
||||
*/
|
||||
Long createOrderItemComment(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO);
|
||||
Long createOrderItemCommentByMember(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 【系统】创建订单项的评论
|
||||
*
|
||||
* @return 被评论的订单数
|
||||
*/
|
||||
int createOrderItemCommentBySystem();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@ package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
import cn.iocoder.yudao.module.member.api.address.AddressApi;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
|
||||
@@ -39,6 +41,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettle
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
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;
|
||||
@@ -62,6 +65,7 @@ import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
|
||||
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
@@ -70,13 +74,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.minusTime;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
@@ -321,39 +323,40 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
|
||||
public void updateOrderPaid(Long id, Long payOrderId) {
|
||||
// 校验并获得交易订单(可支付)
|
||||
// 1. 校验并获得交易订单(可支付)
|
||||
KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
|
||||
TradeOrderDO order = orderResult.getKey();
|
||||
PayOrderRespDTO payOrder = orderResult.getValue();
|
||||
|
||||
// 更新 TradeOrderDO 状态为已支付,等待发货
|
||||
// 2. 更新 TradeOrderDO 状态为已支付,等待发货
|
||||
int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
|
||||
new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true)
|
||||
.setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
|
||||
if (updateCount == 0) {
|
||||
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
|
||||
}
|
||||
// 订单支付成功后
|
||||
|
||||
// 3、订单支付成功后
|
||||
tradeOrderHandlers.forEach(tradeOrderHandler -> tradeOrderHandler.afterPayOrder(new TradeAfterPayOrderReqBO()
|
||||
.setOrderId(order.getId()).setOrderType(order.getType()).setUserId(order.getUserId()).setPayTime(LocalDateTime.now())));
|
||||
// TODO 芋艿:发送订单变化的消息
|
||||
|
||||
// TODO 芋艿:发送站内信
|
||||
|
||||
// TODO 芋艿:OrderLog
|
||||
|
||||
// 增加用户积分(赠送)
|
||||
addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_REWARD, order.getId());
|
||||
// 增加用户经验
|
||||
// 4.1 增加用户积分(赠送)
|
||||
addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId());
|
||||
// 4.2 增加用户经验
|
||||
getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId());
|
||||
// 增加用户佣金
|
||||
// 4.3 增加用户佣金
|
||||
getSelf().addBrokerageAsync(order.getUserId(), order.getId());
|
||||
|
||||
// 5. 记录订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
|
||||
TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验交易订单满足被支付的条件
|
||||
*
|
||||
* <p>
|
||||
* 1. 交易订单未支付
|
||||
* 2. 支付单已支付
|
||||
*
|
||||
@@ -406,6 +409,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY)
|
||||
public void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) {
|
||||
// 1.1 校验并获得交易订单(可发货)
|
||||
TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
|
||||
@@ -417,8 +421,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
// 2. 更新订单为已发货
|
||||
TradeOrderDO updateOrderObj = new TradeOrderDO();
|
||||
// 2.1 快递发货
|
||||
DeliveryExpressDO express = null;
|
||||
if (ObjectUtil.notEqual(deliveryReqVO.getLogisticsId(), TradeOrderDO.LOGISTICS_ID_NULL)) {
|
||||
deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
|
||||
express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
|
||||
updateOrderObj.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
|
||||
} else {
|
||||
// 2.2 无需发货
|
||||
@@ -431,18 +436,19 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
|
||||
}
|
||||
|
||||
// TODO 芋艿:发送订单变化的消息
|
||||
// 3. 记录订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus(),
|
||||
MapUtil.<String, Object>builder().put("expressName", express != null ? express.getName() : "")
|
||||
.put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build());
|
||||
|
||||
// 发送站内信
|
||||
// 4. 发送站内信
|
||||
tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO().setOrderId(order.getId())
|
||||
.setUserId(order.getUserId()).setMessage(null));
|
||||
|
||||
// TODO 芋艿:OrderLog
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验交易订单满足被发货的条件
|
||||
*
|
||||
* <p>
|
||||
* 1. 交易订单未发货
|
||||
*
|
||||
* @param id 交易订单编号
|
||||
@@ -464,6 +470,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
// 订单类类型:砍价
|
||||
if (Objects.equals(TradeOrderTypeEnum.BARGAIN.getType(), order.getType())) {
|
||||
// 校验订单砍价是否成功
|
||||
// TODO @puhui999:砍价的话,应该不用校验。因为是砍价成功后,才可以下单
|
||||
if (!bargainRecordApi.isBargainRecordSuccess(order.getUserId(), order.getId())) {
|
||||
throw exception(ORDER_DELIVERY_FAIL_BARGAIN_RECORD_STATUS_NOT_SUCCESS);
|
||||
}
|
||||
@@ -484,10 +491,54 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_RECEIVE)
|
||||
public void receiveOrder(Long userId, Long id) {
|
||||
public void receiveOrderByMember(Long userId, Long id) {
|
||||
// 校验并获得交易订单(可收货)
|
||||
TradeOrderDO order = validateOrderReceivable(userId, id);
|
||||
|
||||
// 收货订单
|
||||
receiveOrder0(order);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int receiveOrderBySystem() {
|
||||
// 1. 查询过期的待支付订单
|
||||
LocalDateTime expireTime = minusTime(tradeOrderProperties.getReceiveExpireTime());
|
||||
List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndDeliveryTimeLt(
|
||||
TradeOrderStatusEnum.DELIVERED.getStatus(), expireTime);
|
||||
if (CollUtil.isEmpty(orders)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. 遍历执行,逐个取消
|
||||
int count = 0;
|
||||
for (TradeOrderDO order : orders) {
|
||||
try {
|
||||
getSelf().receiveOrderBySystem(order);
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[receiveOrderBySystem][order({}) 自动收货订单异常]", order.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动收货单个订单
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_RECEIVE)
|
||||
public void receiveOrderBySystem(TradeOrderDO order) {
|
||||
receiveOrder0(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收货订单的核心实现
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
private void receiveOrder0(TradeOrderDO order) {
|
||||
// 更新 TradeOrderDO 状态为已完成
|
||||
int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
|
||||
new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now()));
|
||||
@@ -495,114 +546,13 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
|
||||
}
|
||||
|
||||
// TODO 芋艿:lili 发送订单变化的消息
|
||||
|
||||
// TODO 芋艿:lili 发送商品被购买完成的数据
|
||||
|
||||
// TODO 芋艿:销售佣金的记录;
|
||||
|
||||
// 插入订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderRemark(TradeOrderRemarkReqVO reqVO) {
|
||||
// 校验并获得交易订单
|
||||
validateOrderExists(reqVO.getId());
|
||||
|
||||
// 更新
|
||||
TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(reqVO);
|
||||
tradeOrderMapper.updateById(order);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) {
|
||||
// 1、校验交易订单
|
||||
TradeOrderDO order = validateOrderExists(reqVO.getId());
|
||||
if (order.getPayStatus()) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_PAID);
|
||||
}
|
||||
// 2、校验订单项
|
||||
List<TradeOrderItemDO> items = tradeOrderItemMapper.selectListByOrderId(order.getId());
|
||||
if (CollUtil.isEmpty(items)) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_NOT_ITEM);
|
||||
}
|
||||
// 3、校验调价金额是否变化
|
||||
if (ObjectUtil.equal(order.getAdjustPrice(), reqVO.getAdjustPrice())) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_EQUAL);
|
||||
}
|
||||
|
||||
// 4、更新订单
|
||||
TradeOrderDO update = new TradeOrderDO();
|
||||
update.setId(order.getId());
|
||||
update.setAdjustPrice(reqVO.getAdjustPrice());
|
||||
int orderPayPrice = order.getAdjustPrice() != null ? (order.getPayPrice() - order.getAdjustPrice())
|
||||
+ reqVO.getAdjustPrice() : order.getPayPrice() + reqVO.getAdjustPrice();
|
||||
update.setPayPrice(orderPayPrice);
|
||||
tradeOrderMapper.updateById(update);
|
||||
// TODO @芋艿:改价时,赠送的积分,要不要做改动???
|
||||
|
||||
// 5、更新 TradeOrderItem
|
||||
// TradeOrderItemDO 需要做 adjustPrice 的分摊
|
||||
List<Integer> dividePrices = dividePrice(items, orderPayPrice);
|
||||
List<TradeOrderItemDO> updateItems = new ArrayList<>();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
TradeOrderItemDO item = items.get(i);
|
||||
Integer adjustPrice = item.getPrice() - dividePrices.get(i); // 计算调整的金额
|
||||
updateItems.add(new TradeOrderItemDO().setId(item.getId()).setAdjustPrice(adjustPrice)
|
||||
.setPayPrice(item.getPayPrice() - adjustPrice));
|
||||
}
|
||||
tradeOrderItemMapper.updateBatch(updateItems);
|
||||
|
||||
|
||||
// 6、更新支付订单
|
||||
payOrderApi.updatePayOrderPrice(order.getPayOrderId(), update.getPayPrice());
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单调价价格分摊
|
||||
*
|
||||
* @param items 订单项
|
||||
* @param orderPayPrice 订单支付金额
|
||||
* @return 分摊金额数组,和传入的 orderItems 一一对应
|
||||
*/
|
||||
private List<Integer> dividePrice(List<TradeOrderItemDO> items, Integer orderPayPrice) {
|
||||
Integer total = getSumValue(items, TradeOrderItemDO::getPrice, Integer::sum);
|
||||
assert total != null;
|
||||
// 遍历每一个,进行分摊
|
||||
List<Integer> prices = new ArrayList<>(items.size());
|
||||
int remainPrice = orderPayPrice;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
TradeOrderItemDO orderItem = items.get(i);
|
||||
int partPrice;
|
||||
if (i < items.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减
|
||||
partPrice = (int) (orderPayPrice * (1.0D * orderItem.getPayPrice() / total));
|
||||
remainPrice -= partPrice;
|
||||
} else {
|
||||
partPrice = remainPrice;
|
||||
}
|
||||
Assert.isTrue(partPrice >= 0, "分摊金额必须大于等于 0");
|
||||
prices.add(partPrice);
|
||||
}
|
||||
return prices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
|
||||
// 校验交易订单
|
||||
validateOrderExists(reqVO.getId());
|
||||
// TODO 是否需要校验订单是否发货
|
||||
// TODO 发货后是否支持修改收货地址
|
||||
|
||||
// 更新
|
||||
TradeOrderDO update = TradeOrderConvert.INSTANCE.convert(reqVO);
|
||||
tradeOrderMapper.updateById(update);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验交易订单满足可售货的条件
|
||||
*
|
||||
* <p>
|
||||
* 1. 交易订单待收货
|
||||
*
|
||||
* @param userId 用户编号
|
||||
@@ -622,103 +572,10 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
return order;
|
||||
}
|
||||
|
||||
// =================== Order Item ===================
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
|
||||
Long afterSaleId, Integer refundPrice) {
|
||||
// 如果退款成功,则 refundPrice 非空
|
||||
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())
|
||||
&& refundPrice == null) {
|
||||
throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
|
||||
}
|
||||
// 如果退款发起,则 afterSaleId 非空
|
||||
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus())
|
||||
&& afterSaleId == null) {
|
||||
throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id));
|
||||
}
|
||||
|
||||
// 更新订单项
|
||||
int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
|
||||
if (updateCount <= 0) {
|
||||
throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
|
||||
}
|
||||
|
||||
// 如果有退款金额,则需要更新订单
|
||||
if (refundPrice == null) {
|
||||
return;
|
||||
}
|
||||
// 计算总的退款金额
|
||||
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
|
||||
TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
|
||||
Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
|
||||
if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice).setRefundPoint(order.getRefundPoint() + orderItem.getUsePoint())
|
||||
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
|
||||
|
||||
// TODO 芋艿:记录订单日志
|
||||
|
||||
// TODO 芋艿:站内信?
|
||||
} else { // 如果部分售后,则更新退款金额
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
|
||||
}
|
||||
|
||||
// 售后成功后,执行数据回滚逻辑
|
||||
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
|
||||
// 扣减用户积分(赠送的)
|
||||
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, afterSaleId);
|
||||
// 增加用户积分(返还抵扣)
|
||||
addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, afterSaleId);
|
||||
// 扣减用户经验
|
||||
getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId);
|
||||
// 更新分佣记录为已失效
|
||||
getSelf().cancelBrokerageAsync(order.getUserId(), id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT)
|
||||
public Long createOrderItemComment(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO) {
|
||||
// 先通过订单项 ID,查询订单项是否存在
|
||||
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId);
|
||||
if (orderItem == null) {
|
||||
throw exception(ORDER_ITEM_NOT_FOUND);
|
||||
}
|
||||
// 校验订单相关状态
|
||||
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderItem.getOrderId(), userId);
|
||||
if (order == null) {
|
||||
throw exception(ORDER_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
|
||||
throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED);
|
||||
}
|
||||
if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
|
||||
throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
|
||||
}
|
||||
|
||||
// 1. 创建评价
|
||||
ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
|
||||
Long comment = productCommentApi.createComment(productCommentCreateReqDTO);
|
||||
|
||||
// 2. 更新订单项评价状态
|
||||
tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
|
||||
if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) {
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE));
|
||||
// 增加订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
|
||||
public void cancelOrder(Long userId, Long id) {
|
||||
public void cancelOrderByMember(Long userId, Long id) {
|
||||
// 1.1 校验存在
|
||||
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
|
||||
if (order == null) {
|
||||
@@ -729,34 +586,102 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
|
||||
}
|
||||
|
||||
// 2. 更新 TradeOrderDO 状态为已取消
|
||||
// 2. 取消订单
|
||||
cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int cancelOrderBySystem() {
|
||||
// 1. 查询过期的待支付订单
|
||||
LocalDateTime expireTime = minusTime(tradeOrderProperties.getPayExpireTime());
|
||||
List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndCreateTimeLt(
|
||||
TradeOrderStatusEnum.UNPAID.getStatus(), expireTime);
|
||||
if (CollUtil.isEmpty(orders)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. 遍历执行,逐个取消
|
||||
int count = 0;
|
||||
for (TradeOrderDO order : orders) {
|
||||
try {
|
||||
getSelf().cancelOrderBySystem(order);
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[cancelOrderBySystem][order({}) 过期订单异常]", order.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动取消单个订单
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
|
||||
public void cancelOrderBySystem(TradeOrderDO order) {
|
||||
cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订单的核心实现
|
||||
*
|
||||
* @param order 订单
|
||||
* @param cancelType 取消类型
|
||||
*/
|
||||
private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
|
||||
Long id = order.getId();
|
||||
// 1. 更新 TradeOrderDO 状态为已取消
|
||||
int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
|
||||
new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
|
||||
.setCancelTime(LocalDateTime.now())
|
||||
.setCancelType(TradeOrderCancelTypeEnum.MEMBER_CANCEL.getType()));
|
||||
.setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
|
||||
if (updateCount == 0) {
|
||||
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
|
||||
}
|
||||
|
||||
// 2. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀
|
||||
tradeOrderHandlers.forEach(handler -> handler.rollback());
|
||||
// 3. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀
|
||||
tradeOrderHandlers.forEach(handler -> handler.cancelOrder());
|
||||
|
||||
// 4. 回滚库存
|
||||
// 3. 回滚库存
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
|
||||
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
|
||||
|
||||
// 5. 回滚优惠券
|
||||
if (order.getCouponId() > 0) {
|
||||
// 4. 回滚优惠券
|
||||
if (order.getCouponId() != null && order.getCouponId() > 0) {
|
||||
couponApi.returnUsedCoupon(order.getCouponId());
|
||||
}
|
||||
|
||||
// 6. 回滚积分(抵扣的)
|
||||
// 5. 回滚积分(抵扣的)
|
||||
addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId());
|
||||
|
||||
// 7. 增加订单日志
|
||||
// 6. 增加订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果金额全部被退款,则取消订单
|
||||
* 如果还有未被退款的金额,则无需取消订单
|
||||
*
|
||||
* @param order 订单
|
||||
* @param refundPrice 退款金额
|
||||
*/
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_CANCEL_AFTER_SALE)
|
||||
public void cancelOrderByAfterSale(TradeOrderDO order, Integer refundPrice) {
|
||||
// 1. 更新订单
|
||||
if (refundPrice < order.getPayPrice()) {
|
||||
return;
|
||||
}
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
|
||||
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
|
||||
|
||||
// 2. 退还优惠券
|
||||
couponApi.returnUsedCoupon(order.getCouponId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
|
||||
@@ -777,6 +702,135 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderRemark(TradeOrderRemarkReqVO reqVO) {
|
||||
// 校验并获得交易订单
|
||||
validateOrderExists(reqVO.getId());
|
||||
|
||||
// 更新
|
||||
TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(reqVO);
|
||||
tradeOrderMapper.updateById(order);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_PRICE)
|
||||
public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) {
|
||||
// 1.1 校验交易订单
|
||||
TradeOrderDO order = validateOrderExists(reqVO.getId());
|
||||
if (order.getPayStatus()) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_PAID);
|
||||
}
|
||||
// 1.2 校验调价金额是否变化
|
||||
if (order.getAdjustPrice() > 0) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_ALREADY);
|
||||
}
|
||||
// 1.3 支付价格不能为 0
|
||||
int newPayPrice = order.getPayPrice() + order.getAdjustPrice();
|
||||
if (newPayPrice <= 0) {
|
||||
throw exception(ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR);
|
||||
}
|
||||
|
||||
// 2. 更新订单
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setAdjustPrice(reqVO.getAdjustPrice()).setPayPrice(newPayPrice));
|
||||
|
||||
// 3. 更新 TradeOrderItem,需要做 adjustPrice 的分摊
|
||||
List<TradeOrderItemDO> orderOrderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
|
||||
List<Integer> dividePrices = TradePriceCalculatorHelper.dividePrice2(orderOrderItems, newPayPrice);
|
||||
List<TradeOrderItemDO> updateItems = new ArrayList<>();
|
||||
for (int i = 0; i < orderOrderItems.size(); i++) {
|
||||
TradeOrderItemDO item = orderOrderItems.get(i);
|
||||
updateItems.add(new TradeOrderItemDO().setId(item.getId()).setAdjustPrice(dividePrices.get(i))
|
||||
.setPayPrice(item.getPayPrice() + dividePrices.get(i)));
|
||||
}
|
||||
tradeOrderItemMapper.updateBatch(updateItems);
|
||||
|
||||
// 4. 更新支付订单
|
||||
payOrderApi.updatePayOrderPrice(order.getPayOrderId(), newPayPrice);
|
||||
|
||||
// 5. 记录订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus(),
|
||||
MapUtil.<String, Object>builder().put("oldPayPrice", MoneyUtils.fenToYuanStr(order.getPayPrice()))
|
||||
.put("newPayPrice", MoneyUtils.fenToYuanStr(newPayPrice)).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
|
||||
// 校验交易订单
|
||||
validateOrderExists(reqVO.getId());
|
||||
// TODO @puhui999:是否需要校验订单是否发货
|
||||
// TODO 发货后是否支持修改收货地址
|
||||
|
||||
// 更新
|
||||
tradeOrderMapper.updateById(TradeOrderConvert.INSTANCE.convert(reqVO));
|
||||
|
||||
// TODO @puhui999:操作日志
|
||||
}
|
||||
|
||||
// =================== Order Item ===================
|
||||
|
||||
@Override
|
||||
public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
|
||||
// 更新订单项
|
||||
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), afterSaleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
|
||||
// 1. 更新订单项
|
||||
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
|
||||
|
||||
// 2.1 更新订单的退款金额、积分
|
||||
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
|
||||
TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
|
||||
Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
|
||||
Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint();
|
||||
Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ?
|
||||
TradeOrderRefundStatusEnum.ALL.getStatus() // 如果都售后成功,则需要取消订单
|
||||
: TradeOrderRefundStatusEnum.PART.getStatus();
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setRefundStatus(refundStatus)
|
||||
.setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
|
||||
// 2.2 如果全部退款,则进行取消订单
|
||||
getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
|
||||
|
||||
// TODO @puhui999:活动相关的回滚
|
||||
|
||||
// 3. 回滚库存
|
||||
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
|
||||
|
||||
// 4.1 回滚积分:扣减用户积分(赠送的)
|
||||
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
|
||||
// 4.2 回滚积分:增加用户积分(返还抵扣)
|
||||
addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId());
|
||||
|
||||
// 5. 回滚经验:扣减用户经验
|
||||
getSelf().reduceUserExperienceAsync(order.getUserId(), refundPrice, orderItem.getAfterSaleId());
|
||||
|
||||
// 6. 回滚佣金:更新分佣记录为已失效
|
||||
getSelf().cancelBrokerageAsync(order.getUserId(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderItemWhenAfterSaleCancel(Long id) {
|
||||
// 更新订单项
|
||||
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
|
||||
}
|
||||
|
||||
private void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
|
||||
Long afterSaleId) {
|
||||
// 更新订单项
|
||||
int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
|
||||
if (updateCount <= 0) {
|
||||
throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定订单的所有订单项,是不是都售后成功
|
||||
*
|
||||
@@ -789,6 +843,116 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT)
|
||||
public Long createOrderItemCommentByMember(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO) {
|
||||
// 1.1 先通过订单项 ID,查询订单项是否存在
|
||||
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId);
|
||||
if (orderItem == null) {
|
||||
throw exception(ORDER_ITEM_NOT_FOUND);
|
||||
}
|
||||
// 1.2 校验订单相关状态
|
||||
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderItem.getOrderId(), userId);
|
||||
if (order == null) {
|
||||
throw exception(ORDER_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
|
||||
throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED);
|
||||
}
|
||||
if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
|
||||
throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
|
||||
}
|
||||
|
||||
// 2. 创建评价
|
||||
Long commentId = createOrderItemComment0(orderItem, createReqVO);
|
||||
|
||||
// 3. 如果订单项都评论了,则更新订单评价状态
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
|
||||
if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) {
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
|
||||
.setFinishTime(LocalDateTime.now()));
|
||||
// 增加订单日志。注意:只有在所有订单项都评价后,才会增加
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
|
||||
}
|
||||
return commentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int createOrderItemCommentBySystem() {
|
||||
// 1. 查询过期的待支付订单
|
||||
LocalDateTime expireTime = minusTime(tradeOrderProperties.getCommentExpireTime());
|
||||
List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndReceiveTimeLt(
|
||||
TradeOrderStatusEnum.COMPLETED.getStatus(), expireTime, false);
|
||||
if (CollUtil.isEmpty(orders)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. 遍历执行,逐个取消
|
||||
int count = 0;
|
||||
for (TradeOrderDO order : orders) {
|
||||
try {
|
||||
getSelf().createOrderItemCommentBySystemBySystem(order);
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[createOrderItemCommentBySystem][order({}) 过期订单异常]", order.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个订单的评论
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT)
|
||||
public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) {
|
||||
// 1. 查询未评论的订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus(order.getId(), Boolean.FALSE);
|
||||
if (CollUtil.isEmpty(orderItems)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 逐个评论
|
||||
for (TradeOrderItemDO orderItem : orderItems) {
|
||||
// 2.1 创建评价
|
||||
AppTradeOrderItemCommentCreateReqVO commentCreateReqVO = new AppTradeOrderItemCommentCreateReqVO()
|
||||
.setOrderItemId(orderItem.getId()).setAnonymous(false).setContent("")
|
||||
.setBenefitScores(5).setDescriptionScores(5);
|
||||
createOrderItemComment0(orderItem, commentCreateReqVO);
|
||||
|
||||
// 2.2 更新订单项评价状态
|
||||
tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
|
||||
}
|
||||
|
||||
// 3. 所有订单项都评论了,则更新订单评价状态
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
|
||||
.setFinishTime(LocalDateTime.now()));
|
||||
// 增加订单日志。注意:只有在所有订单项都评价后,才会增加
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建订单项的评论的核心实现
|
||||
*
|
||||
* @param orderItem 订单项
|
||||
* @param createReqVO 评论内容
|
||||
* @return 评论编号
|
||||
*/
|
||||
private Long createOrderItemComment0(TradeOrderItemDO orderItem, AppTradeOrderItemCommentCreateReqVO createReqVO) {
|
||||
// 1. 创建评价
|
||||
ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
|
||||
Long commentId = productCommentApi.createComment(productCommentCreateReqDTO);
|
||||
|
||||
// 2. 更新订单项评价状态
|
||||
tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
|
||||
return commentId;
|
||||
}
|
||||
|
||||
// =================== 营销相关的操作 ===================
|
||||
|
||||
@Async
|
||||
protected void addUserExperienceAsync(Long userId, Integer payPrice, Long orderId) {
|
||||
int bizType = MemberExperienceBizTypeEnum.ORDER.getType();
|
||||
@@ -829,14 +993,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
/**
|
||||
* 创建分销记录
|
||||
*
|
||||
* <p>
|
||||
* 目前是支付成功后,就会创建分销记录。
|
||||
*
|
||||
* <p>
|
||||
* 业内还有两种做法,可以根据自己的业务调整:
|
||||
* 1. 确认收货后,才创建分销记录
|
||||
* 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻
|
||||
* 1. 确认收货后,才创建分销记录
|
||||
* 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userId 用户编号
|
||||
* @param orderId 订单编号
|
||||
*/
|
||||
@Async
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.Optional;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
|
||||
// TODO @疯狂:这个可以搞个单测;
|
||||
/**
|
||||
* 赠送积分的 {@link TradePriceCalculator} 实现类
|
||||
*
|
||||
@@ -25,6 +26,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||
@Order(TradePriceCalculator.ORDER_POINT_GIVE)
|
||||
@Slf4j
|
||||
public class TradePointGiveCalculator implements TradePriceCalculator {
|
||||
|
||||
@Resource
|
||||
private MemberPointApi memberPointApi;
|
||||
|
||||
@@ -54,9 +56,9 @@ public class TradePointGiveCalculator implements TradePriceCalculator {
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i);
|
||||
// 商品可能赠送了积分,所以这里要加上
|
||||
orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i));
|
||||
TradePriceCalculatorHelper.recountPayPrice(orderItem);
|
||||
}
|
||||
// 3.3 更新订单赠送积分
|
||||
TradePriceCalculatorHelper.recountAllGivePoint(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
|
||||
import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO;
|
||||
@@ -15,11 +14,13 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
|
||||
|
||||
// TODO @疯狂:搞个单测,嘿嘿;
|
||||
/**
|
||||
* 使用积分的 {@link TradePriceCalculator} 实现类
|
||||
*
|
||||
@@ -29,6 +30,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||
@Order(TradePriceCalculator.ORDER_POINT_USE)
|
||||
@Slf4j
|
||||
public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
|
||||
@Resource
|
||||
private MemberPointApi memberPointApi;
|
||||
@Resource
|
||||
@@ -43,18 +45,18 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
}
|
||||
// 1.2 校验积分抵扣是否开启
|
||||
MemberPointConfigRespDTO config = memberPointApi.getConfig();
|
||||
if (!checkDeductPointEnable(config)) {
|
||||
if (!isDeductPointEnable(config)) {
|
||||
return;
|
||||
}
|
||||
// 1.3 校验用户积分余额
|
||||
MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
|
||||
if (user.getPoint() == null || user.getPoint() < 0) {
|
||||
if (user.getPoint() == null || user.getPoint() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.1 计算积分优惠金额
|
||||
int pointPrice = calculatePointPrice(config, user.getPoint(), result);
|
||||
// 2.1 计算分摊的积分、抵扣金额
|
||||
// 2.2 计算分摊的积分、抵扣金额
|
||||
List<TradePriceCalculateRespBO.OrderItem> orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected);
|
||||
List<Integer> dividePointPrices = TradePriceCalculatorHelper.dividePrice(orderItems, pointPrice);
|
||||
List<Integer> divideUsePoints = TradePriceCalculatorHelper.dividePrice(orderItems, result.getUsePoint());
|
||||
@@ -74,16 +76,10 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
}
|
||||
|
||||
private boolean checkDeductPointEnable(MemberPointConfigRespDTO config) {
|
||||
if (config == null) {
|
||||
return false;
|
||||
}
|
||||
if (!BooleanUtil.isTrue(config.getTradeDeductEnable())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 有没有配置:1 积分抵扣多少分
|
||||
return config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0;
|
||||
private boolean isDeductPointEnable(MemberPointConfigRespDTO config) {
|
||||
return config != null &&
|
||||
!BooleanUtil.isTrue(config.getTradeDeductEnable()) && // 积分功能是否启用
|
||||
config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0; // 有没有配置:1 积分抵扣多少分
|
||||
}
|
||||
|
||||
private Integer calculatePointPrice(MemberPointConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) {
|
||||
@@ -91,19 +87,24 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
if (config.getTradeDeductMaxPrice() != null && config.getTradeDeductMaxPrice() > 0) {
|
||||
usePoint = Math.min(usePoint, config.getTradeDeductMaxPrice());
|
||||
}
|
||||
// TODO @疯狂:这里应该是,抵扣到只剩下 0.01;
|
||||
// 积分优惠金额(分)
|
||||
int pointPrice = usePoint * config.getTradeDeductUnitPrice();
|
||||
// 0元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额, 这时需要根据支付金额反推使用多少积分
|
||||
if (result.getPrice().getPayPrice() < pointPrice) {
|
||||
pointPrice = result.getPrice().getPayPrice();
|
||||
// 反推需要扣除的积分
|
||||
usePoint = NumberUtil.toBigDecimal(pointPrice)
|
||||
.divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP)
|
||||
.intValue();
|
||||
if (result.getPrice().getPayPrice() <= pointPrice) {
|
||||
// 禁止 0 元购
|
||||
throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
|
||||
}
|
||||
// // 允许0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分
|
||||
// if (result.getPrice().getPayPrice() < pointPrice) {
|
||||
// pointPrice = result.getPrice().getPayPrice();
|
||||
// // 反推需要扣除的积分
|
||||
// usePoint = NumberUtil.toBigDecimal(pointPrice)
|
||||
// .divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP)
|
||||
// .intValue();
|
||||
// }
|
||||
// 记录使用的积分
|
||||
result.setUsePoint(usePoint);
|
||||
|
||||
return pointPrice;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,11 +21,13 @@ public interface TradePriceCalculator {
|
||||
/**
|
||||
* 快递运费的计算
|
||||
*
|
||||
* 放在各种营销活动、优惠劵后面 TODO
|
||||
* 放在各种营销活动、优惠劵后面
|
||||
*/
|
||||
int ORDER_DELIVERY = 50;
|
||||
/**
|
||||
* 赠送积分,放最后
|
||||
*
|
||||
* 放在 {@link #ORDER_DELIVERY} 后面的原因,是运费也会产生费用,需要赠送对应积分
|
||||
*/
|
||||
int ORDER_POINT_GIVE = 999;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
@@ -202,6 +203,8 @@ public class TradePriceCalculatorHelper {
|
||||
/**
|
||||
* 按照支付金额,返回每个订单项的分摊金额数组
|
||||
*
|
||||
* 实际上 price 不仅仅可以传递的是金额,也可以是积分。因为它的实现逻辑,就是根据 payPrice 做分摊而已
|
||||
*
|
||||
* @param orderItems 订单项数组
|
||||
* @param price 金额
|
||||
* @return 分摊金额数组,和传入的 orderItems 一一对应
|
||||
@@ -233,6 +236,36 @@ public class TradePriceCalculatorHelper {
|
||||
return prices;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单调价价格分摊
|
||||
*
|
||||
* 和 {@link #dividePrice(List, Integer)} 逻辑一致,只是传入的是 TradeOrderItemDO 对象
|
||||
*
|
||||
* @param items 订单项
|
||||
* @param price 订单支付金额
|
||||
* @return 分摊金额数组,和传入的 orderItems 一一对应
|
||||
*/
|
||||
public static List<Integer> dividePrice2(List<TradeOrderItemDO> items, Integer price) {
|
||||
Integer total = getSumValue(items, TradeOrderItemDO::getPrice, Integer::sum);
|
||||
assert total != null;
|
||||
// 遍历每一个,进行分摊
|
||||
List<Integer> prices = new ArrayList<>(items.size());
|
||||
int remainPrice = price;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
TradeOrderItemDO orderItem = items.get(i);
|
||||
int partPrice;
|
||||
if (i < items.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减
|
||||
partPrice = (int) (price * (1.0D * orderItem.getPayPrice() / total));
|
||||
remainPrice -= partPrice;
|
||||
} else {
|
||||
partPrice = remainPrice;
|
||||
}
|
||||
Assert.isTrue(partPrice >= 0, "分摊金额必须大于等于 0");
|
||||
prices.add(partPrice);
|
||||
}
|
||||
return prices;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加【匹配】单个 OrderItem 的营销明细
|
||||
*
|
||||
|
||||
@@ -4,35 +4,38 @@
|
||||
|
||||
<select id="selectSummaryPageByUserId"
|
||||
resultType="cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO">
|
||||
SELECT bu.id, bu.bind_user_time AS brokerageTime, u.nickname, u.avatar,
|
||||
SELECT bu.id, bu.bind_user_time AS brokerageTime,
|
||||
(SELECT SUM(price) FROM trade_brokerage_record r
|
||||
WHERE r.user_id = u.id AND biz_type = 1 AND r.status = 1 AND r.deleted = FALSE) AS brokeragePrice,
|
||||
WHERE r.user_id = bu.id AND biz_type = #{bizType} AND r.status = #{status} AND r.deleted = FALSE) AS brokeragePrice,
|
||||
(SELECT COUNT(1) FROM trade_brokerage_record r
|
||||
WHERE r.user_id = u.id AND biz_type = 1 AND r.status = 1 AND r.deleted = FALSE) AS brokerageOrderCount,
|
||||
WHERE r.user_id = bu.id AND biz_type = #{bizType} AND r.status = #{status} AND r.deleted = FALSE) AS brokerageOrderCount,
|
||||
(SELECT COUNT(1) FROM trade_brokerage_user c
|
||||
WHERE c.bind_user_id = u.id AND c.deleted = FALSE) AS brokerageUserCount
|
||||
FROM member_user AS u
|
||||
JOIN trade_brokerage_user AS bu ON bu.id = u.id
|
||||
WHERE c.bind_user_id = bu.id AND c.deleted = FALSE) AS brokerageUserCount
|
||||
FROM trade_brokerage_user AS bu
|
||||
<where>
|
||||
<if test="param.nickname != null and param.nickname != ''">
|
||||
AND u.nickname LIKE concat('', #{param.nickname}, '')
|
||||
bu.deleted = false
|
||||
<if test="ids != null and ids.size() > 0">
|
||||
and bu.id in
|
||||
<foreach collection="ids" open="(" item="id" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="param.level == 1">
|
||||
AND bu.bind_user_id = #{userId}
|
||||
</if>
|
||||
<if test="param.level == 2">
|
||||
AND bu.bind_user_id IN (SELECT id FROM trade_brokerage_user c WHERE c.bind_user_id = #{userId})
|
||||
<if test="bindUserIds != null and bindUserIds.size() > 0">
|
||||
and bu.bind_user_id in
|
||||
<foreach collection="bindUserIds" open="(" item="bindUserId" separator="," close=")">
|
||||
#{bindUserId}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
<choose>
|
||||
<when test="param.sortingField.field == 'userCount'">
|
||||
ORDER BY brokerageUserCount ${param.sortingField.order}
|
||||
<when test="sortingField.field == 'userCount'">
|
||||
ORDER BY brokerageUserCount ${sortingField.order}
|
||||
</when>
|
||||
<when test="param.sortingField.field == 'orderCount'">
|
||||
ORDER BY brokerageOrderCount ${param.sortingField.order}
|
||||
<when test="sortingField.field == 'orderCount'">
|
||||
ORDER BY brokerageOrderCount ${sortingField.order}
|
||||
</when>
|
||||
<when test="param.sortingField.field == 'price'">
|
||||
ORDER BY brokeragePrice ${param.sortingField.order}
|
||||
<when test="sortingField.field == 'price'">
|
||||
ORDER BY brokeragePrice ${sortingField.order}
|
||||
</when>
|
||||
<otherwise>
|
||||
ORDER BY bu.bind_user_time DESC
|
||||
|
||||
@@ -38,7 +38,7 @@ public class KdNiaoExpressClientIntegrationTest {
|
||||
public void testGetExpressTrackList() {
|
||||
ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
|
||||
reqDTO.setExpressCode("STO");
|
||||
reqDTO.setLogisticsNo("663220402764314");
|
||||
reqDTO.setLogisticsNo("777168349863987");
|
||||
List<ExpressTrackRespDTO> tracks = client.getExpressTrackList(reqDTO);
|
||||
System.out.println(JsonUtils.toJsonPrettyString(tracks));
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(tradeOrderProperties.getAppId()).thenReturn(888L);
|
||||
when(tradeOrderProperties.getExpireTime()).thenReturn(Duration.ofDays(1));
|
||||
when(tradeOrderProperties.getPayExpireTime()).thenReturn(Duration.ofDays(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -306,7 +306,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
|
||||
// mock 方法(支付单)
|
||||
|
||||
// 调用
|
||||
tradeOrderUpdateService.receiveOrder(userId, id);
|
||||
tradeOrderUpdateService.receiveOrderByMember(userId, id);
|
||||
// 断言
|
||||
TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L);
|
||||
assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
|
||||
|
||||
Reference in New Issue
Block a user