peng
2025-09-16 2040c0a7fa055174f252aa3880a2506af70850cf
Merge branch 'dev' into user_action
25个文件已修改
18个文件已添加
1557 ■■■■■ 已修改文件
buyer-api/src/main/java/cn/lili/controller/lmk/CouponCardController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
buyer-api/src/main/java/cn/lili/controller/lmk/CouponVirtualController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
buyer-api/src/main/java/cn/lili/controller/order/AfterSaleBuyerController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
buyer-api/src/main/java/cn/lili/controller/order/OrderBuyerController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/goods/entity/dos/Goods.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsSku.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsOperationDTO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/goods/sku/GoodsSkuBuilder.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/entity/CouponVirtual.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/form/CouponVirtualForm.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/query/CouponVirtualQuery.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/vo/CouponVirtualVO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/vo/CouponVirtualVOInfo.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/enums/general/VirtualGoodsTypeEnum.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/mapper/CouponVirtualMapper.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/CouponVirtualService.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/impl/CouponVirtualServiceImpl.java 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/cart/entity/dto/SkuMapDTO.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/cart/render/impl/CouponRender.java 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/cart/render/impl/FullDiscountRender.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/cart/render/util/PromotionPriceUtil.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderItem.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderSearchParams.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/enums/ClaimStatusEnum.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/enums/CouPonFlagEnum.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/enums/ModifyAddressEnums.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/enums/ShareStatusEnum.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderSimpleVO.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderSimpleXcxVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/mapper/OrderMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/promotion/entity/dos/Coupon.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/promotion/entity/dos/MemberCoupon.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/resources/mapper/lmk/CouponVirtualMapper.xml 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/resources/mapper/lmk/VideoMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager-api/src/main/java/cn/lili/controller/lmk/CardPackController.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager-api/src/main/java/cn/lili/controller/order/AfterSaleManagerController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller-api/src/main/java/cn/lili/controller/goods/GoodsStoreController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller-api/src/main/java/cn/lili/controller/order/AfterSaleStoreController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
buyer-api/src/main/java/cn/lili/controller/lmk/CouponCardController.java
New file
@@ -0,0 +1,37 @@
package cn.lili.controller.lmk;
import cn.lili.base.Result;
import cn.lili.modules.lmk.service.CouponVirtualService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Validated
@RequiredArgsConstructor
@Api(value = "小程序购物卡接口", tags = "小程序购物卡接口")
@RestController
@RequestMapping("/buyer/lmk/coupon/card")
public class CouponCardController {
    private final CouponVirtualService couponVirtualService;
    @PostMapping("/{cardId}")
    @ApiOperation(value = "领取购物卡", notes = "领取购物卡")
    public Result tackCardById(@PathVariable String cardId){
        return couponVirtualService.tackCardById(cardId);
    }
    @PostMapping("/changShareStatus/{cardId}")
    @ApiOperation(value = "更改分享状态", notes = "更改分享状态")
    public Result changShareStatus(@PathVariable String cardId){
        return couponVirtualService.changShareStatus(cardId);
    }
    @PostMapping("/couponCardInfo/{cardId}")
    @ApiOperation(value = "获取领取信息", notes = "获取领取信息")
    public Result couponCardInfo(@PathVariable String cardId){
        return couponVirtualService.couponCardInfo(cardId);
    }
}
buyer-api/src/main/java/cn/lili/controller/lmk/CouponVirtualController.java
New file
@@ -0,0 +1,37 @@
package cn.lili.controller.lmk;
import cn.lili.base.Result;
import cn.lili.modules.lmk.domain.query.CouponVirtualQuery;
import cn.lili.modules.lmk.service.CouponVirtualService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * lmk-shop-java
 * 虚拟优惠卷商品
 *
 * @author : zxl
 * @date : 2025-09-12 09:41
 **/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/buyer/lmk/couponVirtual")
public class CouponVirtualController {
    private final CouponVirtualService couponVirtualService;
    @GetMapping()
    @ApiOperation(value = "列表", notes = "列表")
    public Result getPage(CouponVirtualQuery query){
        //更具订单id查询
        return couponVirtualService.page(query);
    }
}
buyer-api/src/main/java/cn/lili/controller/order/AfterSaleBuyerController.java
@@ -64,6 +64,10 @@
    @GetMapping(value = "/get/{sn}")
    public ResultMessage<AfterSaleVO> get(@NotNull(message = "售后单号") @PathVariable("sn") String sn) {
        AfterSaleVO afterSale = OperationalJudgment.judgment(afterSaleService.getAfterSale(sn));
        String goodsImage = afterSale.getGoodsImage();
        if (StringUtils.isNotBlank(goodsImage)&&!goodsImage.contains("http")) {
            afterSale.setGoodsImage(cosUtil.getPreviewUrl(goodsImage));
        }
        return ResultUtil.data(afterSale);
    }
buyer-api/src/main/java/cn/lili/controller/order/OrderBuyerController.java
@@ -10,10 +10,12 @@
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.lmk.enums.general.AdminRoleEnum;
import cn.lili.modules.member.entity.dto.MemberAddressDTO;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderPackage;
import cn.lili.modules.order.order.entity.dto.OrderSearchParams;
import cn.lili.modules.order.order.entity.dto.OrderSearchXcxParams;
import cn.lili.modules.order.order.entity.enums.ModifyAddressEnums;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.entity.vo.OrderDetailVO;
import cn.lili.modules.order.order.entity.vo.OrderSimpleVO;
@@ -30,10 +32,12 @@
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
@@ -46,6 +50,7 @@
 * @author Chopper
 * @since 2020/11/16 10:08 下午
 */
