peng
昨天 36d03339f1eb3ac030e5569abc133f9942af0ac8
店铺扫码抽奖功能
9个文件已修改
2个文件已添加
280 ■■■■■ 已修改文件
buyer-api/src/main/java/cn/lili/controller/lmk/StorePrizeClaimController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/entity/PrizeClaimRecord.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/vo/StorePrizeVO.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/mapper/ScanPrizeMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/PrizeClaimRecordService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/PrizeService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/ScanPrizeService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeClaimRecordServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/impl/ScanPrizeServiceImpl.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/resources/mapper/lmk/ScanPrizeMapper.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
buyer-api/src/main/java/cn/lili/controller/lmk/StorePrizeClaimController.java
New file
@@ -0,0 +1,35 @@
package cn.lili.controller.lmk;
import cn.lili.base.Result;
import cn.lili.modules.lmk.service.PrizeClaimRecordService;
import cn.lili.modules.lmk.service.ScanPrizeService;
import cn.lili.modules.lmk.service.StoreCouponClaimRecordService;
import cn.lili.modules.lmk.service.StoreCouponService;
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.*;
@Validated
@RequiredArgsConstructor
@Api(value = "商家端扫码领取抽奖机会接口", tags = "商家端扫码领取抽奖机会接口")
@RestController
@RequestMapping("/buyer/lmk/store/prize")
public class StorePrizeClaimController {
    private final PrizeClaimRecordService prizeClaimRecordService;
    private final ScanPrizeService scanPrizeService;
    @PostMapping("/{id}")
    @ApiOperation(value = "领取抽奖机会", notes = "领取抽奖机会")
    public Result claimPrize(@PathVariable String id){
        return scanPrizeService.claimPrize(id);
    }
    @GetMapping("/{id}")
    @ApiOperation(value = "查看抽奖机会信息", notes = "查看抽奖机会信息")
    public Result getStorePrize(@PathVariable String id){
        return scanPrizeService.getStorePrize(id);
    }
}
framework/src/main/java/cn/lili/modules/lmk/domain/entity/PrizeClaimRecord.java
@@ -1,6 +1,7 @@
package cn.lili.modules.lmk.domain.entity;
import cn.lili.modules.lmk.enums.general.MaterialStatusEnum;
import cn.lili.modules.order.order.entity.enums.ClaimStatusEnum;
import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -52,7 +53,9 @@
    private String nickName;
    @TableField("claim_status")
    /** 领取状态 */
    /** 领取状态
     * @see ClaimStatusEnum
     * */
    private String claimStatus;
    @TableField("material")
framework/src/main/java/cn/lili/modules/lmk/domain/vo/StorePrizeVO.java
New file
@@ -0,0 +1,55 @@
package cn.lili.modules.lmk.domain.vo;
import cn.lili.base.AbsVo;
import cn.lili.modules.lmk.domain.entity.ActionRecord;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import org.springframework.lang.NonNull;
import java.util.Date;
/**
 * 用户行为记录展示
 *
 * @author peng
 * @since 2025-09-08
 */
