From bd2f8c89f49f92ef075bf39798a71a0d6c08a64a Mon Sep 17 00:00:00 2001
From: zxl <763096477@qq.com>
Date: 星期日, 28 九月 2025 11:25:45 +0800
Subject: [PATCH] 店铺绑优惠劵领取记录

---
 framework/src/main/java/cn/lili/modules/lmk/service/impl/StoreCouponClaimRecordServiceImpl.java |  299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 298 insertions(+), 1 deletions(-)

diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/impl/StoreCouponClaimRecordServiceImpl.java b/framework/src/main/java/cn/lili/modules/lmk/service/impl/StoreCouponClaimRecordServiceImpl.java
index 375b685..6a960c2 100644
--- a/framework/src/main/java/cn/lili/modules/lmk/service/impl/StoreCouponClaimRecordServiceImpl.java
+++ b/framework/src/main/java/cn/lili/modules/lmk/service/impl/StoreCouponClaimRecordServiceImpl.java
@@ -1,21 +1,58 @@
 package cn.lili.modules.lmk.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
+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.StoreCoupon;
+import cn.lili.modules.lmk.domain.entity.StoreCouponSingle;
+import cn.lili.modules.lmk.enums.general.PrizeStatusEnum;
+import cn.lili.modules.lmk.enums.general.StoreCouponStausEnum;
+import cn.lili.modules.lmk.service.StoreCouponService;
+import cn.lili.modules.lmk.service.StoreCouponSingleService;
+import cn.lili.modules.order.order.entity.dto.CouponExportDetailDTO;
+import cn.lili.modules.order.order.entity.dto.StoreCouponClaimRecordDTO;
+import cn.lili.modules.order.order.entity.enums.ClaimStatusEnum;
+import cn.lili.modules.promotion.entity.dos.MemberCoupon;
+import cn.lili.modules.promotion.entity.dto.search.MemberCouponSearchParams;
+import cn.lili.modules.promotion.entity.vos.MemberCouponVO;
+import cn.lili.modules.promotion.service.MemberCouponService;
+import cn.lili.rocketmq.RocketmqSendCallbackBuilder;
+import com.alibaba.fastjson.JSON;
+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.StoreCouponClaimRecord;
 import cn.lili.modules.lmk.mapper.StoreCouponClaimRecordMapper;
 import cn.lili.modules.lmk.service.StoreCouponClaimRecordService;
 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.StoreCouponClaimRecordForm;
 import cn.lili.modules.lmk.domain.vo.StoreCouponClaimRecordVO;
 import cn.lili.modules.lmk.domain.query.StoreCouponClaimRecordQuery;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+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.annotation.Transactional;
+import org.springframework.transaction.support.TransactionSynchronization;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
 import org.springframework.util.Assert;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.net.URLEncoder;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
