群聊优惠券

This commit is contained in:
2025-12-02 18:10:34 +08:00
parent 2c2b3765ad
commit d012201752
17 changed files with 628 additions and 22 deletions

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.ChatCoupon;
/**
* 映射层。
*
* @author ww
* @since 2025-12-02
*/
public interface ChatCouponMapper extends BaseMapper<ChatCoupon> {
}

View File

@@ -0,0 +1,142 @@
package com.czg.service.market.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.czg.exception.CzgException;
import com.czg.market.dto.ChatCouponDTO;
import com.czg.market.entity.MkShopCouponRecord;
import com.czg.market.entity.ShopCoupon;
import com.czg.market.service.MkShopCouponRecordService;
import com.czg.market.service.ShopCouponService;
import com.czg.market.vo.ChatCouponVO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.ChatCoupon;
import com.czg.market.service.ChatCouponService;
import com.czg.service.market.mapper.ChatCouponMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 服务层实现。
*
* @author ww
* @since 2025-12-02
*/
@Slf4j
@Service
public class ChatCouponServiceImpl extends ServiceImpl<ChatCouponMapper, ChatCoupon> implements ChatCouponService {
@Resource
private ShopCouponService couponService;
@Resource
private MkShopCouponRecordService recordService;
@Override
@Transactional
public void createChatCoupon(Long shopId, ChatCouponDTO chatCoupon) {
ShopCoupon coupon = couponService.getOne(QueryWrapper.create()
.eq(ShopCoupon::getId, chatCoupon.getCouponId())
);
if (coupon == null) {
throw new CzgException("优惠券不可使用,优惠券不存在");
}
if (!"all".equals(coupon.getGetUserType())) {
throw new CzgException("优惠券不可使用,优惠券存在领取用户限制");
}
ChatCoupon saveChatCoupon = ChatCoupon.builder()
.shopId(shopId)
.title(chatCoupon.getTitle())
.getLimit(chatCoupon.getGetLimit() == null ? 1 : chatCoupon.getGetLimit())
.giveNum(chatCoupon.getGiveNum() == null ? -10086 : chatCoupon.getGiveNum())
.leftNum(chatCoupon.getGiveNum() == null ? -10086 : chatCoupon.getGiveNum())
.useNum(0)
.couponId(chatCoupon.getCouponId())
.build();
save(saveChatCoupon);
if (saveChatCoupon.getId() == null) {
log.info("发放创建失败群聊发放任务回填Id失败{}", JSONObject.toJSONString(saveChatCoupon));
throw new CzgException("发放创建失败,请联系管理员");
}
MkShopCouponRecord record = recordService.assembleRecord(saveChatCoupon.getId(), coupon);
saveChatCoupon.setCouponJson(JSONObject.toJSONString(record));
updateById(saveChatCoupon);
}
@Override
public Page<ChatCouponVO> pageChatCoupon(Long shopId, Integer page, Integer size, Integer status) {
Page<ChatCouponVO> pages = pageAs(Page.of(page, size),
QueryWrapper.create()
.eq(ChatCoupon::getShopId, shopId)
.eq(ChatCoupon::getStatus, status)
.orderBy(ChatCoupon::getStatus).asc()
.orderBy(ChatCoupon::getId).desc(),
ChatCouponVO.class);
return pages;
}
/**
* 失效
*/
@Override
public void expiredChatCoupon(Long shopId, Long id) {
ChatCoupon coupon = new ChatCoupon();
coupon.setStatus(3);
update(coupon, QueryWrapper.create()
.eq(ChatCoupon::getId, id)
.eq(ChatCoupon::getShopId, shopId)
);
}
@Override
@Transactional
public void grantChatCoupon(Long id, Long shopUserId, Long userId) {
ChatCoupon coupon = getOne(QueryWrapper.create()
.eq(ChatCoupon::getId, id)
.eq(ChatCoupon::getShopId, shopUserId)
);
if (coupon == null) {
throw new CzgException("活动不存在");
}
if (coupon.getStatus() != 1) {
throw new CzgException("活动已失效");
}
boolean exists = recordService.exists(QueryWrapper.create()
.eq(MkShopCouponRecord::getSource, "群聊发放")
.eq(MkShopCouponRecord::getSourceId, coupon.getId())
.eq(MkShopCouponRecord::getShopUserId, shopUserId)
.eq(MkShopCouponRecord::getUserId, userId)
);
if (exists) {
throw new CzgException("不可重复领取");
}
if (coupon.getLeftNum() <= 0) {
throw new CzgException("发放失败,剩余数量不足");
}
if (coupon.getLeftNum() < coupon.getGetLimit()) {
throw new CzgException("发放失败,剩余数量不足");
}
coupon.setLeftNum(coupon.getLeftNum() - coupon.getGetLimit());
if (coupon.getLeftNum() == 0) {
coupon.setStatus(3);
}
updateById(coupon);
MkShopCouponRecord record = JSONObject.parseObject(coupon.getCouponJson(), MkShopCouponRecord.class);
record.setShopUserId(shopUserId);
record.setUserId(userId);
recordService.grantChatCoupon(record, coupon.getGetLimit());
}
@Override
public Page<MkShopCouponRecord> grantChatCouponRecord(Long id, Integer status, Integer page, Integer size) {
return recordService.pageAs(Page.of(page, size),
QueryWrapper.create()
.eq(MkShopCouponRecord::getSourceId, id)
.eq(MkShopCouponRecord::getStatus, status)
.orderBy(MkShopCouponRecord::getId).desc(),
MkShopCouponRecord.class);
}
}

