【同步】BOOT 和 CLOUD 的功能

This commit is contained in:
YunaiV
2025-05-02 20:25:24 +08:00
parent 04bd6bff04
commit 103685269e
57 changed files with 1873 additions and 140 deletions

View File

@@ -1,12 +1,13 @@
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.common.util.object.BeanUtils;
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.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -31,16 +32,17 @@ public class AppAfterSaleController {
@GetMapping(value = "/page")
@Operation(summary = "获得售后分页")
public CommonResult<PageResult<AppAfterSaleRespVO>> getAfterSalePage(PageParam pageParam) {
return success(AfterSaleConvert.INSTANCE.convertPage02(
afterSaleService.getAfterSalePage(getLoginUserId(), pageParam)));
public CommonResult<PageResult<AppAfterSaleRespVO>> getAfterSalePage(AppAfterSalePageReqVO pageReqVO) {
PageResult<AfterSaleDO> pageResult = afterSaleService.getAfterSalePage(getLoginUserId(), pageReqVO);
return success(BeanUtils.toBean(pageResult, AppAfterSaleRespVO.class));
}
@GetMapping(value = "/get")
@Operation(summary = "获得售后订单")
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
public CommonResult<AppAfterSaleRespVO> getAfterSale(@RequestParam("id") Long id) {
return success(AfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id)));
AfterSaleDO afterSale = afterSaleService.getAfterSale(getLoginUserId(), id);
return success(BeanUtils.toBean(afterSale, AppAfterSaleRespVO.class));
}
@PostMapping(value = "/create")

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Set;
@Schema(description = "用户 App - 交易售后分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppAfterSalePageReqVO extends PageParam {
@Schema(description = "售后状态", example = "10, 20")
private Set<Integer> statuses;
}

View File