@@ -29,7 +66,11 @@
 public class StoreCouponClaimRecordServiceImpl extends ServiceImpl<StoreCouponClaimRecordMapper, StoreCouponClaimRecord> implements StoreCouponClaimRecordService {
 
     private final StoreCouponClaimRecordMapper storeCouponClaimRecordMapper;
-
+    private final RedissonClient redissonClient;
+    private static final String STORE_COUPON_CLAIM = "store_coupon_claim:";
+    private final MemberCouponService memberCouponService;
+    private final StoreCouponService storeCouponService;
+    private final StoreCouponSingleService storeCouponSingleService;
     /**
      * 娣诲姞
      * @param form
@@ -116,4 +157,260 @@
                 .collect(Collectors.toList());
         return Result.ok().data(vos);
     }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Result claimCoupon(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_COUPON_CLAIM + id);
+        try {
+            redissonLock.lock();
+            LambdaQueryWrapper<StoreCouponSingle> forUpdate = Wrappers.<StoreCouponSingle>lambdaQuery()
+                    .eq(StoreCouponSingle::getId, id).last("FOR UPDATE");
+            StoreCouponSingle storeCouponSingle = storeCouponSingleService.getOne(forUpdate);
+            if (storeCouponSingle == null) {
+                throw new ServiceException("褰撳墠绀煎搧鐮佷笉瀛樺湪");
+            }
+            if (!ClaimStatusEnum.NOT_CLAIM.name().equals(storeCouponSingle.getClaimStatus())) {
+                throw new ServiceException("褰撳墠绀煎搧鐮佺姸鎬佸紓甯�");
+            }
+            LambdaQueryWrapper<StoreCouponSingle> claimListQuery = Wrappers.<StoreCouponSingle>lambdaQuery()
+                    .eq(StoreCouponSingle::getClaimUserId, userId)
+                    .eq(StoreCouponSingle::getStoreCoupRef, storeCouponSingle.getStoreCoupRef())
+                    .eq(StoreCouponSingle::getClaimStatus, ClaimStatusEnum.CLAIM.name());
+            List<StoreCouponSingle> claimList = storeCouponSingleService.list(claimListQuery);
+            if (!claimList.isEmpty()) {
+                throw new ServiceException("宸茬粡棰嗗彇杩囪绫诲瀷鐨勭ぜ鍝佺爜鏃犳硶棰嗗彇");
+            }
+            //澶勭悊骞傜瓑闂闄愬埗涓�涓敤鎴峰彧鑳借搴楅摵棰嗗彇涓�绉嶄紭鎯犲嵎
+            LambdaQueryWrapper<StoreCouponClaimRecord> memCoupon = Wrappers.<StoreCouponClaimRecord>lambdaQuery()
+                    .eq(StoreCouponClaimRecord::getUserId, userId)
+                    .eq(StoreCouponClaimRecord::getCouponId, storeCouponSingle.getCouponId());
+            List<StoreCouponClaimRecord> list = this.list(memCoupon);
+            if (!list.isEmpty()){
+                throw new ServiceException("褰撳墠鐢ㄦ埛宸茬粡棰嗗彇杩囦簡鏃犳硶鍐嶆棰嗗彇");
+            }
+            //鏇存柊鍗曞搧琚鍙栫殑璁板綍
+            storeCouponSingle.setClaimStatus(ClaimStatusEnum.CLAIM.name());
+            storeCouponSingle.setClaimUserId(userId);
+            storeCouponSingle.setClaimUserName(nickName);
+            storeCouponSingleService.updateById(storeCouponSingle);
+            //鏍¢獙鏄惁鍦ㄥ崟鍝佸嵎绫婚鍙栬繃
+            LambdaQueryWrapper<StoreCoupon> storeCoupQuery = Wrappers.<StoreCoupon>lambdaQuery()
+                    .eq(StoreCoupon::getId, storeCouponSingle.getStoreCoupRef()).last("FOR UPDATE");
+            StoreCoupon storeCoupon = storeCouponService.getOne(storeCoupQuery);
+            if (storeCoupon == null) {
+                throw new ServiceException("褰撳墠搴楅摵浼樻儬鍗蜂笉瀛樺湪");
+            }
+            if (!StoreCouponStausEnum.ENABLE.name().equals(storeCoupon.getStatus())) {
+                throw new ServiceException("褰撳墠搴楅摵浼樻儬鍗风姸鎬佸紓甯�");
+            }
+            //棰嗗彇瀵瑰簲鐨勪紭鎯犲嵎鍐欏叆璁板綍
+            memberCouponService.receiveCoupon(storeCouponSingle.getCouponId(),userId , nickName);
+            StoreCouponClaimRecord storeCouponClaimRecord = getStoreCouponClaimRecord(storeCouponSingle, userId);
+            this.save(storeCouponClaimRecord);
+            LambdaUpdateWrapper<StoreCoupon> updateStoreCoupon = Wrappers.<StoreCoupon>lambdaUpdate().eq(StoreCoupon::getId, storeCoupon.getId())
+                    .set(StoreCoupon::getCouponClaimNum, storeCoupon.getCouponClaimNum() + 1)
+                    .ge(StoreCoupon::getCouponNum, storeCoupon.getCouponClaimNum() + 1);
+            boolean update = storeCouponService.update(updateStoreCoupon);
+            if (!update) {
+                throw new ServiceException("鏇存柊澶辫触");
+            }
+            //棰嗗彇鎴愬姛杩斿洖浼樻儬鍗穒d鐢ㄤ簬璺宠浆璐墿浣跨敤
+            return Result.ok().data(storeCouponSingle.getCouponId());
+
+        } 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();
+                    }
+                }
+
+            });
+
+        }
+    }
+    private XSSFWorkbook initCouponExportData(List<StoreCouponClaimRecordVO> list) {
+        // 杞崲VO涓篋TO锛堝鏋淒TO涓嶸O瀛楁涓�鑷达紝鍙洿鎺ヤ娇鐢╒O绠�鍖栦唬鐮侊級
+        List<StoreCouponClaimRecordDTO> dtos = new ArrayList<>();
+        for (StoreCouponClaimRecordVO vo : list) {
+            StoreCouponClaimRecordDTO dto = new StoreCouponClaimRecordDTO();
+            BeanUtil.copyProperties(vo, dto);
+            dtos.add(dto);
+        }
+        System.out.println("-----------------------");
+        System.out.println(dtos);
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        Sheet sheet = workbook.createSheet("浼樻儬鍒搁鍙栬褰�");
+        // 鍒涘缓琛ㄥご
+        Row header = sheet.createRow(0);
+        String[] headers = {
+                "浼氬憳鍚嶇О", "浼樻儬鍒稿悕绉�", "鍙戝竷搴楅摵", "闈㈤/鎶樻墸",
+                 "鑾峰彇鏂瑰紡", "浼氬憳浼樻儬鍒哥姸鎬�", "浼樻儬鍒哥被鍨�",
+                 "浣跨敤璧峰鏃堕棿", "鎴鏃堕棿","棰嗗彇鏃堕棿"
+        };
+        for (int i = 0; i < headers.length; i++) {
+            Cell cell = header.createCell(i);
+            cell.setCellValue(headers[i]);
+        }
+
+        // 濉厖鏁版嵁锛堝鍔犵┖鍊煎鐞嗭紝閬垮厤NPE锛�
+        for (int i = 0; i < dtos.size(); i++) {
+            StoreCouponClaimRecordDTO dto = dtos.get(i);
+            Row row = sheet.createRow(i + 1);
+
+            // 1. 浼氬憳鍚嶇О锛堝彲鑳戒负null锛�
+            row.createCell(0).setCellValue(Objects.nonNull(dto.getMemberName()) ? dto.getMemberName() : "");
+
+            // 2. 浼樻儬鍒稿悕绉帮紙鍙兘涓簄ull锛�
+            row.createCell(1).setCellValue(Objects.nonNull(dto.getCouponName()) ? dto.getCouponName() : "");
+
+            // 3. 鍙戝竷搴楅摵锛堝鐞唒latform鐗规畩鍊硷紝榛樿绌哄瓧绗︿覆锛�
+            String storeName = dto.getStoreName();
+            if ("platform".equals(storeName)) {
+                row.createCell(2).setCellValue("骞冲彴");
+            } else {
+                row.createCell(2).setCellValue(Objects.nonNull(storeName) ? storeName : "");
+            }
+
+            // 4. 闈㈤/鎶樻墸锛堜紭鍏堟樉绀烘姌鎵o紝鍏舵鏄剧ず闈㈤锛岄伩鍏嶈鐩栵級
+            Cell amountCell = row.createCell(3);
+            if (Objects.nonNull(dto.getDiscount())) {
+                amountCell.setCellValue(dto.getDiscount() + "鎶�");
+            } else if (Objects.nonNull(dto.getPrice())) {
+                amountCell.setCellValue(dto.getPrice() + "鍏�"); // 缁熶竴鐢�"鍏�"鏇磋鑼�
+            } else {
+                amountCell.setCellValue(""); // 鍧囦负绌烘椂鏄剧ず绌�
+            }
+
+
+            // 4. 鑾峰彇鏂瑰紡锛堣ˉ鍏呴粯璁ゆ湭鐭ョ姸鎬侊紝瑕嗙洊鎵�鏈夋灇涓惧�硷級
+            String getType = dto.getGetType();
+            String getTypeDesc;
+            switch (getType) {
+                case "FREE":
+                    getTypeDesc = "鍏嶈垂鑾峰彇";
+                    break;
+                case "ACTIVITY":
+                    getTypeDesc = "娲诲姩鑾峰彇";
+                    break;
+                case "INSIDE": // 娉ㄦ剰锛氬師浠g爜lime鏄鑹诧紝瀹為檯鏋氫妇搴斾负INSIDE
+                    getTypeDesc = "鍐呰喘";
+                    break;
+                default:
+                    getTypeDesc = "鏈煡";
+            }
+            row.createCell(4).setCellValue(getTypeDesc);
+
+            // 5. 浼氬憳浼樻儬鍒哥姸鎬侊紙瑕嗙洊鎵�鏈夊彲鑳界姸鎬侊級
+            String status = dto.getMemberCouponStatus();
+            String statusDesc;
+            switch (status) {
+                case "NEW":
+                    statusDesc = "宸查鍙�";
+                    break;
+                case "USED":
+                    statusDesc = "宸蹭娇鐢�";
+                    break;
+                case "EXPIRE":
+                    statusDesc = "宸茶繃鏈�";
+                    break;
+                case "CLOSED":
+                    statusDesc = "宸蹭綔搴�";
+                    break;
+                default:
+                    statusDesc = "鏈煡鐘舵��";
+            }
+            row.createCell(5).setCellValue(statusDesc);
+
+            // 6. 浼樻儬鍒哥被鍨嬶紙琛ュ厖榛樿澶勭悊锛�
+            String couponType = dto.getCouponType();
+            String couponTypeDesc;
+            if ("DISCOUNT".equals(couponType)) {
+                couponTypeDesc = "鎵撴姌";
+            } else if ("PRICE".equals(couponType)) {
+                couponTypeDesc = "鍑忓厤鐜伴噾";
+            } else {
+                couponTypeDesc = "鏈煡绫诲瀷";
+            }
+            row.createCell(6).setCellValue(couponTypeDesc);
+
+
+            // 10. 浣跨敤璧峰鏃堕棿锛堝鐞唍ull锛屾牸寮忓寲鏃堕棿锛�
+            Cell startTimeCell = row.createCell(7);
+            if (Objects.nonNull(dto.getStartTime())) {
+                startTimeCell.setCellValue(DateUtil.formatDateTime(dto.getStartTime()));
+            } else {
+                startTimeCell.setCellValue("");
+            }
+
+            // 11. 鎴鏃堕棿锛堝悓涓婏級
+            Cell endTimeCell = row.createCell(8);
+            if (Objects.nonNull(dto.getEndTime())) {
+                endTimeCell.setCellValue(DateUtil.formatDateTime(dto.getEndTime()));
+            } else {
+                endTimeCell.setCellValue("");
+            }
+            Cell claimTimeCell = row.createCell(9);
+            if (Objects.nonNull(dto.getClaimTime())) {
+                endTimeCell.setCellValue(DateUtil.formatDateTime(dto.getClaimTime()));
+            } else {
+                claimTimeCell.setCellValue("");
+            }
+        }
+
+        return workbook;
+    }
+
+    @Override
+    public void queryExportCoupon(HttpServletResponse response, StoreCouponClaimRecordQuery query) {
+        List<StoreCouponClaimRecordVO> exportData = baseMapper.getExportData(query);
+        XSSFWorkbook workbook = initCouponExportData(exportData);
+
+        try {
+            // 璁剧疆鍝嶅簲澶�
+            String fileName = URLEncoder.encode("浼樻儬鍒搁鍙栬褰�", "UTF-8");
+            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
+            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            ServletOutputStream out = response.getOutputStream();
+            workbook.write(out);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                workbook.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private static StoreCouponClaimRecord getStoreCouponClaimRecord(StoreCouponSingle storeCouponSingle, String userId) {
+        StoreCouponClaimRecord storeCouponClaimRecord = new StoreCouponClaimRecord();
+        storeCouponClaimRecord.setCouponId(storeCouponSingle.getCouponId());
+        storeCouponClaimRecord.setCouponName(storeCouponSingle.getCouponName());
+        storeCouponClaimRecord.setStoreId(storeCouponSingle.getStoreId());
+        storeCouponClaimRecord.setStoreName(storeCouponSingle.getStoreName());
+        storeCouponClaimRecord.setUserId(userId);
+        storeCouponClaimRecord.setStoreCouponId(storeCouponSingle.getStoreCoupRef());
+        return storeCouponClaimRecord;
+    }
 }

--
Gitblit v1.8.0