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); }