@Data
@ApiModel(value = "用户行为记录响应数据", description = "用户行为记录响应数据")
public class StorePrizeVO {
    @ApiModelProperty("id")
    private String id;
    @ApiModelProperty("编号")
    private String no;
    @ApiModelProperty("门店id")
    private String storeId;
    @ApiModelProperty("门店名称")
    private String storeName;
    @ApiModelProperty("活动名称")
    private String activityName;
    @ApiModelProperty("活动描述")
    private String activityDes;
    @ApiModelProperty("开始时间")
    private Date beginTime;
    @ApiModelProperty("结束时间")
    private Date endTime;
    @ApiModelProperty("活动封面")
    private String activityCover;
    @ApiModelProperty("启用状态")
    private String enableStatus;
    @ApiModelProperty("弹窗内容")
    private Boolean popup;
}
framework/src/main/java/cn/lili/modules/lmk/mapper/ScanPrizeMapper.java
@@ -1,6 +1,8 @@
package cn.lili.modules.lmk.mapper;
import cn.lili.base.Result;
import cn.lili.modules.lmk.domain.entity.ScanPrize;
import cn.lili.modules.lmk.domain.vo.StorePrizeVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.lili.modules.lmk.domain.vo.ScanPrizeVO;
@@ -31,4 +33,6 @@
    */
    IPage getPage(IPage page, @Param("query") ScanPrizeQuery query);
    StorePrizeVO getStorePrize(@Param("id") String id);
}
framework/src/main/java/cn/lili/modules/lmk/service/PrizeClaimRecordService.java
@@ -5,6 +5,8 @@
import cn.lili.base.Result;
import cn.lili.modules.lmk.domain.form.PrizeClaimRecordForm;
import cn.lili.modules.lmk.domain.query.PrizeClaimRecordQuery;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
framework/src/main/java/cn/lili/modules/lmk/service/PrizeService.java
@@ -18,4 +18,5 @@
    Result addPrizeNum(AddPrizeNumForm addPrizeNumForm);
}
framework/src/main/java/cn/lili/modules/lmk/service/ScanPrizeService.java
@@ -66,4 +66,9 @@
    Result changeStatus(String id);
    Result generateStorePrize(String id);
    Result claimPrize(String id);
    Result getStorePrize(String id);
}
framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeClaimRecordServiceImpl.java
@@ -1,18 +1,36 @@
package cn.lili.modules.lmk.service.impl;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.modules.lmk.domain.entity.*;
import cn.lili.modules.lmk.domain.form.AddPrizeNumForm;
import cn.lili.modules.lmk.enums.general.PrizeUserActionEnum;
import cn.lili.modules.lmk.enums.general.StoreCouponStausEnum;
import cn.lili.modules.lmk.enums.general.StoreScanPrizeStausEnum;
import cn.lili.modules.lmk.service.PrizeService;
import cn.lili.modules.lmk.service.ScanPrizeService;
import cn.lili.modules.order.order.entity.enums.ClaimStatusEnum;
import cn.lili.modules.promotion.entity.dos.MemberCoupon;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import cn.lili.modules.lmk.domain.entity.PrizeClaimRecord;
import cn.lili.modules.lmk.mapper.PrizeClaimRecordMapper;
import cn.lili.modules.lmk.service.PrizeClaimRecordService;
import cn.lili.base.Result;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.lili.modules.lmk.domain.form.PrizeClaimRecordForm;
import cn.lili.modules.lmk.domain.vo.PrizeClaimRecordVO;
import cn.lili.modules.lmk.domain.query.PrizeClaimRecordQuery;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import cn.lili.utils.PageUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import java.util.List;
framework/src/main/java/cn/lili/modules/lmk/service/impl/ScanPrizeServiceImpl.java
@@ -1,13 +1,20 @@
package cn.lili.modules.lmk.service.impl;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.modules.lmk.domain.entity.PrizeClaimRecord;
import cn.lili.modules.lmk.domain.entity.StoreCoupon;
import cn.lili.modules.lmk.domain.entity.StoreCouponSingle;
import cn.lili.modules.lmk.domain.form.AddPrizeNumForm;
import cn.lili.modules.lmk.domain.vo.StorePrizeVO;
import cn.lili.modules.lmk.enums.general.*;
import cn.lili.modules.lmk.service.PrizeClaimRecordService;
import cn.lili.modules.lmk.service.PrizeService;
import cn.lili.modules.order.order.entity.enums.ClaimStatusEnum;
import cn.lili.utils.COSUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import cn.lili.modules.lmk.domain.entity.ScanPrize;
import cn.lili.modules.lmk.mapper.ScanPrizeMapper;
@@ -49,7 +56,9 @@
    private final RedissonClient redissonClient;
    private static final String STORE_PRIZE_GENERATE = "store_prize_generate:";
    private final PrizeClaimRecordService prizeClaimRecordService;
    private static final String STORE_PRIZE_CLAIM = "store_prize_claim:";
    private final PrizeService prizeService;
    private final COSUtil cosUtil;
    /**
     * 添加
     * @param form
@@ -164,7 +173,7 @@
    }
    @Override
    @Transactional
    @Transactional(rollbackFor = Exception.class)
    public Result generateStorePrize(String id) {
        RLock redissonLock = redissonClient.getLock(STORE_PRIZE_GENERATE + id);
@@ -224,4 +233,97 @@
        prizeClaimRecord.setMaterial(MaterialStatusEnum.NOT_GENERATE.name());
        return prizeClaimRecord;
    }
    @Override
    public Result claimPrize(String id) {
        AuthUser currentUser = UserContext.getCurrentUser();
        if (currentUser == null) {
            throw new ServiceException("当前用户没有登录无法领取");
        }
        String userId = currentUser.getId();
        String nickName = currentUser.getNickName();
        //锁住礼品码id
        RLock redissonLock = redissonClient.getLock(STORE_PRIZE_CLAIM + id);
        try {
            redissonLock.lock();
            LambdaQueryWrapper<PrizeClaimRecord> forUpdate = Wrappers.<PrizeClaimRecord>lambdaQuery()
                    .eq(PrizeClaimRecord::getId, id).last("FOR UPDATE");
            PrizeClaimRecord prizeClaimRecord = prizeClaimRecordService.getOne(forUpdate);
            if (prizeClaimRecord == null) {
                throw new ServiceException("当前领取抽奖机会二维码不存在");
            }
            if (!ClaimStatusEnum.NOT_CLAIM.name().equals(prizeClaimRecord.getClaimStatus())) {
                throw new ServiceException("当前领取抽奖机会状态异常");
            }
            //更新被领取的记录
            prizeClaimRecord.setClaimStatus(ClaimStatusEnum.CLAIM.name());
            prizeClaimRecord.setUserId(userId);
            prizeClaimRecord.setNickName(nickName);
            LambdaQueryWrapper<ScanPrize> storeCoupQuery = Wrappers.<ScanPrize>lambdaQuery()
                    .eq(ScanPrize::getId, prizeClaimRecord.getStorePrizeId()).last("FOR UPDATE");
            ScanPrize scanPrize = this.getOne(storeCoupQuery);
            AddPrizeNumForm addPrizeNumForm = new AddPrizeNumForm();
            addPrizeNumForm.setUserId(userId);
            addPrizeNumForm.setPrizeActivityId(prizeClaimRecord.getPrizeActivityId());
            addPrizeNumForm.setAddType(PrizeUserActionEnum.USER_SCAN_STORE.name());
            prizeService.addPrizeNum(addPrizeNumForm);
            if (scanPrize == null) {
                throw new ServiceException("当前店铺抽奖卷不存在");
            }
            if (!StoreScanPrizeStausEnum.ENABLE.name().equals(scanPrize.getStatus())) {
                throw new ServiceException("当前店铺抽奖卷状态异常");
            }
            //领取对应的优惠卷写入记录
            prizeClaimRecordService.updateById(prizeClaimRecord);
            LambdaUpdateWrapper<ScanPrize> updateStoreCoupon = Wrappers.<ScanPrize>lambdaUpdate()
                    .eq(ScanPrize::getId, scanPrize.getId())
                    .set(ScanPrize::getClaimNum, scanPrize.getClaimNum() + 1)
                    .ge(ScanPrize::getGenerateNum, scanPrize.getClaimNum() + 1);
            boolean update = this.update(updateStoreCoupon);
            if (!update) {
                throw new ServiceException("领取失败");
            }
            return Result.ok().data(0);
        } finally {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    if (redissonLock.isHeldByCurrentThread()) {
                        redissonLock.unlock();
                    }
                }
                @Override
                public void afterCompletion(int status) {
                    // 确保即使在事务回滚的情况下也能释放锁
                    if (redissonLock.isHeldByCurrentThread()) {
                        redissonLock.unlock();
                    }
                }
            });
        }
    }
    @Override
    public Result getStorePrize(String id) {
        StorePrizeVO storePrize = baseMapper.getStorePrize(id);
        if (storePrize == null) {
            throw new ServiceException("当前活动不存在");
        }
        Boolean popup = storePrize.getPopup();
        if (!Boolean.TRUE.equals(popup)) {
            throw new ServiceException("当前活动没有开启");
        }
        if (!PrizeActivityStatusEnum.ON.name().equals(storePrize.getEnableStatus())) {
            throw new ServiceException("当前活动没有开启");
        }
        String activityCover = storePrize.getActivityCover();
        if (StringUtils.isNotBlank(activityCover)&&!activityCover.contains("http")) {
            storePrize.setActivityCover(cosUtil.getPreviewUrl(activityCover));
        }
        return Result.ok().data(storePrize);
    }
}
framework/src/main/resources/mapper/lmk/ScanPrizeMapper.xml
@@ -57,5 +57,37 @@
        WHERE
            LSP.delete_flag = 0
    </select>
    <resultMap id="getStorePrizeMap" type="cn.lili.modules.lmk.domain.vo.StorePrizeVO">
        <id column="id" property="id"/>
        <result property="no" column="no"/>
        <result property="storeId" column="store_id"/>
        <result property="storeName" column="store_name"/>
        <result property="activityName" column="activity_name"/>
        <result property="activityDes" column="activity_des"/>
        <result property="beginTime" column="begin_time"/>
        <result property="activityCover" column="activity_cover"/>
        <result property="enableStatus" column="enable_status"/>
        <result property="popup" column="popup"/>
    </resultMap>
    <select id="getStorePrize" resultMap="getStorePrizeMap">
        SELECT
            pcr.id,
            pcr.no,
            pcr.store_id,
            pcr.store_name,
            lpa.activity_name,
            lpa.activity_des,
            lpa.begin_time,
            lpa.end_time,
            lpa.activity_cover,
            lpa.enable_status,
            lpa.popup
        FROM
            lmk_prize_claim_record pcr
                JOIN lmk_prize_activity lpa ON pcr.prize_activity_id = lpa.id
                AND pcr.delete_flag = 0 AND lpa.delete_flag = 0
        WHERE
            pcr.id = #{id}
    </select>
</mapper>
manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java
@@ -25,6 +25,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -64,12 +65,24 @@
            orderComplainById.setGoodsImage(cosUtil.getPreviewUrl(goodsImage));
        }
        String[] orderComplaintImages = orderComplainById.getOrderComplaintImages();
        List<String> orderComplaintImagesList = Arrays.asList(orderComplaintImages);
        List<String> orderComplaintImagesList = new ArrayList<>();
        for (String orderComplaintImage : orderComplaintImages) {
            if (StringUtils.isNotBlank(orderComplaintImage)&&!orderComplaintImage.contains("http")) {
                orderComplaintImagesList.add(cosUtil.getPreviewUrl(orderComplaintImage));
            }
        }
        String images = orderComplainById.getImages();
        String[] split = images.split(",");
        ArrayList<String> imgs = new ArrayList<>();
        for (String s : split) {
            if (StringUtils.isNotBlank(s)) {
                if (StringUtils.isNotBlank(s)&&!s.contains("http")) {
                    imgs.add(cosUtil.getPreviewUrl(s));
                }
            }
        }
        String join = String.join(",", imgs);
        orderComplainById.setImages(join);
        orderComplainById.setOrderComplaintImages(orderComplaintImagesList.toArray(new String[0]));
        return ResultUtil.data(orderComplainById);
    }