From 36d03339f1eb3ac030e5569abc133f9942af0ac8 Mon Sep 17 00:00:00 2001
From: peng <peng.com>
Date: 星期二, 30 九月 2025 17:08:20 +0800
Subject: [PATCH] 店铺扫码抽奖功能

---
 framework/src/main/java/cn/lili/modules/lmk/mapper/ScanPrizeMapper.java                   |    4 
 framework/src/main/java/cn/lili/modules/lmk/domain/entity/PrizeClaimRecord.java           |    5 
 buyer-api/src/main/java/cn/lili/controller/lmk/StorePrizeClaimController.java             |   35 +++++++
 framework/src/main/java/cn/lili/modules/lmk/domain/vo/StorePrizeVO.java                   |   55 +++++++++++
 framework/src/main/java/cn/lili/modules/lmk/service/PrizeClaimRecordService.java          |    2 
 framework/src/main/java/cn/lili/modules/lmk/service/ScanPrizeService.java                 |    5 +
 framework/src/main/java/cn/lili/modules/lmk/service/PrizeService.java                     |    1 
 framework/src/main/java/cn/lili/modules/lmk/service/impl/ScanPrizeServiceImpl.java        |  106 ++++++++++++++++++++
 manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java   |   15 ++
 framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeClaimRecordServiceImpl.java |   20 +++
 framework/src/main/resources/mapper/lmk/ScanPrizeMapper.xml                               |   32 ++++++
 11 files changed, 275 insertions(+), 5 deletions(-)

diff --git a/buyer-api/src/main/java/cn/lili/controller/lmk/StorePrizeClaimController.java b/buyer-api/src/main/java/cn/lili/controller/lmk/StorePrizeClaimController.java
new file mode 100644
index 0000000..546fce1
--- /dev/null
+++ b/buyer-api/src/main/java/cn/lili/controller/lmk/StorePrizeClaimController.java
@@ -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);
+    }
+}
diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/entity/PrizeClaimRecord.java b/framework/src/main/java/cn/lili/modules/lmk/domain/entity/PrizeClaimRecord.java
index 83b2e1e..b274f1a 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/domain/entity/PrizeClaimRecord.java
+++ b/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")
diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/StorePrizeVO.java b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/StorePrizeVO.java
new file mode 100644
index 0000000..580ebfe
--- /dev/null
+++ b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/StorePrizeVO.java
@@ -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;
+}
diff --git a/framework/src/main/java/cn/lili/modules/lmk/mapper/ScanPrizeMapper.java b/framework/src/main/java/cn/lili/modules/lmk/mapper/ScanPrizeMapper.java
index e4e4169..c21d00f 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/mapper/ScanPrizeMapper.java
+++ b/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);
+
 }
diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/PrizeClaimRecordService.java b/framework/src/main/java/cn/lili/modules/lmk/service/PrizeClaimRecordService.java
index 179988f..e90fd83 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/service/PrizeClaimRecordService.java
+++ b/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;
 
 /**
diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/PrizeService.java b/framework/src/main/java/cn/lili/modules/lmk/service/PrizeService.java
index a67ac86..b4e27da 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/service/PrizeService.java
+++ b/framework/src/main/java/cn/lili/modules/lmk/service/PrizeService.java
@@ -18,4 +18,5 @@
 
     Result addPrizeNum(AddPrizeNumForm addPrizeNumForm);
 
+
 }
diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/ScanPrizeService.java b/framework/src/main/java/cn/lili/modules/lmk/service/ScanPrizeService.java
index 925e543..926b9bf 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/service/ScanPrizeService.java
+++ b/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);
+
 }
diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeClaimRecordServiceImpl.java b/framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeClaimRecordServiceImpl.java
index eb80535..45bf2c7 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeClaimRecordServiceImpl.java
+++ b/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;
diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/impl/ScanPrizeServiceImpl.java b/framework/src/main/java/cn/lili/modules/lmk/service/impl/ScanPrizeServiceImpl.java
index 0193259..cc7d618 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/service/impl/ScanPrizeServiceImpl.java
+++ b/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();
+
+        //閿佷綇绀煎搧鐮乮d
+        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);
+    }
 }
diff --git a/framework/src/main/resources/mapper/lmk/ScanPrizeMapper.xml b/framework/src/main/resources/mapper/lmk/ScanPrizeMapper.xml
index 1a489b4..cbe91b8 100644
--- a/framework/src/main/resources/mapper/lmk/ScanPrizeMapper.xml
+++ b/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>
diff --git a/manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java b/manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java
index 1610157..a134ef3 100644
--- a/manager-api/src/main/java/cn/lili/controller/order/OrderComplaintManagerController.java
+++ b/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);
     }

--
Gitblit v1.8.0