@Slf4j
@RestController
@Api(tags = "买家端,订单接口")
@RequestMapping("/buyer/order/order")
@@ -115,7 +120,37 @@
        });
        return ResultUtil.data(orderDetailVO);
    }
    @ApiOperation(value = "订单明细")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, paramType = "path")
    })
    @GetMapping(value = "/editAddress/{orderSn}")
    public ResultMessage<OrderDetailVO> editAddress(@NotNull(message = "订单编号不能为空") @PathVariable("orderSn") String orderSn) {
        OrderDetailVO orderDetailVO = orderService.queryEditAddressDetail(orderSn);
        Order order = orderDetailVO.getOrder();
        if (!OrderStatusEnum.UNDELIVERED.name().equals(order.getOrderStatus())) {
            log.error("订单号为{}------------------->状态异常为{}",orderSn,order.getOrderStatus());
            throw new ServiceException("订单状态异常无法领取");
        }
        String modifyAddressFlag = order.getModifyAddressFlag();
        if (ModifyAddressEnums.USED.name().equals(modifyAddressFlag)) {
            throw new ServiceException("订单已被领取");
        }
        orderDetailVO.getOrderItems().forEach(orderItem -> {
            String image = orderItem.getImage();
            if (StringUtils.isNotBlank(image)&&!image.contains("http")) {
                orderItem.setImage(cosUtil.getPreviewUrl(image));
            }
        });
        return ResultUtil.data(orderDetailVO);
    }
    @ApiOperation(value = "修改收货人信息")
    @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path")
    @PostMapping(value = "/update/editAddress/{orderSn}/consignee")
    public ResultMessage<Object> consignee(@NotNull(message = "参数非法") @PathVariable String orderSn,
                                           @Valid @RequestBody MemberAddressDTO memberAddressDTO) {
        return ResultUtil.data(orderService.updateAddressConsignee(orderSn, memberAddressDTO));
    }
    @PreventDuplicateSubmissions
    @ApiOperation(value = "确认收货")
    @ApiImplicitParams({
framework/src/main/java/cn/lili/modules/goods/entity/dos/Goods.java
@@ -177,6 +177,10 @@
    @ApiModelProperty(value = "商品排序")
    private Integer goodsSort;
    @ApiModelProperty(value ="表示虚拟商品类型 现有类型:COUPON优惠劵")
    private String virtualGoodsType;
    public Goods() {
    }
@@ -194,6 +198,8 @@
        this.mobileIntro = goodsOperationDTO.getMobileIntro();
        this.goodsVideo = goodsOperationDTO.getGoodsVideo();
        this.price = goodsOperationDTO.getPrice();
        this.virtualGoodsType = goodsOperationDTO.getVirtualGoodsType();
        //不是预售商品预售时间置空
        if (!GoodsSalesModeEnum.PRESALE.name().equals(goodsOperationDTO.getSalesModel())) {
            goodsOperationDTO.setPreSaleBeginDate(null);
@@ -230,6 +236,12 @@
            if (!sku.containsKey("quantity") || StringUtil.isEmpty(sku.get("quantity").toString()) || Convert.toInt(sku.get("quantity").toString()) < 0) {
                throw new ServiceException(ResultCode.GOODS_SKU_QUANTITY_ERROR);
            }
            //没有优惠卷的商品需要移除优惠卷信息
            Object couponId = sku.get("couponId");
            if (couponId == null || StringUtil.isEmpty(couponId.toString())) {
                sku.remove("couponId");
                sku.remove("couponName");
            }
            sku.values().forEach(i -> {
                if (CharSequenceUtil.isBlank(i.toString())) {
                    throw new ServiceException(ResultCode.MUST_HAVE_GOODS_SKU_VALUE);
framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsSku.java
@@ -170,6 +170,12 @@
    @ApiModelProperty(value = "预警数量")
    private Integer alertQuantity;
    @ApiModelProperty(value ="优惠劵id")
    private String couponId;
    @ApiModelProperty(value ="优惠劵名")
    private String couponName;
    public Double getWeight() {
        if (weight == null) {
            return 0d;
@@ -223,6 +229,7 @@
        this.storeCategoryPath = goods.getStoreCategoryPath();
        this.freightTemplateId = goods.getTemplateId();
        this.recommend = goods.getRecommend();
    }
}
framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsOperationDTO.java
@@ -158,6 +158,14 @@
    private BigDecimal commission;
    @ApiModelProperty(value = "商品排序")
    private Integer goodsSort;
    @ApiModelProperty(value = "优惠劵id")
    private String couponId;
    @ApiModelProperty(value = "优惠劵名")
    private String couponName;
    @ApiModelProperty(value ="表示虚拟商品类型 现有类型:COUPON优惠劵")
    private String virtualGoodsType;
    public String getGoodsName() {
        //对商品对名称做一个极限处理。这里没有用xss过滤是因为xss过滤为全局过滤,影响很大。
framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java
@@ -159,7 +159,7 @@
        // 检查是否需要生成索引
        List<GoodsSku> goodsSkus = GoodsSkuBuilder.buildBatch(goods, goodsOperationDTO.getSkuList());
        renderGoodsSkuList(goodsSkus, goodsOperationDTO);
        System.out.println(goodsSkus);
        if (!goodsSkus.isEmpty()) {
            this.saveOrUpdateBatch(goodsSkus);
            this.updateGoodsStock(goodsSkus);
framework/src/main/java/cn/lili/modules/goods/sku/GoodsSkuBuilder.java
@@ -4,6 +4,7 @@
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.json.JSONUtil;
import cn.lili.common.utils.StringUtils;
import cn.lili.modules.goods.entity.dos.Goods;
import cn.lili.modules.goods.entity.dos.GoodsSku;
import org.springframework.stereotype.Component;
@@ -64,7 +65,7 @@
        Map<String, Object> specMap = new LinkedHashMap<>();
        // 原始规格项
        String[] ignoreOriginKeys = {"id", "sn", "cost", "price", "quantity", "weight", "alertQuantity"};
        String[] ignoreOriginKeys = {"id", "sn", "cost", "price", "quantity", "weight", "alertQuantity","couponId","couponName"};
        //获取规格信息
        for (Map.Entry<String, Object> spec : skuInfo.entrySet()) {
            //保存新增规格信息
@@ -83,6 +84,11 @@
        //规格信息
        if(StringUtils.isNotBlank(Convert.toStr(skuInfo.get("couponId")))){
            goodsSku.setCouponId(Convert.toStr(skuInfo.get("couponId"),""));
        }
        goodsSku.setCouponName(Convert.toStr(skuInfo.get("couponName"),""));
        goodsSku.setId(Convert.toStr(skuInfo.get("id"), ""));
        goodsSku.setSn(Convert.toStr(skuInfo.get("sn")));
        goodsSku.setWeight(Convert.toDouble(skuInfo.get("weight"), 0D));
framework/src/main/java/cn/lili/modules/lmk/domain/entity/CouponVirtual.java
New file
@@ -0,0 +1,85 @@
package cn.lili.modules.lmk.domain.entity;
import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
 * 虚拟商品优惠卷
 *
 * @author zxl
 * @since 2025-09-10
 */
@Data
@TableName("lmk_coupon_virtual")
public class CouponVirtual extends BaseEntity {
    private static final long serialVersionUID = 1L;
    @TableField("order_id")
    /** 订单id */
    private String orderId;
    @TableField("item_order_id")
    /** 订单id */
    private String itemOrderId;
    @TableField("goods_id")
    /** 商品id */
    private String goodsId;
    @TableField("sku_id")
    /** 商品id */
    private String skuId;
    @TableField("sku_name")
    /** 商品名称 */
    private String skuName;
    @TableField("coupon_id")
    /** 优惠卷id */
    private String couponId;
    @TableField("coupon_no")
    /** 编号 */
    private String couponNo;
    @TableField("coupon_name")
    /** 优惠卷名称 */
    private String couponName;
    @TableField("user_id")
    /** 用户id */
    private String userId;
    @TableField("user_nickname")
    /** 用户昵称 */
    private String userNickname;
    @TableField("claim_time")
    /** 领取时间 */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date claimTime;
    @TableField("name")
    /** 名称 */
    private String name;
    @TableField("share_status")
    /** 是否分享 */
    private String shareStatus;
    @TableField("claim_status")
    /** 是否领取 */
    private String claimStatus;
}
framework/src/main/java/cn/lili/modules/lmk/domain/form/CouponVirtualForm.java
New file
@@ -0,0 +1,72 @@
package cn.lili.modules.lmk.domain.form;
import cn.lili.base.AbsForm;
import cn.lili.group.Add;
import cn.lili.group.Update;
import cn.lili.modules.lmk.domain.entity.CouponVirtual;
import org.springframework.beans.BeanUtils;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.springframework.lang.NonNull;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
 * 虚拟商品优惠卷表单
 *
 * @author zxl
 * @since 2025-09-10
 */
@Data
@ApiModel(value = "CouponVirtual表单", description = "虚拟商品优惠卷表单")
public class CouponVirtualForm extends AbsForm {
    @NotNull(message = "订单id不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("订单id")
    private Long orderId;
    @NotNull(message = "商品id不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("商品id")
    private Long goodsId;
    @NotBlank(message = "商品名称不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("商品名称")
    private String skuName;
    @NotNull(message = "优惠卷id不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("优惠卷id")
    private Long couponId;
    @NotBlank(message = "编号不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("编号")
    private String couponNo;
    @NotBlank(message = "优惠卷名称不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("优惠卷名称")
    private String couponName;
    @NotBlank(message = "名称不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("名称")
    private String name;
    @NotBlank(message = "是否分享不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("是否分享")
    private String shareStatus;
    @NotBlank(message = "是否领取不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("是否领取")
    private String claimStatus;
    public static CouponVirtual getEntityByForm(@NonNull CouponVirtualForm form, CouponVirtual entity) {
        if(entity == null) {
          entity = new CouponVirtual();
        }
        BeanUtils.copyProperties(form, entity);
        return entity;
    }
}
framework/src/main/java/cn/lili/modules/lmk/domain/query/CouponVirtualQuery.java
New file
@@ -0,0 +1,28 @@
package cn.lili.modules.lmk.domain.query;
import java.util.List;
import cn.lili.base.AbsQuery;
import org.springframework.lang.NonNull;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 虚拟商品优惠卷查询
 *
 * @author zxl
 * @since 2025-09-10
 */
@Data
@ApiModel(value = "CouponVirtual查询参数", description = "虚拟商品优惠卷查询参数")
public class CouponVirtualQuery extends AbsQuery {
    private String orderId;
    private String userNickName;
    private String skuName;
}
framework/src/main/java/cn/lili/modules/lmk/domain/vo/CouponVirtualVO.java
New file
@@ -0,0 +1,77 @@
package cn.lili.modules.lmk.domain.vo;
import java.util.List;
import cn.lili.base.AbsVo;
import cn.lili.modules.lmk.domain.entity.CouponVirtual;
import com.baomidou.mybatisplus.annotation.TableField;
import org.springframework.lang.NonNull;
import org.springframework.beans.BeanUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
 * 虚拟商品优惠卷展示
 *
 * @author zxl
 * @since 2025-09-10
 */
@Data
@ApiModel(value = "虚拟商品优惠卷响应数据", description = "虚拟商品优惠卷响应数据")
public class CouponVirtualVO extends AbsVo {
    /** 订单id */
    @ApiModelProperty("订单id")
    private String orderId;
    /** 商品id */
    @ApiModelProperty("商品id")
    private String goodsId;
    /** 商品名称 */
    @ApiModelProperty("商品名称")
    private String skuName;
    /** 优惠卷id */
    @ApiModelProperty("优惠卷id")
    private String couponId;
    /** 编号 */
    @ApiModelProperty("编号")
    private String couponNo;
    /** 优惠卷名称 */
    @ApiModelProperty("优惠卷名称")
    private String couponName;
    /** 名称 */
    @ApiModelProperty("名称")
    private String name;
    /** 是否分享 */
    @ApiModelProperty("是否分享")
    private String shareStatus;
    /** 是否领取 */
    @ApiModelProperty("是否领取")
    private String claimStatus;
    private Boolean deleteFlag;
    private String original;
    private String userNickName;
    public static CouponVirtualVO getVoByEntity(@NonNull CouponVirtual entity, CouponVirtualVO vo) {
        if(vo == null) {
            vo = new CouponVirtualVO();
        }
        BeanUtils.copyProperties(entity, vo);
        return vo;
    }
}
framework/src/main/java/cn/lili/modules/lmk/domain/vo/CouponVirtualVOInfo.java
New file
@@ -0,0 +1,72 @@
package cn.lili.modules.lmk.domain.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
 * lmk-shop-java
 *
 * @author : zxl
 * @date : 2025-09-12 15:19
 **/
@Data
public class CouponVirtualVOInfo {
    private static final long serialVersionUID = 1L;
    private String id;
    private String orderId;
    private String itemOrderId;
    private String goodsId;
    private String skuId;
    private String skuName;
    private String couponId;
    private String couponNo;
    private String couponName;
    private String userId;
    private String userNickname;
    /** 领取时间 */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date claimTime;
    /** 领取时间 */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /** 名称 */
    private String name;
    /** 是否分享 */
    private String shareStatus;
    /** 是否领取 */
    private String claimStatus;
    private String goodsUrl;
}
framework/src/main/java/cn/lili/modules/lmk/enums/general/VirtualGoodsTypeEnum.java
New file
@@ -0,0 +1,13 @@
package cn.lili.modules.lmk.enums.general;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum VirtualGoodsTypeEnum {
    COUPON("虚拟优惠劵");
    private String description;
}
framework/src/main/java/cn/lili/modules/lmk/mapper/CouponVirtualMapper.java
New file
@@ -0,0 +1,35 @@
package cn.lili.modules.lmk.mapper;
import cn.lili.modules.lmk.domain.entity.CouponVirtual;
import cn.lili.modules.lmk.domain.query.CouponVirtualQuery;
import cn.lili.modules.lmk.domain.vo.CouponVirtualVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * 虚拟商品优惠卷 Mapper 接口
 *
 * @author zxl
 * @since 2025-09-10
 */
@Mapper
public interface CouponVirtualMapper extends BaseMapper<CouponVirtual> {
    /**
     * id查找虚拟商品优惠卷
     * @param id
     * @return
     */
    CouponVirtualVO getById(Integer id);
    /**
    *  分页
    */
    IPage getPage(IPage page, @Param("query") CouponVirtualQuery query);
}
framework/src/main/java/cn/lili/modules/lmk/service/CouponVirtualService.java
New file
@@ -0,0 +1,74 @@
package cn.lili.modules.lmk.service;
import cn.lili.base.Result;
import cn.lili.modules.lmk.domain.entity.CouponVirtual;
import cn.lili.modules.lmk.domain.form.CouponVirtualForm;
import cn.lili.modules.lmk.domain.query.CouponVirtualQuery;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
 * 虚拟商品优惠卷 服务类
 *
 * @author zxl
 * @since 2025-09-10
 */
public interface CouponVirtualService extends IService<CouponVirtual> {
    /**
     * 添加
     * @param form
     * @return
     */
    Result add(CouponVirtualForm form);
    /**
     * 修改
     * @param form
     * @return
     */
    Result update(CouponVirtualForm form);
    /**
     * 批量删除
     * @param ids
     * @return
     */
    Result remove(List<String> ids);
    /**
     * id删除
     * @param id
     * @return
     */
    Result removeById(String id);
    /**
     * 分页查询
     * @param query
     * @return
     */
    Result page(CouponVirtualQuery query);
    /**
     * 根据id查找
     * @param id
     * @return
     */
    Result detail(Integer id);
    /**
     * 列表
     * @return
     */
    Result all();
    Result tackCardById( String cardId);
    Result changShareStatus( String cardId);
    Result couponCardInfo(String cardId);
}
framework/src/main/java/cn/lili/modules/lmk/service/impl/CouponVirtualServiceImpl.java
New file
@@ -0,0 +1,262 @@
package cn.lili.modules.lmk.service.impl;
import cn.lili.base.Result;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.modules.goods.service.GoodsService;
import cn.lili.modules.lmk.domain.entity.CouponVirtual;
import cn.lili.modules.lmk.domain.form.CouponVirtualForm;
import cn.lili.modules.lmk.domain.query.CouponVirtualQuery;
import cn.lili.modules.lmk.domain.vo.CouponVirtualVO;
import cn.lili.modules.lmk.domain.vo.CouponVirtualVOInfo;
import cn.lili.modules.lmk.mapper.CouponVirtualMapper;
import cn.lili.modules.lmk.service.CouponVirtualService;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.order.order.entity.enums.ClaimStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.entity.enums.RefundStatusEnum;
import cn.lili.modules.order.order.entity.enums.ShareStatusEnum;
import cn.lili.modules.order.order.service.OrderItemService;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.promotion.service.MemberCouponService;
import cn.lili.utils.COSUtil;
import cn.lili.utils.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
 * 虚拟商品优惠卷 服务实现类
 *
 * @author zxl
 * @since 2025-09-10
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CouponVirtualServiceImpl extends ServiceImpl<CouponVirtualMapper, CouponVirtual> implements CouponVirtualService {
    private final CouponVirtualMapper couponVirtualMapper;
    private final RedissonClient redissonClient;
    private final static String LOCK_COUPON_VIRTUAL_CARD_ID = "lock_coupon_virtual_card_id:";
    private final OrderItemService orderItemService;
    private final OrderService orderService;
    private final MemberCouponService memberCouponService;
    private final GoodsService goodsService;
    private final COSUtil cosUtil;
    /**
     * 添加
     *
     * @param form
     * @return
     */
    @Override
    public Result add(CouponVirtualForm form) {
        CouponVirtual entity = CouponVirtualForm.getEntityByForm(form, null);
        baseMapper.insert(entity);
        return Result.ok("添加成功");
    }
    /**
     * 修改
     *
     * @param form
     * @return
     */
    @Override
    public Result update(CouponVirtualForm form) {
        CouponVirtual entity = baseMapper.selectById(form.getId());
        // 为空抛IllegalArgumentException,做全局异常处理
        Assert.notNull(entity, "记录不存在");
        BeanUtils.copyProperties(form, entity);
        baseMapper.updateById(entity);
        return Result.ok("修改成功");
    }
    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @Override
    public Result remove(List<String> ids) {
        baseMapper.deleteBatchIds(ids);
        return Result.ok("删除成功");
    }
    /**
     * id删除
     *
     * @param id
     * @return
     */
    @Override
    public Result removeById(String id) {
        baseMapper.deleteById(id);
        return Result.ok("删除成功");
    }
    /**
     * 分页查询
     *
     * @param query
     * @return
     */
    @Override
    public Result page(CouponVirtualQuery query) {
        IPage<CouponVirtualVO> page = PageUtil.getPage(query, CouponVirtualVO.class);
        baseMapper.getPage(page, query);
        for (CouponVirtualVO record : page.getRecords()) {
            String original = record.getOriginal();
            if (StringUtils.isNotBlank(original) && !original.contains("http")) {
                record.setOriginal(cosUtil.getPreviewUrl(original));
            }
        }
        return Result.ok().data(page.getRecords()).total(page.getTotal());
    }
    /**
     * 根据id查找
     *
     * @param id
     * @return
     */
    @Override
    public Result detail(Integer id) {
        CouponVirtualVO vo = baseMapper.getById(id);
        Assert.notNull(vo, "记录不存在");
        return Result.ok().data(vo);
    }
    /**
     * 列表
     *
     * @return
     */
    @Override
    public Result all() {
        List<CouponVirtual> entities = baseMapper.selectList(null);
        List<CouponVirtualVO> vos = entities.stream()
                .map(entity -> CouponVirtualVO.getVoByEntity(entity, null))
                .collect(Collectors.toList());
        return Result.ok().data(vos);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result tackCardById(String cardId) {
        log.info("被领取的礼品卡的id为--------------------------->{}",cardId);
        AuthUser currentUser = UserContext.getCurrentUser();
        if (currentUser == null) {
            throw new ServiceException("当前用户没有登录");
        }
        String userId = currentUser.getId();
        String nickName = currentUser.getNickName();
        RLock lock = null;
        try {
            lock = redissonClient.getLock(LOCK_COUPON_VIRTUAL_CARD_ID + cardId);
            lock.lock();
            LambdaQueryWrapper<CouponVirtual> forUpdate = Wrappers.<CouponVirtual>lambdaQuery().eq(CouponVirtual::getId, cardId).last("FOR UPDATE");
            CouponVirtual cardInfo = this.getOne(forUpdate);
            if (cardInfo == null) {
                throw new ServiceException("当前优惠卷不存在");
            }
            if (ClaimStatusEnum.CLAIM.name().equals(cardInfo.getClaimStatus())) {
                throw new ServiceException("当前购物卡已经被领取");
            }
            //校验订单状态是否正常
            String orderNo = cardInfo.getOrderId();
            if (StringUtils.isBlank(orderNo)) {
                throw new ServiceException("当前订单不存在无法领取");
            }
            String itemOrderId = cardInfo.getItemOrderId();
            Order order = orderService.getBySn(orderNo);
            if (order == null) {
                throw new ServiceException("当前订单不存在无法领取");
            }
            if (!OrderStatusEnum.COMPLETED.name().equals(order.getOrderStatus())) {
                throw new ServiceException("订单状态异常无法领取");
            }
            OrderItem orderItem = orderItemService.getById(itemOrderId);
            if (orderItem == null) {
                throw new ServiceException("当前订单不存在");
            }
            String orderSn = orderItem.getOrderSn();
            if (!orderNo.equals(orderSn)) {
                throw new ServiceException("订单无法对应无法领取");
            }
            if (!RefundStatusEnum.NO_REFUND.name().equals(orderItem.getIsRefund())) {
                throw new ServiceException("当前订单已退款无法领取");
            }
            //领取对应的优惠卷
            memberCouponService.receiveCoupon(cardInfo.getCouponId(),userId , nickName);
            cardInfo.setUserNickname(nickName);
            cardInfo.setUserId(userId);
            cardInfo.setClaimStatus(ClaimStatusEnum.CLAIM.name());
            cardInfo.setShareStatus(ShareStatusEnum.SHARE.name());
            cardInfo.setClaimTime(new Date());
            boolean b = this.updateById(cardInfo);
            //去领取优惠卷
            if (!b) {
                throw new RuntimeException("领取失败");
            }
            return Result.ok().data(cardInfo.getCouponId());
        } finally {
            if ( lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    @Override
    public Result changShareStatus(String cardId) {
        CouponVirtual couponVirtual = this.getById(cardId);
        couponVirtual.setShareStatus(ShareStatusEnum.SHARE.name());
        this.updateById(couponVirtual);
        return Result.ok();
    }
    @Override
    public Result couponCardInfo(String cardId) {
        CouponVirtual couponVirtual = this.getById(cardId);
        if (couponVirtual == null) {
            throw new ServiceException("当前购物卡不存在");
        }
        CouponVirtualVOInfo virtualVOInfo = new CouponVirtualVOInfo();
        BeanUtils.copyProperties(couponVirtual, virtualVOInfo);
        String goodsId = virtualVOInfo.getGoodsId();
        String url = goodsService.getById(goodsId).getOriginal();
        if (StringUtils.isNotBlank(url) && !url.contains("http")) {
            virtualVOInfo.setGoodsUrl(cosUtil.getPreviewUrl(url));
        }
        return Result.ok().data(virtualVOInfo);
    }
}
framework/src/main/java/cn/lili/modules/order/cart/entity/dto/SkuMapDTO.java
New file
@@ -0,0 +1,12 @@
package cn.lili.modules.order.cart.entity.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class SkuMapDTO {
    private String skuId;
    private Double price;
}
framework/src/main/java/cn/lili/modules/order/cart/render/impl/CouponRender.java
@@ -5,6 +5,7 @@
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.StringUtils;
import cn.lili.modules.order.cart.entity.dto.MemberCouponDTO;
import cn.lili.modules.order.cart.entity.dto.SkuMapDTO;
import cn.lili.modules.order.cart.entity.dto.TradeDTO;
import cn.lili.modules.order.cart.entity.enums.RenderStepEnums;
import cn.lili.modules.order.cart.entity.vo.CartSkuVO;
@@ -240,8 +241,13 @@
        //接收具体优惠券信息
        MemberCoupon coupon = memberCouponDTO.getMemberCoupon();
        Integer goodsNum = tradeDTO.getSkuList().stream().map(CartSkuVO::getNum).reduce(Integer::sum).get();
        //处理一个极端情况,如果优惠券满减金额大于订单金额
        if (coupon.getCouponType().equals(CouponTypeEnum.PRICE.name()) && coupon.getPrice() > countPrice) {
        if (coupon.getCouponType().equals(CouponTypeEnum.PRICE.name())
                && coupon.getPrice() > countPrice
                && coupon.getGoodsUseLimitNum() >= goodsNum) {
            //将符合优惠券的金额写入,即最大扣减金额
            coupon.setPrice(countPrice);
        }
@@ -264,21 +270,42 @@
     * @param memberCouponDTO 用于计算优惠券结算详情
     */
    private void renderCouponPrice(Map<String, Double> couponMap, TradeDTO tradeDTO, MemberCoupon coupon, MemberCouponDTO memberCouponDTO) {
        List<SkuMapDTO> skuMapDTOList = new ArrayList<>();
        for (String skuId : couponMap.keySet()) {
            skuMapDTOList.add(new SkuMapDTO(skuId, couponMap.get(skuId)));
        }
        skuMapDTOList.sort((o1, o2) -> o2.getPrice().compareTo(o1.getPrice()));
        //分发优惠券
        PromotionPriceUtil.recountPrice(tradeDTO, memberCouponDTO.getSkuDetail(), memberCouponDTO.getMemberCoupon().getPrice(),
                Boolean.TRUE.equals(coupon.getPlatformFlag()) ?
                        PromotionTypeEnum.PLATFORM_COUPON : PromotionTypeEnum.COUPON, memberCouponDTO.getMemberCoupon().getCouponId());
        PromotionPriceUtil.recountPrice(tradeDTO, memberCouponDTO.getSkuDetail(),
                memberCouponDTO.getMemberCoupon().getPrice(),
                Boolean.TRUE.equals(coupon.getPlatformFlag()) ? PromotionTypeEnum.PLATFORM_COUPON : PromotionTypeEnum.COUPON,
                memberCouponDTO.getMemberCoupon().getCouponId(),
                memberCouponDTO.getMemberCoupon().getGoodsUseLimitNum());
        //如果是平台券 则需要计算商家承担比例
        if (Boolean.TRUE.equals(coupon.getPlatformFlag()) && coupon.getStoreCommission() > 0) {
            Integer i = coupon.getGoodsUseLimitNum();
            //循环所有优惠券
            for (String skuId : couponMap.keySet()) {
            for (SkuMapDTO skuMapDTO : skuMapDTOList) {
                for (CartSkuVO cartSkuVO : tradeDTO.getSkuList()) {
                    //写入平台优惠券承担比例
                    if (cartSkuVO.getGoodsSku().getId().equals(skuId)) {
                        //写入店铺承担比例
                        cartSkuVO.getPriceDetailDTO().setSiteCouponPoint(coupon.getStoreCommission());
                    if (cartSkuVO.getGoodsSku().getId().equals(skuMapDTO.getSkuId())) {
                        if (i == null) {
                            cartSkuVO.getPriceDetailDTO().setSiteCouponPoint(coupon.getStoreCommission());
                        } else {
                            if (i > 0) {
                                //写入店铺承担比例
                                if (i >= cartSkuVO.getNum()) {
                                    cartSkuVO.getPriceDetailDTO().setSiteCouponPoint(coupon.getStoreCommission());
                                } else {
                                    cartSkuVO.getPriceDetailDTO().setSiteCouponPoint(coupon.getStoreCommission() * i / cartSkuVO.getNum());
                                }
                                i = i - cartSkuVO.getNum();
                            } else {
                                return;
                            }
                        }
                    }
                }
            }
@@ -294,6 +321,7 @@
     * @param coupon    优惠券信息
     */
    private void renderCouponDiscount(Map<String, Double> couponMap, TradeDTO tradeDTO, MemberCoupon coupon) {
        //循环所有优惠券
        for (String skuId : couponMap.keySet()) {
@@ -315,9 +343,10 @@
                    }
                    priceDetailDTO.setCouponPrice(CurrencyUtil.add(priceDetailDTO.getCouponPrice(), discountCouponPrice));
                } else {
                    return;
                }
            }
        }
    }
}
framework/src/main/java/cn/lili/modules/order/cart/render/impl/FullDiscountRender.java
@@ -86,7 +86,7 @@
                        if (isFull(countPrice, cart)) {
                            //如果减现金
                            if (Boolean.TRUE.equals(fullDiscount.getFullMinusFlag())) {
                                PromotionPriceUtil.recountPrice(tradeDTO, skuPriceDetail, fullDiscount.getFullMinus(), PromotionTypeEnum.FULL_DISCOUNT, fullDiscountVO.getId());
                                PromotionPriceUtil.recountPrice(tradeDTO, skuPriceDetail, fullDiscount.getFullMinus(), PromotionTypeEnum.FULL_DISCOUNT, fullDiscountVO.getId(), 0);
                            }
                            //打折
                            else if (Boolean.TRUE.equals(fullDiscount.getFullRateFlag())) {
framework/src/main/java/cn/lili/modules/order/cart/render/util/PromotionPriceUtil.java
@@ -3,11 +3,13 @@
import cn.hutool.core.map.MapUtil;
import cn.lili.common.enums.PromotionTypeEnum;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.modules.order.cart.entity.dto.SkuMapDTO;
import cn.lili.modules.order.cart.entity.dto.TradeDTO;
import cn.lili.modules.order.cart.entity.vo.CartSkuVO;
import cn.lili.modules.order.order.entity.dto.DiscountPriceItem;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -32,79 +34,111 @@
     * @param promotionTypeEnum  促销类型
     */
    public static void recountPrice(TradeDTO tradeDTO, Map<String, Double> skuPromotionDetail, Double discountPrice,
                                    PromotionTypeEnum promotionTypeEnum, String activityId) {
                                    PromotionTypeEnum promotionTypeEnum, String activityId, Integer goodsUseLimitNum) {
        // sku 促销信息非空判定
        if (skuPromotionDetail == null || skuPromotionDetail.isEmpty()) {
            return;
        }
        //计算总金额
        Double totalPrice = 0D;
        for (Double value : skuPromotionDetail.values()) {
            totalPrice = CurrencyUtil.add(totalPrice, value);
        }
        //极端情况,如果扣减金额小于需要支付的金额,则扣减金额=支付金额,不能成为负数
        if (discountPrice > totalPrice) {
            discountPrice = totalPrice;
            for (String skuId : skuPromotionDetail.keySet()) {
                //获取对应商品进行计算
                for (CartSkuVO cartSkuVO : tradeDTO.getSkuList()) {
                    if (cartSkuVO.getGoodsSku().getId().equals(skuId)) {
                        //优惠券金额,则计入优惠券 ,其他则计入总的discount price
                        if (promotionTypeEnum == PromotionTypeEnum.COUPON) {
                            cartSkuVO.getPriceDetailDTO().setCouponPrice(cartSkuVO.getPriceDetailDTO().getGoodsPrice());
                        } else {
                            cartSkuVO.getPriceDetailDTO().setDiscountPrice(cartSkuVO.getPriceDetailDTO().getGoodsPrice());
                        }
                    }
                }
            }
        }
        //获取购物车信息
        List<CartSkuVO> skuVOList = tradeDTO.getSkuList();
        // 获取map分配sku的总数,如果是最后一个商品分配金额,则将金额从百分比改为总金额扣减,避免出现小数除不尽
        AtomicInteger count = new AtomicInteger(skuPromotionDetail.size());
        //已优惠金额
        AtomicReference<Double> deducted = new AtomicReference<>(0D);
        for (String skuId : skuPromotionDetail.keySet()) {
        if (goodsUseLimitNum > 0) {
            // 处理限制商品数量逻辑,只处理一个商品
            List<SkuMapDTO> skuMapDTOList = new ArrayList<>();
            for (String skuId : skuPromotionDetail.keySet()) {
                skuMapDTOList.add(new SkuMapDTO(skuId, skuPromotionDetail.get(skuId)));
            }
            skuMapDTOList.sort((o1, o2) -> o2.getPrice().compareTo(o1.getPrice()));
            //获取对应商品进行计算
            final int[] i = {0};
            for (SkuMapDTO skuMap : skuMapDTOList) {
            Double finalDiscountPrice = discountPrice;
            Double finalTotalPrice = totalPrice;
            skuVOList.stream().filter(l -> l.getGoodsSku().getId().equals(skuId)).findFirst().ifPresent(cartSkuVO -> {
                //sku 优惠金额
                Double skuDiscountPrice;
                count.getAndDecrement();
                //获取对应商品进行计算
                Double finalDiscountPrice = discountPrice;
                skuVOList.stream().filter(l -> l.getGoodsSku().getId().equals(skuMap.getSkuId())).findFirst().ifPresent(cartSkuVO -> {
                    //sku 优惠金额
                    Double skuDiscountPrice = 0D;
                    if (i[0] < 1) {
                //非最后一个商品,则按照比例计算
                if (count.get() > 0) {
                    //商品金额占比
                    double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), finalTotalPrice, 4);
                    //商品优惠金额
                    skuDiscountPrice = CurrencyUtil.mul(finalDiscountPrice, point);
                    //累加已优惠金额
                    deducted.set(CurrencyUtil.add(deducted.get(), skuDiscountPrice));
                        if (cartSkuVO.getUtilPrice() > finalDiscountPrice) {
                            skuDiscountPrice = cartSkuVO.getUtilPrice() - finalDiscountPrice;
                        } else {
                            skuDiscountPrice = cartSkuVO.getUtilPrice();
                        }
                        //累加已优惠金额
                        deducted.set(CurrencyUtil.add(deducted.get(), skuDiscountPrice));
                        i[0] = i[0] + 1;
                    }
                    calculateCartSkuPromotionsPrice(cartSkuVO, skuDiscountPrice, promotionTypeEnum, activityId);
                });
                discountPrice = deducted.get();
            }
        } else {
            for (Double value : skuPromotionDetail.values()) {
                totalPrice = CurrencyUtil.add(totalPrice, value);
            }
            //极端情况,如果扣减金额小于需要支付的金额,则扣减金额=支付金额,不能成为负数
            if (discountPrice > totalPrice) {
                discountPrice = totalPrice;
                for (String skuId : skuPromotionDetail.keySet()) {
                    //获取对应商品进行计算
                    for (CartSkuVO cartSkuVO : tradeDTO.getSkuList()) {
                        if (cartSkuVO.getGoodsSku().getId().equals(skuId)) {
                            //优惠券金额,则计入优惠券 ,其他则计入总的discount price
                            if (promotionTypeEnum == PromotionTypeEnum.COUPON) {
                                cartSkuVO.getPriceDetailDTO().setCouponPrice(cartSkuVO.getPriceDetailDTO().getGoodsPrice());
                            } else {
                                cartSkuVO.getPriceDetailDTO().setDiscountPrice(cartSkuVO.getPriceDetailDTO().getGoodsPrice());
                            }
                        }
                    }
                }
                // 如果是最后一个商品 则减去之前优惠的金额来进行计算
                else {
                    skuDiscountPrice = CurrencyUtil.sub(finalDiscountPrice, deducted.get());
                }
            }
                calculateCartSkuPromotionsPrice(cartSkuVO, skuDiscountPrice, promotionTypeEnum, activityId);
            });
            // 获取map分配sku的总数,如果是最后一个商品分配金额,则将金额从百分比改为总金额扣减,避免出现小数除不尽
            AtomicInteger count = new AtomicInteger(skuPromotionDetail.size());
            for (String skuId : skuPromotionDetail.keySet()) {
                //获取对应商品进行计算
                Double finalDiscountPrice = discountPrice;
                Double finalTotalPrice = totalPrice;
                skuVOList.stream().filter(l -> l.getGoodsSku().getId().equals(skuId)).findFirst().ifPresent(cartSkuVO -> {
                    //sku 优惠金额
                    Double skuDiscountPrice;
                    count.getAndDecrement();
                    //非最后一个商品,则按照比例计算
                    if (count.get() > 0) {
                        //商品金额占比
                        double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), finalTotalPrice, 4);
                        //商品优惠金额
                        skuDiscountPrice = CurrencyUtil.mul(finalDiscountPrice, point);
                        //累加已优惠金额
                        deducted.set(CurrencyUtil.add(deducted.get(), skuDiscountPrice));
                    }
                    // 如果是最后一个商品 则减去之前优惠的金额来进行计算
                    else {
                        skuDiscountPrice = CurrencyUtil.sub(finalDiscountPrice, deducted.get());
                    }
                    calculateCartSkuPromotionsPrice(cartSkuVO, skuDiscountPrice, promotionTypeEnum, activityId);
                });
            }
        }
        calculateNotEnoughPromotionsPrice(skuVOList, skuPromotionDetail, discountPrice, totalPrice, promotionTypeEnum, activityId);
    }
@@ -218,9 +252,11 @@
            // 如果还有剩余金额,则继续分摊
            if (balance.get() > 0) {
                skuPromotionDetailClone.remove(lastSkuId.toString());
                double lastDiscountPrice = CurrencyUtil.sub(discountPrice, skuPromotionDetail.get(lastSkuId.toString()));
                double lastTotalPrice = CurrencyUtil.sub(totalPrice, skuPromotionDetail.get(lastSkuId.toString()));
                filterEnoughSku(skuVOList, skuPromotionDetailClone, lastDiscountPrice, lastTotalPrice, balance, lastSkuId, promotionTypeEnum, activityId);
                if (skuPromotionDetail.containsKey(lastSkuId.toString())) {
                    double lastDiscountPrice = CurrencyUtil.sub(discountPrice, skuPromotionDetail.get(lastSkuId.toString()));
                    double lastTotalPrice = CurrencyUtil.sub(totalPrice, skuPromotionDetail.get(lastSkuId.toString()));
                    filterEnoughSku(skuVOList, skuPromotionDetailClone, lastDiscountPrice, lastTotalPrice, balance, lastSkuId, promotionTypeEnum, activityId);
                }
            } else {
                break;
            }
@@ -235,9 +271,9 @@
    private static void filterEnoughSku(List<CartSkuVO> skuVOList, Map<String, Double> skuPromotionDetail,
                                                   Double discountPrice, Double totalPrice,
                                                   AtomicReference<Double> balance, StringBuilder lastSkuId,
                                                   PromotionTypeEnum promotionTypeEnum, String activityId) {
                                        Double discountPrice, Double totalPrice,
                                        AtomicReference<Double> balance, StringBuilder lastSkuId,
                                        PromotionTypeEnum promotionTypeEnum, String activityId) {
        AtomicInteger count = new AtomicInteger(skuPromotionDetail.size());
        AtomicReference<Double> countPrice = new AtomicReference<>(0D);
        for (String skuId : skuPromotionDetail.keySet()) {
framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java
@@ -187,6 +187,17 @@
    private String orderType;
    /**
     * @see CouPonFlagEnum
     */
    @ApiModelProperty(value = "是否是礼品卡")
    private String couponFlag;
    /**
     * 订单地址修改状态
     */
    @ApiModelProperty(value = "订单地址修改状态")
    private String modifyAddressFlag;
    /**
     * @see OrderPromotionTypeEnum
     */
    @ApiModelProperty(value = "订单促销类型")
framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderItem.java
@@ -85,6 +85,12 @@
    @ApiModelProperty(value = "促销id")
    private String promotionId;
    @ApiModelProperty(value = "优惠卷id")
    private String couponId;
    @ApiModelProperty(value = "优惠卷名称")
    private String couponName;
    @ApiModelProperty(value = "销售金额")
    private Double goodsPrice;
framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderSearchParams.java
@@ -117,6 +117,9 @@
    @ApiModelProperty(value = "订单促销类型")
    private String orderPromotionType;
    private String couponFlag;
    public <T> QueryWrapper<T> queryWrapper() {
        AuthUser currentUser = UserContext.getCurrentUser();
        QueryWrapper<T> wrapper = new QueryWrapper<>();
@@ -180,6 +183,8 @@
        //按评价状态
        wrapper.eq(CharSequenceUtil.isNotEmpty(commentStatus), "oi.comment_status", commentStatus);
        wrapper.eq(CharSequenceUtil.isNotEmpty(couponFlag),"o.coupon_flag", couponFlag);
        //按标签查询
        if (CharSequenceUtil.isNotEmpty(tag)) {
            String orderStatusColumn = "o.order_status";
framework/src/main/java/cn/lili/modules/order/order/entity/enums/ClaimStatusEnum.java
New file
@@ -0,0 +1,30 @@
package cn.lili.modules.order.order.entity.enums;
/**
 * 领取状态状态
 *
 **/
public enum ClaimStatusEnum {
    /**
     * 分享
     */
    CLAIM("领取"),
    /**
     * 未分享
     */
    NOT_CLAIM("未领取");
    private final String description;
    ClaimStatusEnum(String description) {
        this.description = description;
    }
    public String description() {
        return this.description;
    }
}
framework/src/main/java/cn/lili/modules/order/order/entity/enums/CouPonFlagEnum.java
New file
@@ -0,0 +1,29 @@
package cn.lili.modules.order.order.entity.enums;
/**
 * 分享状态
 *
 **/
public enum CouPonFlagEnum {
    /**
     * 礼品卡
     */
    COUPON("礼品卡"),
    /**
     * 不是礼品卡
     */
    NOT_COUPON("不是礼品卡");
    private final String description;
    CouPonFlagEnum(String description) {
        this.description = description;
    }
    public String description() {
        return this.description;
    }
}
framework/src/main/java/cn/lili/modules/order/order/entity/enums/ModifyAddressEnums.java
New file
@@ -0,0 +1,11 @@
package cn.lili.modules.order.order.entity.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum ModifyAddressEnums {
    USED("已被领取");
    private final String des;
}
framework/src/main/java/cn/lili/modules/order/order/entity/enums/ShareStatusEnum.java
New file
@@ -0,0 +1,29 @@
package cn.lili.modules.order.order.entity.enums;
/**
 * 分享状态
 *
 **/
public enum ShareStatusEnum {
    /**
     * 分享
     */
    SHARE("分享"),
    /**
     * 未分享
     */
    NOT_SHARE("未分享");
    private final String description;
    ShareStatusEnum(String description) {
        this.description = description;
    }
    public String description() {
        return this.description;
    }
}
framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderSimpleVO.java
@@ -159,6 +159,7 @@
    private String consigneeName;
    private String consigneeMobile;
    private String modifyAddressFlag;
    private String memberId;
framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderSimpleXcxVO.java
@@ -27,6 +27,8 @@
@Data
public class OrderSimpleXcxVO {
    private String id;
    @ApiModelProperty("sn")
    private String sn;
@@ -154,6 +156,10 @@
    @ApiModelProperty(value = "卖家订单备注")
    private String sellerRemark;
    @ApiModelProperty(value = "是否是礼品卡")
    private String couponFlag;
    private int i = 0;
    public List<OrderItemVO> initOrderItems() {
        if (CharSequenceUtil.isEmpty(groupGoodsId)) {
framework/src/main/java/cn/lili/modules/order/order/mapper/OrderMapper.java
@@ -90,6 +90,7 @@
            "o.consignee_mobile AS consigneeMobile," +
            "o.member_id AS memberId," +
            "m.nick_name AS nickName," +
            "o.modify_address_flag AS modifyAddressFlag,"+
            " GROUP_CONCAT(oi.goods_id) as group_goods_id," +
            " GROUP_CONCAT(oi.sku_id) as group_sku_id," +
            " GROUP_CONCAT(oi.num) as group_num" +
@@ -117,6 +118,7 @@
            "o.consignee_name AS consigneeName," +
            "o.consignee_mobile AS consigneeMobile," +
            "o.member_id AS memberId," +
            "o.modify_address_flag AS modifyAddressFlag,"+
            " GROUP_CONCAT(oi.goods_id) as group_goods_id," +
            " GROUP_CONCAT(oi.sku_id) as group_sku_id," +
            " GROUP_CONCAT(oi.num) as group_num" +
@@ -139,8 +141,8 @@
     * @param queryWrapper 查询条件
     * @return 简短订单分页
     */
    @Select("select o.sn,o.flow_price,o.create_time,o.order_status,o.pay_status,o.payment_method,o.payment_time,o.member_name,o.store_name as " +
            "store_name,o.store_id as store_id,o.client_type,o.order_type,o.deliver_status,o.order_promotion_type,o.seller_remark " +
    @Select("select o.id,o.sn,o.flow_price,o.create_time,o.order_status,o.pay_status,o.payment_method,o.payment_time,o.member_name,o.store_name as " +
            "store_name,o.store_id as store_id,o.client_type,o.order_type,o.deliver_status,o.order_promotion_type,o.seller_remark,o.coupon_flag " +
            ",GROUP_CONCAT(oi.goods_id) as group_goods_id," +
            " GROUP_CONCAT(oi.sku_id) as group_sku_id," +
            " GROUP_CONCAT(oi.num) as group_num" +
framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java
@@ -127,6 +127,8 @@
     */
    OrderDetailVO queryDetail(String orderSn);
    OrderDetailVO queryEditAddressDetail(String orderSn);
    String sendMqMessage(String  snNo);
    /**
     * 创建订单
@@ -175,6 +177,8 @@
     */
    Order updateConsignee(String orderSn, MemberAddressDTO memberAddressDTO);
    Order updateAddressConsignee(String orderSn, MemberAddressDTO memberAddressDTO);
    /**
     * 订单发货
     *
framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java
@@ -27,8 +27,10 @@
import cn.lili.modules.goods.entity.dos.Goods;
import cn.lili.modules.goods.entity.dto.GoodsCompleteMessage;
import cn.lili.modules.goods.service.GoodsService;
import cn.lili.modules.lmk.domain.entity.CouponVirtual;
import cn.lili.modules.lmk.domain.vo.OrderCountVO;
import cn.lili.modules.lmk.enums.general.AdminRoleEnum;
import cn.lili.modules.lmk.service.CouponVirtualService;
import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.entity.dto.MemberAddressDTO;
import cn.lili.modules.member.mapper.MemberMapper;
@@ -47,7 +49,9 @@
import cn.lili.modules.permission.entity.dos.AdminUser;
import cn.lili.modules.permission.service.AdminUserService;
import cn.lili.modules.permission.service.RoleService;
import cn.lili.modules.promotion.entity.dos.Coupon;
import cn.lili.modules.promotion.entity.dos.Pintuan;
import cn.lili.modules.promotion.service.CouponService;
import cn.lili.modules.promotion.service.PintuanService;
import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO;
import cn.lili.modules.store.service.StoreDetailService;
@@ -87,6 +91,7 @@
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
@@ -199,7 +204,18 @@
    @Resource
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private CouponService couponService;
    @Autowired
    private CouponVirtualService couponVirtualService;
    @Autowired
    private RedissonClient redissonClient;
    private final static  String LOCK_ORDER_NO_MQ="lock_order_no_mq:";
    private final static  String LOCK_EDIT_ORDER_ADDRESS="lock_edit_order_address:";
    private final static  String LOCK_COUPON_CARD="lock_coupon_card:";
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void intoDB(TradeDTO tradeDTO) {
@@ -488,6 +504,18 @@
        //查询订单和自订单,然后写入vo返回
        return new OrderDetailVO(order, orderItems, orderLogs, receipt);
    }
    @Override
    public OrderDetailVO queryEditAddressDetail(String orderSn) {
        Order order = this.getBySn(orderSn);
        if (order == null) {
            throw new ServiceException(ResultCode.ORDER_NOT_EXIST);
        }
        //查询订单项信息
        List<OrderItem> orderItems = orderItemService.getByOrderSn(orderSn);
        //查询订单和自订单,然后写入vo返回
        return new OrderDetailVO(order, orderItems, null, null);
    }
    @Override
    @Transactional
@@ -666,7 +694,40 @@
        return order;
    }
    @Override
    @SystemLogPoint(description = "修改订单", customerLog = "'订单[' + #orderSn + ']收货信息修改,修改为'+#memberAddressDTO.consigneeDetail")
    @Transactional(rollbackFor = Exception.class)
    public Order updateAddressConsignee(String orderSn, MemberAddressDTO memberAddressDTO) {
        Order order = this.getBySn(orderSn);
        if (order == null) {
            throw new ServiceException(ResultCode.ORDER_NOT_EXIST);
        }
        //限制30秒只能请求一次避免出现重新提交问题
        Boolean b = redisTemplate.opsForValue().setIfAbsent(LOCK_EDIT_ORDER_ADDRESS + orderSn, orderSn,30, TimeUnit.SECONDS);
        if ( Boolean.FALSE.equals(b)){
            throw new ServiceException("请在30秒后重试");
        }
        String modifyAddressFlag = order.getModifyAddressFlag();
        if (StringUtils.isNotBlank(modifyAddressFlag)) {
            throw new ServiceException("当前订单已经被领取");
        }
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("id", order.getId());
        // 使用 last 方法拼接 FOR UPDATE 语句
        wrapper.last("FOR UPDATE");
        baseMapper.selectOne(wrapper);
        //要记录之前的收货地址,所以需要以代码方式进行调用 不采用注解
        String message = "订单[" + orderSn + "]收货信息修改,由[" +order.getConsigneeAddressPath()+  order.getConsigneeDetail() + "]修改为[" + memberAddressDTO.getConsigneeAddressPath()+ memberAddressDTO.getConsigneeDetail() + "]";
        //记录订单操作日志
        BeanUtil.copyProperties(memberAddressDTO, order);
        order.setModifyAddressFlag(ModifyAddressEnums.USED.name());
        this.updateById(order);
        OrderLog orderLog = new OrderLog(orderSn, UserContext.getCurrentUser().getId(), UserContext.getCurrentUser().getRole().getRole(),
                UserContext.getCurrentUser().getUsername(), message);
        orderLogService.save(orderLog);
        return order;
    }
    @Override
    @OrderLogPoint(description = "'订单['+#orderSn+']发货,发货单号['+#logisticsNo+']'", orderSn = "#orderSn")
    @Transactional(rollbackFor = Exception.class)
@@ -740,17 +801,76 @@
    @OrderLogPoint(description = "'订单['+#orderSn+']核销,核销码['+#verificationCode+']'", orderSn = "#orderSn")
    @Transactional(rollbackFor = Exception.class)
    public Order take(String orderSn, String verificationCode) {
        Order order;
        RLock lock = redissonClient.getLock(LOCK_COUPON_CARD + orderSn);
        try {
             lock.lock();
            //获取订单信息
            order = this.getBySn(orderSn);
            //订单幂等问题
            if (OrderStatusEnum.COMPLETED.name().equals(order.getOrderStatus())) {
                throw new ServiceException("当前订单已完成无法再次核验");
            }
        //获取订单信息
        Order order = this.getBySn(orderSn);
        //检测虚拟订单信息
        checkVerificationOrder(order, verificationCode);
        order.setOrderStatus(OrderStatusEnum.COMPLETED.name());
        //订单完成
        this.complete(orderSn);
            //检测虚拟订单信息
            checkVerificationOrder(order, verificationCode);
            order.setOrderStatus(OrderStatusEnum.COMPLETED.name());
            //订单完成
            //获取所有的订单子项用于生成优惠卷订单信息
            List<OrderItem> orderItems = orderItemService.getByOrderSn(orderSn);
            List<CouponVirtual> couponVirtuals = new  ArrayList<>();
            for (OrderItem orderItem : orderItems) {
                String couponId = orderItem.getCouponId();
                if (StringUtils.isBlank(couponId)) {
                    continue;
                }
                String storeId = order.getStoreId();
                Coupon one = couponService.getOne(Wrappers.<Coupon>lambdaQuery().eq(Coupon::getStoreId, storeId).eq(Coupon::getId, couponId));
                if (one == null) {
                    log.error("当前订单订单号为:{}不存在中的优惠卷不存在----------------------->{}",order.getId(),orderItem.getOrderSn());
                }else {
                    Integer num = orderItem.getNum();
                    //当购买数量不为空的时候进行
                    if (num != null) {
                        for (int i = 1; i <= num; i++) {
                            CouponVirtual couponVirtual = getCouponVirtual(orderItem);
                            couponVirtual.setCouponNo(String.format("%08d", i));
                            couponVirtuals.add(couponVirtual);
                        }
                    }
                }
            }
            if (!couponVirtuals.isEmpty()) {
                order.setCouponFlag(CouPonFlagEnum.COUPON.name());
                couponVirtualService.saveBatch(couponVirtuals);
                //更新状态用于后续小程序判断弹出卷列表
                this.updateById(order);
            }
            this.complete(orderSn);
        } finally {
            assert lock != null;
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return order;
    }
    private static CouponVirtual getCouponVirtual(OrderItem orderItem) {
        CouponVirtual couponVirtual = new CouponVirtual();
        couponVirtual.setOrderId(orderItem.getOrderSn());
        couponVirtual.setCouponId(orderItem.getCouponId());
        couponVirtual.setCouponName(orderItem.getCouponName());
        couponVirtual.setGoodsId(orderItem.getGoodsId());
        couponVirtual.setSkuId(orderItem.getSkuId());
        couponVirtual.setItemOrderId(orderItem.getId());
        couponVirtual.setSkuName(orderItem.getGoodsName());
        couponVirtual.setName(orderItem.getCouponName());
        couponVirtual.setShareStatus(ShareStatusEnum.NOT_SHARE.name());
        couponVirtual.setClaimStatus(ClaimStatusEnum.NOT_CLAIM.name());
        return couponVirtual;
    }
    @Override
    public Order take(String verificationCode) {
        String storeId = OperationalJudgment.judgment(UserContext.getCurrentUser()).getStoreId();
framework/src/main/java/cn/lili/modules/promotion/entity/dos/Coupon.java
@@ -82,6 +82,9 @@
    @ApiModelProperty(value = "有效期")
    private Integer effectiveDays;
    @ApiModelProperty(value = "商品使用限制")
    private Integer goodsUseLimitNum;
    public Coupon(CouponVO couponVO) {
        BeanUtils.copyProperties(couponVO, this);
    }
framework/src/main/java/cn/lili/modules/promotion/entity/dos/MemberCoupon.java
@@ -107,6 +107,8 @@
    @ApiModelProperty(value = "会员优惠券状态")
    private String memberCouponStatus;
    @ApiModelProperty(value = "商品使用限制")
    private Integer goodsUseLimitNum;
    public MemberCoupon() {
    }
@@ -120,6 +122,7 @@
        setScopeType(coupon.getScopeType());
        setScopeId(coupon.getScopeId());
        setCouponType(coupon.getCouponType());
        setGoodsUseLimitNum(coupon.getGoodsUseLimitNum());
        setStartTime(coupon.getStartTime() == null ? new Date() : coupon.getStartTime());
        setGetType(coupon.getGetType());
framework/src/main/resources/mapper/lmk/CouponVirtualMapper.xml
New file
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.lili.modules.lmk.mapper.CouponVirtualMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="cn.lili.modules.lmk.domain.vo.CouponVirtualVO">
        <result column="order_id" property="orderId" />
        <result column="goods_id" property="goodsId" />
        <result column="sku_name" property="skuName" />
        <result column="coupon_id" property="couponId" />
        <result column="coupon_no" property="couponNo" />
        <result column="coupon_name" property="couponName" />
        <result column="name" property="name" />
        <result column="share_status" property="shareStatus" />
        <result column="claim_status" property="claimStatus" />
        <result column="update_time" property="updateTime" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="delete_flag" property="deleteFlag" />
        <result column="original" property="original" />
        <result column="user_nickname" property="userNickName" />
    </resultMap>
    <select id="getById" resultMap="BaseResultMap">
        SELECT
            LCV.order_id,
            LCV.goods_id,
            LCV.sku_name,
            LCV.coupon_id,
            LCV.coupon_no,
            LCV.coupon_name,
            LCV.name,
            LCV.share_status,
            LCV.claim_status,
            LCV.update_time,
            LCV.create_by,
            LCV.create_time,
            LCV.update_by,
            LCV.delete_flag,
            LCV.id
        FROM
            lmk_coupon_virtual LCV
        WHERE
            LCV.id = #{id} AND LCV.deleted = 0
    </select>
    <select id="getPage" resultMap="BaseResultMap">
        SELECT
            LCV.order_id,
            LCV.goods_id,
            LCV.sku_name,
            LCV.coupon_id,
            LCV.coupon_no,
            LCV.coupon_name,
            LCV.name,
            LCV.share_status,
            LCV.claim_status,
            LCV.update_time,
            LCV.create_by,
            LCV.create_time,
            LCV.update_by,
            LCV.delete_flag,
            LCV.user_nickname,
            LCV.id,
            LGS.original
        FROM
        lmk_coupon_virtual LCV JOIN li_goods_sku LGS ON LGS.id = LCV.sku_id
        WHERE
        LCV.delete_flag = 0 AND LGS.delete_flag = 0
        <if test="query.orderId != null and query.orderId !=''">
           AND LCV.order_id = #{query.orderId}
        </if>
        <if test="query.userNickName != null and query.userNickName !=''">
            AND LCV.user_nickname like CONCAT('%', #{query.userNickName}, '%')
        </if>
        <if test="query.skuName != null and query.skuName !=''">
            AND LCV.sku_name like CONCAT('%', #{query.skuName}, '%')
        </if>
    </select>
</mapper>
framework/src/main/resources/mapper/lmk/VideoMapper.xml
@@ -300,7 +300,7 @@
            LV.delete_flag = 0
          AND LV.STATUS = '1'
          AND LV.video_type = #{query.videoType}
          AND lm.id IS NOT NULL
          AND LM.id IS NOT NULL
        UNION ALL
        SELECT
            LV.author_id,
@@ -334,7 +334,7 @@
            LV.delete_flag = 0
          AND LV.STATUS = '1'
          AND LV.video_type = #{query.videoType}
          AND lm.id IS NOT NULL
          AND LM.id IS NOT NULL
        ORDER BY
            create_time DESC
    </select>
manager-api/src/main/java/cn/lili/controller/lmk/CardPackController.java
New file
@@ -0,0 +1,36 @@
package cn.lili.controller.lmk;
import cn.lili.base.Result;
import cn.lili.modules.lmk.domain.query.CouponVirtualQuery;
import cn.lili.modules.lmk.service.CouponVirtualService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * lmk-shop-java
 *
 * @author : zxl
 * @date : 2025-09-12 18:11
 **/
@Validated
@RequiredArgsConstructor
@Api(value = "礼品卡包", tags = "礼品卡包")
@RestController
@RequestMapping("/manager/cardPack")
public class CardPackController {
    private final CouponVirtualService couponVirtualService;
    @GetMapping
    @ApiOperation(value = "列表", notes = "列表")
    public Result getPage(CouponVirtualQuery query){
        //更具订单id查询
        return couponVirtualService.page(query);
    }
}
manager-api/src/main/java/cn/lili/controller/order/AfterSaleManagerController.java
@@ -2,13 +2,16 @@
import cn.lili.common.aop.annotation.PreventDuplicateSubmissions;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleSearchParams;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleVO;
import cn.lili.modules.order.aftersale.mapper.AfterSaleMapper;
import cn.lili.modules.order.aftersale.service.AfterSaleService;
import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO;
import cn.lili.modules.system.entity.vo.Traces;
import cn.lili.utils.COSUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@@ -18,6 +21,7 @@
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
@@ -36,11 +40,19 @@
     */
    @Autowired
    private AfterSaleService afterSaleService;
    @Autowired
    private COSUtil cosUtil;
    @ApiOperation(value = "分页获取售后服务")
    @GetMapping(value = "/page")
    public ResultMessage<IPage<AfterSaleVO>> getByPage(AfterSaleSearchParams searchParams) {
        return ResultUtil.data(afterSaleService.getAfterSalePages(searchParams));
        IPage<AfterSaleVO> afterSalePages = afterSaleService.getAfterSalePages(searchParams);
        for (AfterSaleVO record : afterSalePages.getRecords()) {
            if (StringUtils.isNotBlank(record.getGoodsImage())&&!record.getGoodsImage().contains("http")) {
                record.setGoodsImage(cosUtil.getPreviewUrl(record.getGoodsImage()));
            }
        }
        return ResultUtil.data(afterSalePages);
    }
    @ApiOperation(value = "获取导出售后服务列表列表")
@@ -53,7 +65,23 @@
    @ApiImplicitParam(name = "sn", value = "售后单号", required = true, paramType = "path")
    @GetMapping(value = "/get/{sn}")
    public ResultMessage<AfterSaleVO> get(@NotNull(message = "售后单号") @PathVariable("sn") String sn) {
        return ResultUtil.data(afterSaleService.getAfterSale(sn));
        AfterSaleVO afterSale = afterSaleService.getAfterSale(sn);
        String afterSaleImage = afterSale.getAfterSaleImage();
        if (StringUtils.isNotBlank(afterSaleImage)) {
            String[] split = afterSaleImage.split(",");
            List<String> asleImages = new ArrayList<>(split.length);
            for (String s : split) {
                if (!s.contains("http")){
                    asleImages.add(cosUtil.getPreviewUrl(s));
                }
            }
            String join = String.join(",", asleImages);
            afterSale.setAfterSaleImage(join);
        }
        if (StringUtils.isNotBlank(afterSale.getGoodsImage())&&!afterSale.getGoodsImage().contains("http")) {
            afterSale.setGoodsImage(cosUtil.getPreviewUrl(afterSale.getGoodsImage()));
        }
        return ResultUtil.data(afterSale);
    }
    @ApiOperation(value = "查看买家退货物流踪迹")
manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java
@@ -4,6 +4,7 @@
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.PageVO;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.order.order.entity.dos.OrderComplaint;
@@ -15,6 +16,7 @@
import cn.lili.modules.order.order.entity.vo.OrderComplaintVO;
import cn.lili.modules.order.order.service.OrderComplaintCommunicationService;
import cn.lili.modules.order.order.service.OrderComplaintService;
import cn.lili.utils.COSUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@@ -23,6 +25,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
@@ -48,11 +52,26 @@
    @Autowired
    private OrderComplaintCommunicationService orderComplaintCommunicationService;
    @Autowired
    private COSUtil cosUtil;
    @ApiOperation(value = "通过id获取")
    @ApiImplicitParam(name = "id", value = "投诉单ID", required = true, paramType = "path")
    @GetMapping(value = "/{id}")
    public ResultMessage<OrderComplaintVO> get(@PathVariable String id) {
        return ResultUtil.data(orderComplaintService.getOrderComplainById(id));
        OrderComplaintVO orderComplainById = orderComplaintService.getOrderComplainById(id);
        String goodsImage = orderComplainById.getGoodsImage();
        if (StringUtils.isNotBlank(goodsImage)&&!goodsImage.contains("http")) {
            orderComplainById.setGoodsImage(cosUtil.getPreviewUrl(goodsImage));
        }
        String[] orderComplaintImages = orderComplainById.getOrderComplaintImages();
        List<String> orderComplaintImagesList = Arrays.asList(orderComplaintImages);
        for (String orderComplaintImage : orderComplaintImages) {
            if (StringUtils.isNotBlank(orderComplaintImage)&&!orderComplaintImage.contains("http")) {
                orderComplaintImagesList.add(cosUtil.getPreviewUrl(orderComplaintImage));
            }
        }
        orderComplainById.setOrderComplaintImages(orderComplaintImagesList.toArray(new String[0]));
        return ResultUtil.data(orderComplainById);
    }
    @ApiOperation(value = "分页获取")
seller-api/src/main/java/cn/lili/controller/goods/GoodsStoreController.java
@@ -155,6 +155,7 @@
    @ApiOperation(value = "新增商品")
    @PostMapping(value = "/create", consumes = "application/json", produces = "application/json")
    public ResultMessage<GoodsOperationDTO> save(@Valid @RequestBody GoodsOperationDTO goodsOperationDTO) {
        System.err.println(JSONObject.toJSONString(goodsOperationDTO));
        goodsService.addGoods(goodsOperationDTO);
        return ResultUtil.success();
seller-api/src/main/java/cn/lili/controller/order/AfterSaleStoreController.java
@@ -22,6 +22,7 @@
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -46,6 +47,21 @@
    @GetMapping(value = "/{sn}")
    public ResultMessage<AfterSaleVO> get(@PathVariable String sn) {
        AfterSaleVO afterSale = OperationalJudgment.judgment(afterSaleService.getAfterSale(sn));
        String afterSaleImage = afterSale.getAfterSaleImage();
        if (StringUtils.isNotBlank(afterSaleImage)) {
            String[] split = afterSaleImage.split(",");
            List<String> asleImages = new ArrayList<>(split.length);
            for (String s : split) {
                if (!s.contains("http")){
                    asleImages.add(cosUtil.getPreviewUrl(s));
                }
            }
            String join = String.join(",", asleImages);
            afterSale.setAfterSaleImage(join);
        }
        if (StringUtils.isNotBlank(afterSale.getGoodsImage())&&!afterSale.getGoodsImage().contains("http")) {
            afterSale.setGoodsImage(cosUtil.getPreviewUrl(afterSale.getGoodsImage()));
        }
        return ResultUtil.data(afterSale);
    }