View File

@@ -33,7 +33,6 @@ import com.czg.utils.PageUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
@@ -511,4 +510,78 @@ public class MkShopCouponRecordServiceImpl extends ServiceImpl<MkShopCouponRecor
receiveCoupon(giftDTO, rewardCouponDTO.getNum(), false);
return true;
}
/**
* 组装信息
*/
public MkShopCouponRecord assembleRecord(Long chatCouponId, ShopCoupon coupon) {
LocalDateTime start = LocalDateTime.now().with(LocalTime.MIN);
LocalDateTime end = null;
if ("fixed".equals(coupon.getValidType())) {
//固定时间
if (coupon.getDaysToTakeEffect() != null && coupon.getDaysToTakeEffect() > 0) {
start = LocalDateTimeUtil.offset(start, coupon.getDaysToTakeEffect(), ChronoUnit.DAYS).with(LocalTime.MIN);
}
end = LocalDateTimeUtil.offset(start, coupon.getValidDays(), ChronoUnit.DAYS).with(LocalTime.MAX).truncatedTo(ChronoUnit.SECONDS);
} else if ("custom".equals(coupon.getValidType())) {
//自定义时间
start = coupon.getValidStartTime();
end = coupon.getValidEndTime();
}
MkShopCouponRecord record = new MkShopCouponRecord();
record.setShopId(coupon.getShopId());
record.setCouponId(coupon.getId());
record.setCouponSyncId(coupon.getSyncId());
record.setSourceId(chatCouponId);
record.setSource("群聊发放");
record.setStatus(0);
record.setType(coupon.getCouponType());
record.setFullAmount(coupon.getFullAmount());
record.setDiscountAmount(coupon.getDiscountAmount());
record.setDiscountRate(coupon.getDiscountRate());
record.setMaxDiscountAmount(coupon.getMaxDiscountAmount());
record.setCreateTime(LocalDateTime.now());
record.setIsDel(0);
record.setUseStartTime(start);
record.setUseEndTime(end);
return record;
}
@Override
public void grantChatCoupon(MkShopCouponRecord record, Integer number) {
ShopCoupon coupon = couponService.selectOneById(record.getCouponId());
AssertUtil.isNull(coupon, "优惠券不存在");
List<MkShopCouponRecord> recordList = new ArrayList<>();
// 检查优惠券状态
if (coupon.getIsDel() != 0) {
// 状态异常,直接返回
return;
}
// 检查优惠券是否失效
if (coupon.getStatus() != 1) {
return;
}
coupon.setGiftNum(coupon.getGiftNum() + number);
for (int i = 0; i < number; i++) {
recordList.add(record);
}
mapper.insertBatchSelective(recordList, 50);
QueryWrapper queryWrapper = new QueryWrapper();
ShopCoupon newCoupon = new ShopCoupon();
newCoupon.setGiftNum(coupon.getGiftNum());
if (coupon.getSyncId() != null) {
queryWrapper.and(q -> {
q.eq(ShopCoupon::getId, coupon.getSyncId()).or(q1 -> {
q1.eq(ShopCoupon::getSyncId, coupon.getSyncId());
});
});
couponService.updateByQuery(newCoupon, queryWrapper);
} else {
newCoupon.setId(coupon.getId());
couponService.update(newCoupon, true);
}
}
}