@@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.base.spu;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 商品 SPU 基础 Response VO
*
@@ -25,4 +23,10 @@ public class AppProductSpuBaseRespVO {
@Schema(description = "商品分类编号", example = "1")
private Long categoryId;
@Schema(description = "商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
private Integer stock;
@Schema(description = "商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
}

View File

@@ -1,8 +1,12 @@
package cn.iocoder.yudao.module.trade.controller.app.delivery.vo.pickup;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalTime;
@Schema(description = "用户 App - 自提门店 Response VO")
@Data
public class AppDeliveryPickUpStoreRespVO {
@@ -28,6 +32,16 @@ public class AppDeliveryPickUpStoreRespVO {
@Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号")
private String detailAddress;
@Schema(description = "营业开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "营业开始时间不能为空")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
private LocalTime openingTime;
@Schema(description = "营业结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "营业结束时间不能为空")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm")
private LocalTime closingTime;
@Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.88")
private Double latitude;

View File

@@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUse
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -63,10 +62,6 @@ public interface AfterSaleConvert {
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
AppAfterSaleRespVO convert(AfterSaleDO bean);
PageResult<AppAfterSaleRespVO> convertPage02(PageResult<AfterSaleDO> page);
default AfterSaleDetailRespVO convert(AfterSaleDO afterSale, TradeOrderDO order, TradeOrderItemDO orderItem,
MemberUserRespDTO user, List<AfterSaleLogDO> logs) {
AfterSaleDetailRespVO respVO = convert02(afterSale);

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.convert.cart;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
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.product.enums.spu.ProductSpuStatusEnum;
@@ -33,21 +34,18 @@ public interface TradeCartConvert {
cartVO.setId(cart.getId()).setCount(cart.getCount()).setSelected(cart.getSelected());
ProductSpuRespDTO spu = spuMap.get(cart.getSpuId());
ProductSkuRespDTO sku = skuMap.get(cart.getSkuId());
cartVO.setSpu(convert(spu)).setSku(convert(sku));
cartVO.setSpu(BeanUtils.toBean(spu, AppProductSpuBaseRespVO.class))
.setSku(BeanUtils.toBean(sku, AppProductSkuBaseRespVO.class));
// 如果 SPU 不存在,或者下架,或者库存不足,说明是无效的
if (spu == null
|| !ProductSpuStatusEnum.isEnable(spu.getStatus())
|| spu.getStock() <= 0) {
cartVO.setSelected(false); // 强制设置成不可选中
invalidList.add(cartVO);
} else {
// 虽然 SKU 可能也会不存在,但是可以通过购物车重新选择
validList.add(cartVO);
}
});
return new AppCartListRespVO().setValidList(validList).setInvalidList(invalidList);
}
AppProductSpuBaseRespVO convert(ProductSpuRespDTO spu);
AppProductSkuBaseRespVO convert(ProductSkuRespDTO sku);
}

View File

@@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
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.aftersale.vo.AfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
@@ -27,9 +27,10 @@ public interface AfterSaleMapper extends BaseMapperX<AfterSaleDO> {
.orderByDesc(AfterSaleDO::getId));
}
default PageResult<AfterSaleDO> selectPage(Long userId, PageParam pageParam) {
return selectPage(pageParam, new LambdaQueryWrapperX<AfterSaleDO>()
.eqIfPresent(AfterSaleDO::getUserId, userId)
default PageResult<AfterSaleDO> selectPage(Long userId, AppAfterSalePageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<AfterSaleDO>()
.eq(AfterSaleDO::getUserId, userId)
.inIfPresent(AfterSaleDO::getStatus, pageReqVO.getStatuses())
.orderByDesc(AfterSaleDO::getId));
}

View File

@@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.trade.service.aftersale;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
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.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
/**
@@ -28,10 +28,10 @@ public interface AfterSaleService {
* 【会员】获得售后订单分页
*
* @param userId 用户编号
* @param pageParam 分页参数
* @param pageReqVO 分页参数
* @return 售后订单分页
*/
PageResult<AfterSaleDO> getAfterSalePage(Long userId, PageParam pageParam);
PageResult<AfterSaleDO> getAfterSalePage(Long userId, AppAfterSalePageReqVO pageReqVO);
/**
* 【会员】获得售后单

View File

@@ -3,7 +3,6 @@ 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.StrUtil;
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;
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
@@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePage
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
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.AppAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
@@ -87,8 +87,8 @@ public class AfterSaleServiceImpl implements AfterSaleService {
}
@Override
public PageResult<AfterSaleDO> getAfterSalePage(Long userId, PageParam pageParam) {
return tradeAfterSaleMapper.selectPage(userId, pageParam);
public PageResult<AfterSaleDO> getAfterSalePage(Long userId, AppAfterSalePageReqVO pageReqVO) {
return tradeAfterSaleMapper.selectPage(userId, pageReqVO);
}
@Override
@@ -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));

View File

@@ -545,6 +545,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
}
// 1.3 校验是否支持延迟(不允许取消)
if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) {
PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId()).getCheckedData();
if (payOrder != null && PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
log.warn("[cancelOrderByMember][order({}) 支付单已支付(支付回调延迟),不支持取消]", order.getId());
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
}
}
// 2. 取消订单
cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
@@ -581,6 +589,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Transactional(rollbackFor = Exception.class)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
public void cancelOrderBySystem(TradeOrderDO order) {
// 校验是否支持延迟(不允许取消)
if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) {
PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId()).getCheckedData();
if (payOrder != null && PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
log.warn("[cancelOrderBySystem][order({}) 支付单已支付(支付回调延迟),不支持取消]", order.getId());
return;
}
}
cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
}
@@ -895,12 +912,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 1.3 校验订单是否支付
if (!order.getPayStatus()) {
throw exception(ORDER_CANCEL_PAID_FAIL, "已支付");
}
// 1.3 校验订单是否未退款
// 1.4 校验订单是否未退款
if (ObjUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
throw exception(ORDER_CANCEL_PAID_FAIL, "未退款");
}

View File

@@ -20,6 +20,7 @@ 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.convertList;
@@ -101,13 +102,17 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler {
protected void addBrokerage(Long userId, List<TradeOrderItemDO> orderItems) {
MemberUserRespDTO user = memberUserApi.getUser(userId).getCheckedData();
Assert.notNull(user);
ProductSpuRespDTO spu = productSpuApi.getSpu(orderItems.get(0).getSpuId()).getCheckedData();
Assert.notNull(spu);
ProductSkuRespDTO sku = productSkuApi.getSku(orderItems.get(0).getSkuId()).getCheckedData();
Map<Long, ProductSpuRespDTO> spusMap = productSpuApi.getSpuMap(convertList(orderItems, TradeOrderItemDO::getSpuId));
Map<Long, ProductSkuRespDTO> skusMap = productSkuApi.getSkuMap(convertList(orderItems, TradeOrderItemDO::getSkuId));
// 每一个订单项,都会去生成分销记录
List<BrokerageAddReqBO> addList = convertList(orderItems,
item -> TradeOrderConvert.INSTANCE.convert(user, item, spu, sku));
List<BrokerageAddReqBO> addList = convertList(orderItems, item -> {
ProductSpuRespDTO spu = spusMap.get(item.getSpuId());
Assert.notNull(spu);
ProductSkuRespDTO sku = skusMap.get(item.getSkuId());
Assert.notNull(sku);
return TradeOrderConvert.INSTANCE.convert(user, item, spu, sku);
});
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
}