【同步】BOOT 和 CLOUD 的功能
This commit is contained in:
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.aftersale;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;
|
||||
@@ -32,7 +31,6 @@ public class AppAfterSaleController {
|
||||
|
||||
@GetMapping(value = "/page")
|
||||
@Operation(summary = "获得售后分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppAfterSaleRespVO>> getAfterSalePage(PageParam pageParam) {
|
||||
return success(AfterSaleConvert.INSTANCE.convertPage02(
|
||||
afterSaleService.getAfterSalePage(getLoginUserId(), pageParam)));
|
||||
@@ -41,21 +39,18 @@ public class AppAfterSaleController {
|
||||
@GetMapping(value = "/get")
|
||||
@Operation(summary = "获得售后订单")
|
||||
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppAfterSaleRespVO> getAfterSale(@RequestParam("id") Long id) {
|
||||
return success(AfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id)));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/create")
|
||||
@Operation(summary = "申请售后")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> createAfterSale(@RequestBody AppAfterSaleCreateReqVO createReqVO) {
|
||||
return success(afterSaleService.createAfterSale(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping(value = "/delivery")
|
||||
@Operation(summary = "退回货物")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> deliveryAfterSale(@RequestBody AppAfterSaleDeliveryReqVO deliveryReqVO) {
|
||||
afterSaleService.deliveryAfterSale(getLoginUserId(), deliveryReqVO);
|
||||
return success(true);
|
||||
@@ -64,7 +59,6 @@ public class AppAfterSaleController {
|
||||
@DeleteMapping(value = "/cancel")
|
||||
@Operation(summary = "取消售后")
|
||||
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> cancelAfterSale(@RequestParam("id") Long id) {
|
||||
afterSaleService.cancelAfterSale(getLoginUserId(), id);
|
||||
return success(true);
|
||||
|
||||
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.controller.app.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.log.AppAfterSaleLogRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;
|
||||
import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService;
|
||||
@@ -34,7 +33,6 @@ public class AppAfterSaleLogController {
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得售后日志列表")
|
||||
@Parameter(name = "afterSaleId", description = "售后编号", required = true, example = "1")
|
||||
@PreAuthenticated
|
||||
public CommonResult<List<AppAfterSaleLogRespVO>> getAfterSaleLogList(
|
||||
@RequestParam("afterSaleId") Long afterSaleId) {
|
||||
List<AfterSaleLogDO> logs = afterSaleLogService.getAfterSaleLogList(afterSaleId);
|
||||
|
||||
@@ -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.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO;
|
||||
@@ -37,7 +36,6 @@ public class AppBrokerageRecordController {
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得分销记录分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageRecordRespVO>> getBrokerageRecordPage(@Valid AppBrokerageRecordPageReqVO pageReqVO) {
|
||||
PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(
|
||||
BrokerageRecordConvert.INSTANCE.convert(pageReqVO, getLoginUserId()));
|
||||
@@ -46,7 +44,6 @@ public class AppBrokerageRecordController {
|
||||
|
||||
@GetMapping("/get-product-brokerage-price")
|
||||
@Operation(summary = "获得商品的分销金额")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
|
||||
return success(brokerageRecordService.calculateProductBrokeragePrice(getLoginUserId(), spuId));
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.brokerage;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
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.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.*;
|
||||
@@ -55,7 +54,6 @@ public class AppBrokerageUserController {
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得个人分销信息")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() {
|
||||
Optional<BrokerageUserDO> user = Optional.ofNullable(brokerageUserService.getOrCreateBrokerageUser(getLoginUserId()));
|
||||
// 返回数据
|
||||
@@ -68,14 +66,12 @@ public class AppBrokerageUserController {
|
||||
|
||||
@PutMapping("/bind")
|
||||
@Operation(summary = "绑定推广员")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
|
||||
return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/get-summary")
|
||||
@Operation(summary = "获得个人分销统计")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
|
||||
// 查询当前登录用户信息
|
||||
Long userId = getLoginUserId();
|
||||
@@ -101,7 +97,6 @@ public class AppBrokerageUserController {
|
||||
|
||||
@GetMapping("/rank-page-by-user-count")
|
||||
@Operation(summary = "获得分销用户排行分页(基于用户量)")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageUserRankByUserCountRespVO>> getBrokerageUserRankPageByUserCount(AppBrokerageUserRankPageReqVO pageReqVO) {
|
||||
// 分页查询
|
||||
PageResult<AppBrokerageUserRankByUserCountRespVO> pageResult = brokerageUserService.getBrokerageUserRankPageByUserCount(pageReqVO);
|
||||
@@ -112,7 +107,6 @@ public class AppBrokerageUserController {
|
||||
|
||||
@GetMapping("/rank-page-by-price")
|
||||
@Operation(summary = "获得分销用户排行分页(基于佣金)")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageUserRankByPriceRespVO>> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
|
||||
// 分页查询
|
||||
PageResult<AppBrokerageUserRankByPriceRespVO> pageResult = brokerageRecordService.getBrokerageUserChildSummaryPageByPrice(pageReqVO);
|
||||
@@ -123,7 +117,6 @@ public class AppBrokerageUserController {
|
||||
|
||||
@GetMapping("/child-summary-page")
|
||||
@Operation(summary = "获得下级分销统计分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageUserChildSummaryRespVO>> getBrokerageUserChildSummaryPage(
|
||||
AppBrokerageUserChildSummaryPageReqVO pageReqVO) {
|
||||
PageResult<AppBrokerageUserChildSummaryRespVO> pageResult = brokerageUserService.getBrokerageUserChildSummaryPage(pageReqVO, getLoginUserId());
|
||||
@@ -133,7 +126,6 @@ public class AppBrokerageUserController {
|
||||
@GetMapping("/get-rank-by-price")
|
||||
@Operation(summary = "获得分销用户排行(基于佣金)")
|
||||
@Parameter(name = "times", description = "时间段", required = true)
|
||||
@PreAuthenticated
|
||||
public CommonResult<Integer> getRankByPrice(
|
||||
@RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) {
|
||||
return success(brokerageRecordService.getUserRankByPrice(getLoginUserId(), times));
|
||||
|
||||
@@ -2,7 +2,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.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;
|
||||
@@ -33,7 +32,6 @@ public class AppBrokerageWithdrawController {
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得分销提现分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppBrokerageWithdrawRespVO>> getBrokerageWithdrawPage(AppBrokerageWithdrawPageReqVO pageReqVO) {
|
||||
PageResult<BrokerageWithdrawDO> pageResult = brokerageWithdrawService.getBrokerageWithdrawPage(
|
||||
BrokerageWithdrawConvert.INSTANCE.convert(pageReqVO, getLoginUserId()));
|
||||
@@ -42,7 +40,6 @@ public class AppBrokerageWithdrawController {
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建分销提现")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> createBrokerageWithdraw(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) {
|
||||
return success(brokerageWithdrawService.createBrokerageWithdraw(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.service.cart.CartService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -32,14 +31,12 @@ public class AppCartController {
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "添加购物车商品")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> addCart(@Valid @RequestBody AppCartAddReqVO addCountReqVO) {
|
||||
return success(cartService.addCart(getLoginUserId(), addCountReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update-count")
|
||||
@Operation(summary = "更新购物车商品数量")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> updateCartCount(@Valid @RequestBody AppCartUpdateCountReqVO updateReqVO) {
|
||||
cartService.updateCartCount(getLoginUserId(), updateReqVO);
|
||||
return success(true);
|
||||
@@ -47,7 +44,6 @@ public class AppCartController {
|
||||
|
||||
@PutMapping("/update-selected")
|
||||
@Operation(summary = "更新购物车商品选中")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> updateCartSelected(@Valid @RequestBody AppCartUpdateSelectedReqVO updateReqVO) {
|
||||
cartService.updateCartSelected(getLoginUserId(), updateReqVO);
|
||||
return success(true);
|
||||
@@ -55,7 +51,6 @@ public class AppCartController {
|
||||
|
||||
@PutMapping("/reset")
|
||||
@Operation(summary = "重置购物车商品")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> resetCart(@Valid @RequestBody AppCartResetReqVO updateReqVO) {
|
||||
cartService.resetCart(getLoginUserId(), updateReqVO);
|
||||
return success(true);
|
||||
@@ -64,7 +59,6 @@ public class AppCartController {
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除购物车商品")
|
||||
@Parameter(name = "ids", description = "购物车商品编号", required = true, example = "1024,2048")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> deleteCart(@RequestParam("ids") List<Long> ids) {
|
||||
cartService.deleteCart(getLoginUserId(), ids);
|
||||
return success(true);
|
||||
@@ -72,14 +66,12 @@ public class AppCartController {
|
||||
|
||||
@GetMapping("get-count")
|
||||
@Operation(summary = "查询用户在购物车中的商品数量")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Integer> getCartCount() {
|
||||
return success(cartService.getCartCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询用户的购物车列表")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppCartListRespVO> getCartList() {
|
||||
return success(cartService.getCartList(getLoginUserId()));
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -36,6 +37,7 @@ public class AppTradeConfigController {
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得交易配置")
|
||||
@PermitAll
|
||||
public CommonResult<AppTradeConfigRespVO> getTradeConfig() {
|
||||
TradeConfigDO config = ObjUtil.defaultIfNull(tradeConfigService.getTradeConfig(), new TradeConfigDO());
|
||||
return success(TradeConfigConvert.INSTANCE.convert02(config).setTencentLbsKey(tencentLbsKey));
|
||||
|
||||
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -30,6 +31,7 @@ public class AppDeliverExpressController {
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得快递公司列表")
|
||||
@PermitAll
|
||||
public CommonResult<List<AppDeliveryExpressRespVO>> getDeliveryExpressList() {
|
||||
List<DeliveryExpressDO> list = deliveryExpressService.getDeliveryExpressListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
list.sort(Comparator.comparing(DeliveryExpressDO::getSort));
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -36,6 +37,7 @@ public class AppDeliverPickUpStoreController {
|
||||
@Parameter(name = "latitude", description = "精度", example = "110"),
|
||||
@Parameter(name = "longitude", description = "纬度", example = "120")
|
||||
})
|
||||
@PermitAll
|
||||
public CommonResult<List<AppDeliveryPickUpStoreRespVO>> getDeliveryPickUpStoreList(
|
||||
@RequestParam(value = "latitude", required = false) Double latitude,
|
||||
@RequestParam(value = "longitude", required = false) Double longitude) {
|
||||
@@ -47,6 +49,7 @@ public class AppDeliverPickUpStoreController {
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得自提门店")
|
||||
@Parameter(name = "id", description = "门店编号")
|
||||
@PermitAll
|
||||
public CommonResult<AppDeliveryPickUpStoreRespVO> getOrder(@RequestParam("id") Long id) {
|
||||
DeliveryPickUpStoreDO store = deliveryPickUpStoreService.getDeliveryPickUpStore(id);
|
||||
return success(DeliveryPickUpStoreConvert.INSTANCE.convert03(store));
|
||||
|
||||
@@ -62,3 +62,8 @@ tenant-id: {{appTenentId}}
|
||||
GET {{appApi}}/trade/order/get-express-track-list?id=70
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### /trade-order/settlement-product 获得商品结算信息
|
||||
GET {{appApi}}/trade/order/settlement-product?spuIds=633
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.controller.app.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
|
||||
@@ -17,11 +16,14 @@ import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
|
||||
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 cn.iocoder.yudao.module.trade.service.price.TradePriceService;
|
||||
import com.google.common.collect.Maps;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -47,23 +49,30 @@ public class AppTradeOrderController {
|
||||
private TradeOrderQueryService tradeOrderQueryService;
|
||||
@Resource
|
||||
private DeliveryExpressService deliveryExpressService;
|
||||
|
||||
@Resource
|
||||
private AfterSaleService afterSaleService;
|
||||
@Resource
|
||||
private TradePriceService priceService;
|
||||
|
||||
@Resource
|
||||
private TradeOrderProperties tradeOrderProperties;
|
||||
|
||||
@GetMapping("/settlement")
|
||||
@Operation(summary = "获得订单结算信息")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(@Valid AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/settlement-product")
|
||||
@Operation(summary = "获得商品结算信息", description = "用于商品列表、商品详情,获得参与活动后的价格信息")
|
||||
@Parameter(name = "spuIds", description = "商品 SPU 编号数组")
|
||||
@PermitAll
|
||||
public CommonResult<List<AppTradeProductSettlementRespVO>> settlementProduct(@RequestParam("spuIds") List<Long> spuIds) {
|
||||
return success(priceService.calculateProductPrice(getLoginUserId(), spuIds));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建订单")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO) {
|
||||
TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), createReqVO);
|
||||
return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId()));
|
||||
@@ -79,28 +88,37 @@ public class AppTradeOrderController {
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得交易订单")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeOrderDetailRespVO> getOrder(@RequestParam("id") Long id) {
|
||||
// 查询订单
|
||||
@Parameters({
|
||||
@Parameter(name = "id", description = "交易订单编号"),
|
||||
@Parameter(name = "sync", description = "是否同步支付状态", example = "true")
|
||||
})
|
||||
public CommonResult<AppTradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long id,
|
||||
@RequestParam(value = "sync", required = false) Boolean sync) {
|
||||
// 1.1 查询订单
|
||||
TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id);
|
||||
if (order == null) {
|
||||
return success(null);
|
||||
}
|
||||
// 1.2 sync 仅在等待支付
|
||||
if (Boolean.TRUE.equals(sync)
|
||||
&& TradeOrderStatusEnum.isUnpaid(order.getStatus()) && !order.getPayStatus()) {
|
||||
tradeOrderUpdateService.syncOrderPayStatusQuietly(order.getId(), order.getPayOrderId());
|
||||
// 重新查询,因为同步后,可能会有变化
|
||||
order = tradeOrderQueryService.getOrder(id);
|
||||
}
|
||||
|
||||
// 查询订单项
|
||||
// 2.1 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId());
|
||||
// 查询物流公司
|
||||
// 2.2 查询物流公司
|
||||
DeliveryExpressDO express = order.getLogisticsId() != null && order.getLogisticsId() > 0 ?
|
||||
deliveryExpressService.getDeliveryExpress(order.getLogisticsId()) : null;
|
||||
// 最终组合
|
||||
// 2.3 最终组合
|
||||
return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express));
|
||||
}
|
||||
|
||||
@GetMapping("/get-express-track-list")
|
||||
@Operation(summary = "获得交易订单的物流轨迹")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
@PreAuthenticated
|
||||
public CommonResult<List<AppOrderExpressTrackRespDTO>> getOrderExpressTrackList(@RequestParam("id") Long id) {
|
||||
return success(TradeOrderConvert.INSTANCE.convertList02(
|
||||
tradeOrderQueryService.getExpressTrackList(id, getLoginUserId())));
|
||||
@@ -108,7 +126,6 @@ public class AppTradeOrderController {
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得交易订单分页")
|
||||
@PreAuthenticated
|
||||
public CommonResult<PageResult<AppTradeOrderPageItemRespVO>> getOrderPage(AppTradeOrderPageReqVO reqVO) {
|
||||
// 查询订单
|
||||
PageResult<TradeOrderDO> pageResult = tradeOrderQueryService.getOrderPage(getLoginUserId(), reqVO);
|
||||
@@ -121,7 +138,6 @@ public class AppTradeOrderController {
|
||||
|
||||
@GetMapping("/get-count")
|
||||
@Operation(summary = "获得交易订单数量")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Map<String, Long>> getOrderCount() {
|
||||
Map<String, Long> orderCount = Maps.newLinkedHashMapWithExpectedSize(5);
|
||||
// 全部
|
||||
@@ -146,7 +162,6 @@ public class AppTradeOrderController {
|
||||
@PutMapping("/receive")
|
||||
@Operation(summary = "确认交易订单收货")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> receiveOrder(@RequestParam("id") Long id) {
|
||||
tradeOrderUpdateService.receiveOrderByMember(getLoginUserId(), id);
|
||||
return success(true);
|
||||
@@ -155,7 +170,6 @@ public class AppTradeOrderController {
|
||||
@DeleteMapping("/cancel")
|
||||
@Operation(summary = "取消交易订单")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> cancelOrder(@RequestParam("id") Long id) {
|
||||
tradeOrderUpdateService.cancelOrderByMember(getLoginUserId(), id);
|
||||
return success(true);
|
||||
@@ -164,7 +178,6 @@ public class AppTradeOrderController {
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除交易订单")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> deleteOrder(@RequestParam("id") Long id) {
|
||||
tradeOrderUpdateService.deleteOrder(getLoginUserId(), id);
|
||||
return success(true);
|
||||
@@ -175,7 +188,6 @@ public class AppTradeOrderController {
|
||||
@GetMapping("/item/get")
|
||||
@Operation(summary = "获得交易订单项")
|
||||
@Parameter(name = "id", description = "交易订单项编号")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeOrderItemRespVO> getOrderItem(@RequestParam("id") Long id) {
|
||||
TradeOrderItemDO item = tradeOrderQueryService.getOrderItem(getLoginUserId(), id);
|
||||
return success(TradeOrderConvert.INSTANCE.convert03(item));
|
||||
@@ -183,7 +195,6 @@ public class AppTradeOrderController {
|
||||
|
||||
@PostMapping("/item/create-comment")
|
||||
@Operation(summary = "创建交易订单项的评价")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> createOrderItemComment(@RequestBody AppTradeOrderItemCommentCreateReqVO createReqVO) {
|
||||
return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单创建 Request VO")
|
||||
@Data
|
||||
|
||||
@@ -6,13 +6,13 @@ import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单结算 Request VO")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@@ -34,6 +35,13 @@ public class AppTradeOrderSettlementRespVO {
|
||||
@Schema(description = "总积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer totalPoint;
|
||||
|
||||
/**
|
||||
* 营销活动数组
|
||||
*
|
||||
* 只对应 {@link TradePriceCalculateRespBO.Price#items} 商品匹配的活动
|
||||
*/
|
||||
private List<TradePriceCalculateRespBO.Promotion> promotions;
|
||||
|
||||
@Schema(description = "购物项")
|
||||
@Data
|
||||
public static class Item {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "用户 App - 商品结算信息 Response VO")
|
||||
@Data
|
||||
public class AppTradeProductSettlementRespVO {
|
||||
|
||||
@Schema(description = "SPU 商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "SKU 价格信息数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private List<Sku> skus;
|
||||
|
||||
@Schema(description = "满减送活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private RewardActivity rewardActivity;
|
||||
|
||||
@Schema(description = "SKU 价格信息")
|
||||
@Data
|
||||
public static class Sku implements Serializable {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "优惠后价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer promotionPrice;
|
||||
|
||||
@Schema(description = "营销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "4")
|
||||
private Integer promotionType; // 对应 PromotionTypeEnum 枚举,目前只有 4 和 6 两种
|
||||
|
||||
@Schema(description = "营销编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long promotionId; // 目前只有限时折扣活动的编号
|
||||
|
||||
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime promotionEndTime;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "满减送活动信息")
|
||||
@Data
|
||||
public static class RewardActivity {
|
||||
|
||||
@Schema(description = "满减活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "条件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer conditionType;
|
||||
|
||||
@Schema(description = "优惠规则的数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<RewardActivityRule> rules;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "优惠规则")
|
||||
@Data
|
||||
public static class RewardActivityRule {
|
||||
|
||||
@Schema(description = "优惠门槛", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 1. 满 N 元,单位:分; 2. 满 N 件
|
||||
private Integer limit;
|
||||
|
||||
@Schema(description = "优惠价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer discountPrice;
|
||||
|
||||
@Schema(description = "是否包邮", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean freeDelivery;
|
||||
|
||||
@Schema(description = "赠送的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer point;
|
||||
|
||||
@Schema(description = "赠送的优惠劵编号的数组")
|
||||
private Map<Long, Integer> giveCouponTemplateCounts;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,8 +46,7 @@ public interface AfterSaleConvert {
|
||||
@Mapping(source = "afterSale.refundPrice", target = "price"),
|
||||
@Mapping(source = "orderProperties.payAppKey", target = "appKey")
|
||||
})
|
||||
PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale,
|
||||
TradeOrderProperties orderProperties);
|
||||
PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale, TradeOrderProperties orderProperties);
|
||||
|
||||
MemberUserRespVO convert(MemberUserRespDTO bean);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ 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 jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -44,7 +45,6 @@ import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -386,7 +386,7 @@ public class AfterSaleServiceImpl implements AfterSaleService {
|
||||
public void afterCommit() {
|
||||
// 创建退款单
|
||||
PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties)
|
||||
.setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));
|
||||
.setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));;
|
||||
Long payRefundId = payRefundApi.createRefund(createReqDTO).getCheckedData();
|
||||
// 更新售后单的退款单号
|
||||
tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
|
||||
|
||||
@@ -23,10 +23,10 @@ import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClien
|
||||
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
|
||||
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -215,7 +215,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
* @return 物流轨迹
|
||||
*/
|
||||
@Cacheable(cacheNames = RedisKeyConstants.EXPRESS_TRACK, key = "#code + '-' + #logisticsNo + '-' + #receiverMobile",
|
||||
condition = "#result != null && #result.length() > 0")
|
||||
unless = "#result == null")
|
||||
public List<ExpressTrackRespDTO> getExpressTrackList(String code, String logisticsNo, String receiverMobile) {
|
||||
return expressClientFactory.getDefaultExpressClient().getExpressTrackList(new ExpressTrackQueryReqDTO()
|
||||
.setExpressCode(code).setLogisticsNo(logisticsNo).setPhone(receiverMobile));
|
||||
|
||||
@@ -49,6 +49,17 @@ public interface TradeOrderUpdateService {
|
||||
*/
|
||||
void updateOrderPaid(Long id, Long payOrderId);
|
||||
|
||||
/**
|
||||
* 同步订单的支付状态
|
||||
*
|
||||
* 1. Quietly 表示,即使同步失败,也不会抛出异常
|
||||
* 2. 什么时候回出现异常?因为是主动同步,可能和支付模块的回调通知 {@link #updateOrderPaid(Long, Long)} 存在并发冲突,导致抛出异常
|
||||
*
|
||||
* @param id 订单编号
|
||||
* @param payOrderId 支付订单编号
|
||||
*/
|
||||
void syncOrderPayStatusQuietly(Long id, Long payOrderId);
|
||||
|
||||
/**
|
||||
* 【管理员】发货交易订单
|
||||
*
|
||||
|
||||
@@ -9,7 +9,6 @@ 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.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
@@ -166,7 +165,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList);
|
||||
calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程,保证都是选中的
|
||||
"商品({}) 未设置为选中", item.getSkuId()));
|
||||
return tradePriceService.calculatePrice(calculateReqBO);
|
||||
return tradePriceService.calculateOrderPrice(calculateReqBO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -269,12 +268,24 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
@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();
|
||||
// 1.1 校验订单是否存在
|
||||
TradeOrderDO order = validateOrderExists(id);
|
||||
// 1.2 校验订单已支付
|
||||
if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayStatus()) {
|
||||
// 特殊:如果订单已支付,且支付单号相同,直接返回,说明重复回调
|
||||
if (ObjectUtil.equals(order.getPayOrderId(), payOrderId)) {
|
||||
log.warn("[updateOrderPaid][order({}) 已支付,且支付单号相同({}),直接返回]", order, payOrderId);
|
||||
return;
|
||||
}
|
||||
log.error("[updateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
|
||||
id, payOrderId, JsonUtils.toJsonString(order));
|
||||
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
|
||||
}
|
||||
|
||||
// 2. 更新 TradeOrderDO 状态为已支付,等待发货
|
||||
// 2. 校验支付订单的合法性
|
||||
PayOrderRespDTO payOrder = validatePayOrderPaid(order, payOrderId);
|
||||
|
||||
// 3. 更新 TradeOrderDO 状态为已支付,等待发货
|
||||
int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
|
||||
new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true)
|
||||
.setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
|
||||
@@ -282,66 +293,65 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
|
||||
}
|
||||
|
||||
// 3. 执行 TradeOrderHandler 的后置处理
|
||||
// 4. 执行 TradeOrderHandler 的后置处理
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
|
||||
tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));
|
||||
|
||||
// 4. 记录订单日志
|
||||
// 5. 记录订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
|
||||
TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验交易订单满足被支付的条件
|
||||
* <p>
|
||||
* 1. 交易订单未支付
|
||||
* 2. 支付单已支付
|
||||
*
|
||||
* @param id 交易订单编号
|
||||
* @param payOrderId 支付订单编号
|
||||
* @return 交易订单
|
||||
*/
|
||||
private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
|
||||
// 校验订单是否存在
|
||||
TradeOrderDO order = validateOrderExists(id);
|
||||
// 校验订单未支付
|
||||
if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayStatus()) {
|
||||
log.error("[validateOrderPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]",
|
||||
id, JsonUtils.toJsonString(order));
|
||||
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
|
||||
}
|
||||
// 校验支付订单匹配
|
||||
if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
|
||||
log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
|
||||
id, payOrderId, JsonUtils.toJsonString(order));
|
||||
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
|
||||
}
|
||||
|
||||
// 校验支付单是否存在
|
||||
@Override
|
||||
public void syncOrderPayStatusQuietly(Long id, Long payOrderId) {
|
||||
PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId).getCheckedData();
|
||||
if (payOrder == null) {
|
||||
log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
|
||||
return;
|
||||
}
|
||||
if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getSelf().updateOrderPaid(id, payOrderId);
|
||||
} catch (Throwable e) {
|
||||
log.warn("[syncOrderPayStatusQuietly][id({}) payOrderId({}) 同步支付状态失败]", id, payOrderId, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验支付订单的合法性
|
||||
*
|
||||
* @param order 交易订单
|
||||
* @param payOrderId 支付订单编号
|
||||
* @return 支付订单
|
||||
*/
|
||||
private PayOrderRespDTO validatePayOrderPaid(TradeOrderDO order, Long payOrderId) {
|
||||
// 1. 校验支付单是否存在
|
||||
PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId).getCheckedData();
|
||||
if (payOrder == null) {
|
||||
log.error("[validatePayOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", order.getId(), payOrderId);
|
||||
throw exception(ORDER_NOT_FOUND);
|
||||
}
|
||||
// 校验支付单已支付
|
||||
|
||||
// 2.1 校验支付单已支付
|
||||
if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
|
||||
log.error("[validateOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
|
||||
id, payOrderId, JsonUtils.toJsonString(payOrder));
|
||||
log.error("[validatePayOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
|
||||
order.getId(), payOrderId, JsonUtils.toJsonString(payOrder));
|
||||
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
|
||||
}
|
||||
// 校验支付金额一致
|
||||
// 2.2 校验支付金额一致
|
||||
if (ObjectUtil.notEqual(payOrder.getPrice(), order.getPayPrice())) {
|
||||
log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
|
||||
id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
|
||||
log.error("[validatePayOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
|
||||
order.getId(), payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
|
||||
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
|
||||
}
|
||||
// 校验支付订单匹配(二次)
|
||||
if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
|
||||
log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
|
||||
id, payOrderId, JsonUtils.toJsonString(payOrder));
|
||||
// 2.2 校验支付订单匹配(二次)
|
||||
if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), order.getId().toString())) {
|
||||
log.error("[validatePayOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
|
||||
order.getId(), payOrderId, JsonUtils.toJsonString(payOrder));
|
||||
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
|
||||
}
|
||||
return new KeyValue<>(order, payOrder);
|
||||
return payOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -375,7 +385,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
// 3. 记录订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus(),
|
||||
MapUtil.<String, Object>builder().put("expressName", express != null ? express.getName() : "")
|
||||
MapUtil.<String, Object>builder().put("deliveryName", express != null ? express.getName() : "")
|
||||
.put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build());
|
||||
|
||||
// 4.1 发送站内信
|
||||
@@ -676,7 +686,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
tradeOrderItemMapper.updateBatch(updateItems);
|
||||
|
||||
// 4. 更新支付订单
|
||||
payOrderApi.updatePayOrderPrice(order.getPayOrderId(), newPayPrice).getCheckedData();
|
||||
payOrderApi.updatePayOrderPrice(order.getPayOrderId(), newPayPrice).checkError();
|
||||
|
||||
// 5. 记录订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus(),
|
||||
@@ -887,7 +897,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
.setAppKey(tradeOrderProperties.getPayAppKey()).setUserIp(getClientIP()) // 支付应用
|
||||
.setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
|
||||
.setMerchantRefundId(String.valueOf(order.getId()))
|
||||
.setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())).getCheckedData(); // 价格信息
|
||||
.setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())).checkError(); // 价格信息
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -51,7 +51,7 @@ public class TradeCombinationOrderHandler implements TradeOrderHandler {
|
||||
// 1. 校验是否满足拼团活动相关限制
|
||||
TradeOrderItemDO item = orderItems.get(0);
|
||||
combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(),
|
||||
order.getCombinationHeadId(), item.getSkuId(), item.getCount());
|
||||
order.getCombinationHeadId(), item.getSkuId(), item.getCount()).checkError();
|
||||
|
||||
// 2. 校验该用户是否存在未支付的拼团活动订单,避免一个拼团可以下多个单子了
|
||||
TradeOrderDO activityOrder = orderQueryService.getOrderByUserIdAndStatusAndCombination(
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeProductSettlementRespVO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 价格计算 Service 接口
|
||||
*
|
||||
@@ -13,11 +15,20 @@ import jakarta.validation.Valid;
|
||||
public interface TradePriceService {
|
||||
|
||||
/**
|
||||
* 价格计算
|
||||
* 【订单】价格计算
|
||||
*
|
||||
* @param calculateReqDTO 计算信息
|
||||
* @return 计算结果
|
||||
*/
|
||||
TradePriceCalculateRespBO calculatePrice(@Valid TradePriceCalculateReqBO calculateReqDTO);
|
||||
TradePriceCalculateRespBO calculateOrderPrice(@Valid TradePriceCalculateReqBO calculateReqDTO);
|
||||
|
||||
/**
|
||||
* 【商品】价格计算,用于商品列表、商品详情
|
||||
*
|
||||
* @param userId 用户编号,允许为空
|
||||
* @param spuIds 商品 SPU 编号数组
|
||||
* @return 计算结果
|
||||
*/
|
||||
List<AppTradeProductSettlementRespVO> calculateProductPrice(Long userId, List<Long> spuIds);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeProductSettlementRespVO;
|
||||
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.TradeDiscountActivityPriceCalculator;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
|
||||
@@ -37,12 +46,19 @@ public class TradePriceServiceImpl implements TradePriceService {
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
@Resource
|
||||
private DiscountActivityApi discountActivityApi;
|
||||
@Resource
|
||||
private RewardActivityApi rewardActivityApi;
|
||||
|
||||
@Resource
|
||||
private List<TradePriceCalculator> priceCalculators;
|
||||
|
||||
@Resource
|
||||
private TradeDiscountActivityPriceCalculator discountActivityPriceCalculator;
|
||||
|
||||
@Override
|
||||
public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) {
|
||||
public TradePriceCalculateRespBO calculateOrderPrice(TradePriceCalculateReqBO calculateReqBO) {
|
||||
// 1.1 获得商品 SKU 数组
|
||||
List<ProductSkuRespDTO> skuList = checkSkuList(calculateReqBO);
|
||||
// 1.2 获得商品 SPU 数组
|
||||
@@ -81,8 +97,58 @@ public class TradePriceServiceImpl implements TradePriceService {
|
||||
}
|
||||
|
||||
private List<ProductSpuRespDTO> checkSpuList(List<ProductSkuRespDTO> skuList) {
|
||||
// 获得商品 SPU 数组
|
||||
return productSpuApi.validateSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId)).getCheckedData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AppTradeProductSettlementRespVO> calculateProductPrice(Long userId, List<Long> spuIds) {
|
||||
// 1.1 获得 SPU 与 SKU 的映射
|
||||
List<ProductSkuRespDTO> allSkuList = productSkuApi.getSkuListBySpuId(spuIds).getCheckedData();
|
||||
Map<Long, List<ProductSkuRespDTO>> spuIdAndSkuListMap = convertMultiMap(allSkuList, ProductSkuRespDTO::getSpuId);
|
||||
// 1.2 获得会员等级
|
||||
MemberLevelRespDTO level = discountActivityPriceCalculator.getMemberLevel(userId);
|
||||
// 1.3 获得限时折扣活动
|
||||
Map<Long, DiscountProductRespDTO> skuIdAndDiscountMap = convertMap(
|
||||
discountActivityApi.getMatchDiscountProductListBySkuIds(convertSet(allSkuList, ProductSkuRespDTO::getId)).getCheckedData(),
|
||||
DiscountProductRespDTO::getSkuId);
|
||||
// 1.4 获得满减送活动
|
||||
List<RewardActivityMatchRespDTO> rewardActivityMap = rewardActivityApi.getMatchRewardActivityListBySpuIds(spuIds).getCheckedData();
|
||||
|
||||
// 2. 价格计算
|
||||
return convertList(spuIds, spuId -> {
|
||||
AppTradeProductSettlementRespVO spuVO = new AppTradeProductSettlementRespVO().setSpuId(spuId);
|
||||
// 2.1 优惠价格
|
||||
List<ProductSkuRespDTO> skuList = spuIdAndSkuListMap.get(spuId);
|
||||
List<AppTradeProductSettlementRespVO.Sku> skuVOList = convertList(skuList, sku -> {
|
||||
AppTradeProductSettlementRespVO.Sku skuVO = new AppTradeProductSettlementRespVO.Sku()
|
||||
.setId(sku.getId()).setPromotionPrice(sku.getPrice());
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem()
|
||||
.setPayPrice(sku.getPrice()).setCount(1);
|
||||
// 计算限时折扣的优惠价格
|
||||
DiscountProductRespDTO discountProduct = skuIdAndDiscountMap.get(sku.getId());
|
||||
Integer discountPrice = discountActivityPriceCalculator.calculateActivityPrice(discountProduct, orderItem);
|
||||
// 计算 VIP 优惠金额
|
||||
Integer vipPrice = discountActivityPriceCalculator.calculateVipPrice(level, orderItem);
|
||||
if (discountPrice <= 0 && vipPrice <= 0) {
|
||||
return skuVO;
|
||||
}
|
||||
// 选择一个大的优惠
|
||||
if (discountPrice > vipPrice) {
|
||||
return skuVO.setPromotionPrice(sku.getPrice() - discountPrice)
|
||||
.setPromotionType(PromotionTypeEnum.DISCOUNT_ACTIVITY.getType())
|
||||
.setPromotionId(discountProduct.getId()).setPromotionEndTime(discountProduct.getActivityEndTime());
|
||||
} else {
|
||||
return skuVO.setPromotionPrice(sku.getPrice() - vipPrice)
|
||||
.setPromotionType(PromotionTypeEnum.MEMBER_LEVEL.getType());
|
||||
}
|
||||
});
|
||||
spuVO.setSkus(skuVOList);
|
||||
// 2.2 满减送活动
|
||||
RewardActivityMatchRespDTO rewardActivity = CollUtil.findOne(rewardActivityMap,
|
||||
activity -> CollUtil.contains(activity.getSpuIds(), spuId));
|
||||
spuVO.setRewardActivity(BeanUtils.toBean(rewardActivity, AppTradeProductSettlementRespVO.RewardActivity.class));
|
||||
return spuVO;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.bo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
||||
@@ -122,9 +122,9 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
||||
*/
|
||||
private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) {
|
||||
TradeConfigDO config = tradeConfigService.getTradeConfig();
|
||||
return config != null
|
||||
&& Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮
|
||||
&& result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice(); // 满足包邮的价格
|
||||
return config == null
|
||||
|| Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮
|
||||
|| result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice(); // 满足包邮的价格
|
||||
}
|
||||
|
||||
private void calculateDeliveryPrice(List<OrderItem> selectedSkus,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
|
||||
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
|
||||
@@ -10,20 +13,23 @@ import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.calculateRatePrice;
|
||||
import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
|
||||
|
||||
/**
|
||||
* 限时折扣的 {@link TradePriceCalculator} 实现类
|
||||
*
|
||||
* 由于“会员折扣”和“限时折扣”是冲突,需要选择优惠金额多的,所以也放在这里计算
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
@@ -32,6 +38,10 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
|
||||
|
||||
@Resource
|
||||
private DiscountActivityApi discountActivityApi;
|
||||
@Resource
|
||||
private MemberLevelApi memberLevelApi;
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Override
|
||||
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
@@ -39,51 +49,103 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
|
||||
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
|
||||
return;
|
||||
}
|
||||
// 获得 SKU 对应的限时折扣活动
|
||||
List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductList(
|
||||
convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)).getCheckedData();
|
||||
if (CollUtil.isEmpty(discountProducts)) {
|
||||
return;
|
||||
}
|
||||
Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId);
|
||||
|
||||
// 处理每个 SKU 的限时折扣
|
||||
// 1.1 获得 SKU 对应的限时折扣活动
|
||||
List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductListBySkuIds(
|
||||
convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)).getCheckedData();
|
||||
Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId);
|
||||
// 1.2 获得会员等级
|
||||
MemberLevelRespDTO level = getMemberLevel(param.getUserId());
|
||||
|
||||
// 2. 计算每个 SKU 的优惠金额
|
||||
result.getItems().forEach(orderItem -> {
|
||||
// 1. 获取该 SKU 的优惠信息
|
||||
DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId());
|
||||
if (discountProduct == null) {
|
||||
if (!orderItem.getSelected()) {
|
||||
return;
|
||||
}
|
||||
// 2.1 计算限时折扣的优惠金额
|
||||
DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId());
|
||||
Integer discountPrice = calculateActivityPrice(discountProduct, orderItem);
|
||||
// 2.2 计算 VIP 优惠金额
|
||||
Integer vipPrice = calculateVipPrice(level, orderItem);
|
||||
if (discountPrice <= 0 && vipPrice <= 0) {
|
||||
return;
|
||||
}
|
||||
// 2. 计算优惠金额
|
||||
Integer newPayPrice = calculatePayPrice(discountProduct, orderItem);
|
||||
Integer newDiscountPrice = orderItem.getPayPrice() - newPayPrice;
|
||||
|
||||
// 3.1 记录优惠明细
|
||||
if (orderItem.getSelected()) {
|
||||
// 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
|
||||
// 3. 选择优惠金额多的
|
||||
if (discountPrice > vipPrice) {
|
||||
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
||||
discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
|
||||
StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
|
||||
newDiscountPrice);
|
||||
StrUtil.format("限时折扣:省 {} 元", formatPrice(discountPrice)),
|
||||
discountPrice);
|
||||
// 更新 SKU 优惠金额
|
||||
orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice);
|
||||
} else {
|
||||
assert level != null;
|
||||
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
||||
level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
|
||||
String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
|
||||
vipPrice);
|
||||
// 更新 SKU 的优惠金额
|
||||
orderItem.setVipPrice(vipPrice);
|
||||
}
|
||||
// 3.2 更新 SKU 优惠金额
|
||||
orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
|
||||
|
||||
// 4. 分摊优惠
|
||||
TradePriceCalculatorHelper.recountPayPrice(orderItem);
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
});
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
}
|
||||
|
||||
private Integer calculatePayPrice(DiscountProductRespDTO discountProduct,
|
||||
TradePriceCalculateRespBO.OrderItem orderItem) {
|
||||
Integer price = orderItem.getPayPrice();
|
||||
if (PromotionDiscountTypeEnum.PRICE.getType().equals(discountProduct.getDiscountType())) { // 减价
|
||||
price -= discountProduct.getDiscountPrice() * orderItem.getCount();
|
||||
} else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discountProduct.getDiscountType())) { // 打折
|
||||
price = price * discountProduct.getDiscountPercent() / 100;
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("优惠活动的商品(%s) 的优惠类型不正确", discountProduct));
|
||||
/**
|
||||
* 获得用户的等级
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 用户等级
|
||||
*/
|
||||
public MemberLevelRespDTO getMemberLevel(Long userId) {
|
||||
MemberUserRespDTO user = memberUserApi.getUser(userId).getCheckedData();
|
||||
if (user == null || user.getLevelId() == null || user.getLevelId() <= 0) {
|
||||
return null;
|
||||
}
|
||||
return price;
|
||||
return memberLevelApi.getMemberLevel(user.getLevelId()).getCheckedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算优惠活动的价格
|
||||
*
|
||||
* @param discount 优惠活动
|
||||
* @param orderItem 交易项
|
||||
* @return 优惠价格
|
||||
*/
|
||||
public Integer calculateActivityPrice(DiscountProductRespDTO discount,
|
||||
TradePriceCalculateRespBO.OrderItem orderItem) {
|
||||
if (discount == null) {
|
||||
return 0;
|
||||
}
|
||||
Integer newPrice = orderItem.getPayPrice();
|
||||
if (PromotionDiscountTypeEnum.PRICE.getType().equals(discount.getDiscountType())) { // 减价
|
||||
newPrice -= discount.getDiscountPrice() * orderItem.getCount();
|
||||
} else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discount.getDiscountType())) { // 打折
|
||||
newPrice = calculateRatePrice(orderItem.getPayPrice(), discount.getDiscountPercent() / 100.0);
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("优惠活动的商品(%s) 的优惠类型不正确", discount));
|
||||
}
|
||||
return orderItem.getPayPrice() - newPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算会员 VIP 的优惠价格
|
||||
*
|
||||
* @param level 会员等级
|
||||
* @param orderItem 交易项
|
||||
* @return 优惠价格
|
||||
*/
|
||||
public Integer calculateVipPrice(MemberLevelRespDTO level,
|
||||
TradePriceCalculateRespBO.OrderItem orderItem) {
|
||||
if (level == null || level.getDiscountPercent() == null) {
|
||||
return 0;
|
||||
}
|
||||
Integer newPrice = calculateRatePrice(orderItem.getPayPrice(), level.getDiscountPercent().doubleValue());
|
||||
return orderItem.getPayPrice() - newPrice;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
|
||||
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
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;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
|
||||
|
||||
/**
|
||||
* 会员 VIP 折扣的 {@link TradePriceCalculator} 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
@Order(TradePriceCalculator.ORDER_MEMBER_LEVEL)
|
||||
public class TradeMemberLevelPriceCalculator implements TradePriceCalculator {
|
||||
|
||||
@Resource
|
||||
private MemberLevelApi memberLevelApi;
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Override
|
||||
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
// 0. 只有【普通】订单,才计算该优惠
|
||||
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
|
||||
return;
|
||||
}
|
||||
// 1. 获得用户的会员等级
|
||||
MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()).getCheckedData();
|
||||
if (user.getLevelId() == null || user.getLevelId() <= 0) {
|
||||
return;
|
||||
}
|
||||
MemberLevelRespDTO level = memberLevelApi.getMemberLevel(user.getLevelId()).getCheckedData();
|
||||
if (level == null || level.getDiscountPercent() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 计算每个 SKU 的优惠金额
|
||||
result.getItems().forEach(orderItem -> {
|
||||
// 2.1 计算优惠金额
|
||||
Integer vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());
|
||||
if (vipPrice <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.2 记录优惠明细
|
||||
if (orderItem.getSelected()) {
|
||||
// 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
|
||||
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
||||
level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
|
||||
String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
|
||||
vipPrice);
|
||||
}
|
||||
|
||||
// 2.3 更新 SKU 的优惠金额
|
||||
orderItem.setVipPrice(vipPrice);
|
||||
TradePriceCalculatorHelper.recountPayPrice(orderItem);
|
||||
});
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算会员 VIP 优惠价格
|
||||
*
|
||||
* @param price 原价
|
||||
* @param discountPercent 折扣
|
||||
* @return 优惠价格
|
||||
*/
|
||||
public Integer calculateVipPrice(Integer price, Integer discountPercent) {
|
||||
if (discountPercent == null) {
|
||||
return 0;
|
||||
}
|
||||
Integer newPrice = price * discountPercent / 100;
|
||||
return price - newPrice;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,8 +13,6 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
*/
|
||||
public interface TradePriceCalculator {
|
||||
|
||||
int ORDER_MEMBER_LEVEL = 5;
|
||||
|
||||
int ORDER_SECKILL_ACTIVITY = 8;
|
||||
int ORDER_BARGAIN_ACTIVITY = 8;
|
||||
int ORDER_COMBINATION_ACTIVITY = 8;
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
@@ -17,8 +14,6 @@ import jakarta.annotation.Resource;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -47,14 +42,15 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
|
||||
return;
|
||||
}
|
||||
// 获得 SKU 对应的满减送活动
|
||||
List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getMatchRewardActivityList(
|
||||
List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getMatchRewardActivityListBySpuIds(
|
||||
convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSpuId)).getCheckedData();
|
||||
if (CollUtil.isEmpty(rewardActivities)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理每个满减送活动
|
||||
rewardActivities.forEach(rewardActivity -> calculate(param, result, rewardActivity));
|
||||
// 处理最新的满减送活动
|
||||
if (!rewardActivities.isEmpty()) {
|
||||
calculate(param, result, rewardActivities.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result,
|
||||
@@ -69,7 +65,7 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
|
||||
if (rule == null) {
|
||||
TradePriceCalculatorHelper.addNotMatchPromotion(result, orderItems,
|
||||
rewardActivity.getId(), rewardActivity.getName(), PromotionTypeEnum.REWARD_ACTIVITY.getType(),
|
||||
getRewardActivityNotMeetTip(rewardActivity, orderItems));
|
||||
"满减送:" + rewardActivity.getRules().get(0).getDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,6 +73,10 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
|
||||
Integer newDiscountPrice = rule.getDiscountPrice();
|
||||
// 2.2 计算分摊的优惠金额
|
||||
List<Integer> divideDiscountPrices = TradePriceCalculatorHelper.dividePrice(orderItems, newDiscountPrice);
|
||||
// 2.3 计算是否包邮
|
||||
if (Boolean.TRUE.equals(rule.getFreeDelivery())) {
|
||||
result.setFreeDelivery(true);
|
||||
}
|
||||
|
||||
// 3.1 记录使用的优惠劵
|
||||
result.setCouponId(param.getCouponId());
|
||||
@@ -110,16 +110,8 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
|
||||
// 4.3 记录赠送的优惠券
|
||||
if (CollUtil.isNotEmpty(rule.getGiveCouponTemplateCounts())) {
|
||||
for (Map.Entry<Long, Integer> entry : rule.getGiveCouponTemplateCounts().entrySet()) {
|
||||
Map<Long, Integer> giveCouponTemplateCounts = result.getGiveCouponTemplateCounts();
|
||||
// TODO @puhui999:是不是有一种可能性,这个 key 没有,别的 key 有哈。
|
||||
// TODO 这里还有一种简化的写法。就是下面,大概两行就可以啦
|
||||
// result.getGiveCouponTemplateCounts().put(entry.getKey(),
|
||||
// result.getGiveCouponTemplateCounts().getOrDefault(entry.getKey(), 0) + entry.getValue());
|
||||
if (giveCouponTemplateCounts.get(entry.getKey()) == null) { // 情况一:还没有赠送的优惠券
|
||||
result.setGiveCouponTemplateCounts(rule.getGiveCouponTemplateCounts());
|
||||
} else { // 情况二:别的满减活动送过同类优惠券,则直接增加数量
|
||||
giveCouponTemplateCounts.put(entry.getKey(), giveCouponTemplateCounts.get(entry.getKey()) + entry.getValue());
|
||||
}
|
||||
result.getGiveCouponTemplateCounts().put(entry.getKey(),
|
||||
result.getGiveCouponTemplateCounts().getOrDefault(entry.getKey(), 0) + entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,28 +125,14 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
|
||||
*/
|
||||
private List<TradePriceCalculateRespBO.OrderItem> filterMatchActivityOrderItems(TradePriceCalculateRespBO result,
|
||||
RewardActivityMatchRespDTO rewardActivity) {
|
||||
// 情况一:全部商品都可以参与
|
||||
if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
|
||||
return result.getItems();
|
||||
}
|
||||
// 情况二:指定商品参与
|
||||
if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
|
||||
return filterList(result.getItems(),
|
||||
orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getSpuId()));
|
||||
}
|
||||
// 情况三:指定商品类型参与
|
||||
if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
|
||||
return filterList(result.getItems(),
|
||||
orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getCategoryId()));
|
||||
}
|
||||
return ListUtil.of();
|
||||
return filterList(result.getItems(), orderItem -> CollUtil.contains(rewardActivity.getSpuIds(), orderItem.getSpuId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得最大匹配的满减送活动的规则
|
||||
*
|
||||
* @param rewardActivity 满减送活动
|
||||
* @param orderItems 商品项
|
||||
* @param orderItems 商品项
|
||||
* @return 匹配的活动规则
|
||||
*/
|
||||
private RewardActivityMatchRespDTO.Rule getMaxMatchRewardActivityRule(RewardActivityMatchRespDTO rewardActivity,
|
||||
@@ -179,31 +157,4 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得满减送活动不匹配时的提示
|
||||
*
|
||||
* @param rewardActivity 满减送活动
|
||||
* @return 提示
|
||||
*/
|
||||
private String getRewardActivityNotMeetTip(RewardActivityMatchRespDTO rewardActivity,
|
||||
List<TradePriceCalculateRespBO.OrderItem> orderItems) {
|
||||
// 1. 计算数量和价格
|
||||
Integer count = TradePriceCalculatorHelper.calculateTotalCount(orderItems);
|
||||
Integer price = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
|
||||
assert count != null && price != null;
|
||||
|
||||
// 2. 构建不满足时的提示信息:按最低档规则算
|
||||
String meetTip = "满减送:购满 {} {},可以减 {} 元";
|
||||
List<RewardActivityMatchRespDTO.Rule> rules = new ArrayList<>(rewardActivity.getRules());
|
||||
rules.sort(Comparator.comparing(RewardActivityMatchRespDTO.Rule::getLimit)); // 按优惠门槛升序
|
||||
RewardActivityMatchRespDTO.Rule rule = rules.get(0);
|
||||
if (PromotionConditionTypeEnum.PRICE.getType().equals(rewardActivity.getConditionType())) {
|
||||
return StrUtil.format(meetTip, rule.getLimit(), "元", MoneyUtils.fenToYuanStr(rule.getDiscountPrice()));
|
||||
}
|
||||
if (PromotionConditionTypeEnum.COUNT.getType().equals(rewardActivity.getConditionType())) {
|
||||
return StrUtil.format(meetTip, rule.getLimit(), "件", MoneyUtils.fenToYuanStr(rule.getDiscountPrice()));
|
||||
}
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user