View File

@@ -16,15 +16,13 @@ import com.czg.account.vo.UserCouponVo;
import com.czg.exception.CzgException;
import com.czg.market.dto.MkShopCouponGiftDTO;
import com.czg.market.dto.ShopCouponDTO;
import com.czg.market.entity.ChatCoupon;
import com.czg.market.entity.MkShopCouponRecord;
import com.czg.market.entity.ShopCoupon;
import com.czg.market.service.MkCouponGiftService;
import com.czg.market.service.MkShopCouponRecordService;
import com.czg.market.service.ShopCouponService;
import com.czg.market.service.*;
import com.czg.market.vo.ShopCouponPopUp;
import com.czg.market.vo.UserCouponVO;
import com.czg.order.entity.OrderInfo;
import com.czg.market.service.OrderInfoService;
import com.czg.product.entity.Product;
import com.czg.product.service.ProductService;
import com.czg.sa.StpKit;
@@ -43,6 +41,7 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.scheduling.annotation.Async;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -74,6 +73,27 @@ public class ShopCouponServiceImpl extends ServiceImpl<ShopCouponMapper, ShopCou
private ProductService productService;
@Resource
private OrderInfoService orderInfoService;
@Resource
private ChatCouponService chatCouponService;
@Override
public Page<ShopCouponDTO> chatCoupon(Long shopId, String search) {
QueryWrapper queryWrapper = new QueryWrapper();
// queryWrapper.select("id as id,title as name");
queryWrapper.eq(ShopCoupon::getShopId, shopId)
.eq(ShopCoupon::getGetUserType, "all")
.eq(ShopCoupon::getIsDel, 0)
.eq(ShopCoupon::getStatus, 1)
.like(ShopCoupon::getTitle, CzgStrUtils.getStrOrNull(search))
.orderBy(ShopCoupon::getCreateTime).desc();
queryWrapper.and(q -> {
q.eq(ShopCoupon::getValidType, "fixed").or(q1 -> {
q1.eq(ShopCoupon::getValidType, "custom").gt(ShopCoupon::getValidEndTime, LocalDateTime.now());
});
});
Page<ShopCouponDTO> list = pageAs(PageUtil.buildPage(), queryWrapper, ShopCouponDTO.class);
return list;
}
@Override
public Page<ShopCouponDTO> getCouponPage(ShopCouponDTO param) {
@@ -314,12 +334,30 @@ public class ShopCouponServiceImpl extends ServiceImpl<ShopCouponMapper, ShopCou
}
@Override
@Async
public Boolean use(List<Long> ids, Long shopUserId, Long orderId) {
List<MkShopCouponRecord> records = recordService.listByIds(ids);
if (records.isEmpty()) {
log.error("优惠券使用失败订单Id:{}", orderId);
return false;
}
Map<Long, Long> chatCouponIdCountMap = records.stream()
.filter(record -> "群聊发放".equals(record.getSource()))
.collect(Collectors.groupingBy(MkShopCouponRecord::getSourceId,
Collectors.counting()
));
chatCouponIdCountMap.forEach((chatCouponId, count) -> {
ChatCoupon chatCoupon = chatCouponService.getById(chatCouponId);
if (chatCoupon == null) {
return;
}
ChatCoupon upChatCoupon = new ChatCoupon();
upChatCoupon.setId(chatCoupon.getId());
upChatCoupon.setUseNum(chatCoupon.getUseNum() + count.intValue());
chatCouponService.updateById(upChatCoupon);
});
// 使用流来统计 couponId 出现的次数
Map<Long, Long> couponIdCountMap = records.stream()
.collect(Collectors.groupingBy(MkShopCouponRecord::getCouponId,

View File

@@ -0,0 +1,7 @@
<?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="com.czg.service.market.mapper.ChatCouponMapper">
</mapper>