| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- package com.ylx.giftCard.service.impl;
- import cn.hutool.core.bean.BeanUtil;
- import cn.hutool.core.collection.CollectionUtil;
- import cn.hutool.core.util.ObjectUtil;
- import cn.hutool.core.util.StrUtil;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.ylx.common.core.domain.model.WxLoginUser;
- import com.ylx.common.exception.ServiceException;
- import com.ylx.common.utils.DateUtils;
- import com.ylx.common.utils.SecurityUtils;
- import com.ylx.common.weixinPay.enums.WxPayTypeEnum;
- import com.ylx.common.weixinPay.service.WxPayV3Service;
- import com.ylx.giftCard.domain.GiftCard;
- import com.ylx.giftCard.domain.GiftCardOrder;
- import com.ylx.giftCard.domain.dto.GiftCardManageQueryDTO;
- import com.ylx.giftCard.domain.dto.GiftCardManageSaveDTO;
- import com.ylx.giftCard.domain.dto.GiftCardManageUpdateDTO;
- import com.ylx.giftCard.domain.dto.GiftCardPublishStatusDTO;
- import com.ylx.giftCard.domain.dto.GiftCardPurchaseDTO;
- import com.ylx.giftCard.domain.vo.GiftCardDetailVO;
- import com.ylx.giftCard.domain.vo.GiftCardManageDetailVO;
- import com.ylx.giftCard.domain.vo.GiftCardManageExportVO;
- import com.ylx.giftCard.domain.vo.GiftCardManagePageVO;
- import com.ylx.giftCard.domain.vo.GiftCardVO;
- import com.ylx.giftCard.mapper.GiftCardMapper;
- import com.ylx.giftCard.service.IGiftCardOrderService;
- import com.ylx.giftCard.service.IGiftCardService;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import javax.annotation.Resource;
- import java.math.BigDecimal;
- import java.time.LocalDate;
- import java.time.ZoneId;
- import java.util.Date;
- import java.util.List;
- import java.util.Map;
- import java.util.stream.Collectors;
- @Slf4j
- @Service
- public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> implements IGiftCardService {
- @Resource
- private IGiftCardOrderService giftCardOrderService;
- @Resource
- private WxPayV3Service wxPayV3Service;
- private static final int NOT_DELETE = 0;
- private static final int PUBLISHED = 1;
- private static final int UNPUBLISHED = 0;
- private static final int DELETE = 1;
- @Override
- public Page<GiftCardVO> getGiftCardPage(Page<GiftCard> page) {
- LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(GiftCard::getIsDelete, NOT_DELETE);
- wrapper.eq(GiftCard::getIsPublished, PUBLISHED);
- wrapper.orderByDesc(GiftCard::getCreateTime);
- Page<GiftCard> giftCardPage = this.baseMapper.selectPage(page, wrapper);
- Page<GiftCardVO> pageData = new Page<>(
- giftCardPage.getCurrent(),
- giftCardPage.getSize(),
- giftCardPage.getTotal()
- );
- if (CollectionUtil.isNotEmpty(giftCardPage.getRecords())) {
- List<GiftCardVO> voList = giftCardPage.getRecords().stream()
- .map(GiftCardVO::new)
- .collect(Collectors.toList());
- pageData.setRecords(voList);
- }
- return pageData;
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Map<String, Object> purchaseGiftCard(GiftCardPurchaseDTO dto) {
- // 1. 获取当前用户
- WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser();
- if (ObjectUtil.isNull(wxLoginUser)) {
- log.warn("用户未登录,无法创建订单");
- throw new ServiceException("用户未登录");
- }
- Long id = dto.getId();
- Integer quantity = dto.getQuantity();
- String merchantId = dto.getMerchantId();
- // 2. 查询并校验购物卡
- GiftCard card = this.getById(id);
- validateGiftCard(card, quantity);
- // 3. 乐观锁扣减库存(增加状态校验,防止无效更新)
- int rowsAffected = deductStockOptimisticLock(card.getId(), dto.getQuantity());
- if (rowsAffected <= 0) {
- log.warn("购买失败,库存不足或商品不存在,购物卡ID: {}", id);
- throw new ServiceException("库存不足或商品状态异常");
- }
- log.info("购买成功,购物卡ID: {}, 数量: {}", id, quantity);
- // 4. 创建订单
- GiftCardOrder order = this.giftCardOrderService.buildOrder(card, quantity, merchantId, wxLoginUser);
- if(ObjectUtil.isNull(order)){
- log.warn("购物卡订单创建失败,购物卡ID: {},下单人ID: {}", card.getId(), wxLoginUser.getId());
- throw new ServiceException("购物卡订单创建失败");
- }
- log.info("购物卡订单创建,购物卡ID: {}, 订单编号: {}", id, order.getOrderNo());
- // 5. 调用微信支付
- return createWxPayOrder(order, wxLoginUser);
- }
- @Override
- public GiftCardDetailVO getGiftCardDetail(Long id) {
- LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(GiftCard::getId, id)
- .eq(GiftCard::getIsDelete, NOT_DELETE)
- .eq(GiftCard::getIsPublished, PUBLISHED);
- GiftCard card = this.getOne(wrapper);
- if (ObjectUtil.isNull(card)) {
- return null;
- }
- GiftCardDetailVO vo = new GiftCardDetailVO();
- BeanUtil.copyProperties(card, vo);
- return vo;
- }
- @Override
- public Page<GiftCardManagePageVO> getManagePage(Page<GiftCard> page, GiftCardManageQueryDTO dto) {
- Page<GiftCard> pageParam = ObjectUtil.isNull(page) ? new Page<>(1, 10) : page;
- LambdaQueryWrapper<GiftCard> wrapper = buildManageQueryWrapper(dto);
- Page<GiftCard> entityPage = this.baseMapper.selectPage(pageParam, wrapper);
- Page<GiftCardManagePageVO> pageData = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
- pageData.setPages(entityPage.getPages());
- if (CollectionUtil.isNotEmpty(entityPage.getRecords())) {
- pageData.setRecords(entityPage.getRecords().stream()
- .map(this::toManagePageVO)
- .collect(Collectors.toList()));
- }
- return pageData;
- }
- @Override
- public List<GiftCardManageExportVO> getManageExportList(GiftCardManageQueryDTO dto) {
- GiftCardManageQueryDTO query = ObjectUtil.isNull(dto) ? new GiftCardManageQueryDTO() : dto;
- checkCreateDateRange(query.getBeginCreateDate(), query.getEndCreateDate());
- if (StrUtil.isNotBlank(query.getName())) {
- query.setName(StrUtil.trim(query.getName()));
- }
- List<GiftCard> entityList = this.baseMapper.selectManageList(
- query,
- toStartDate(query.getBeginCreateDate()),
- toEndDate(query.getEndCreateDate())
- );
- return entityList.stream().map(this::toManageExportVO).collect(Collectors.toList());
- }
- @Override
- public GiftCardManageDetailVO getManageDetail(Long id) {
- GiftCard card = getActiveGiftCard(id);
- GiftCardManageDetailVO vo = new GiftCardManageDetailVO();
- BeanUtil.copyProperties(card, vo);
- return vo;
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void addGiftCard(GiftCardManageSaveDTO dto) {
- checkSaveParam(dto);
- GiftCard entity = new GiftCard();
- fillGiftCard(entity, dto, normalizePublishStatus(dto.getIsPublished(), UNPUBLISHED));
- entity.setId(null);
- entity.setSales(0);
- entity.setCreateBy(SecurityUtils.getUsername());
- entity.setCreateTime(DateUtils.getNowDate());
- entity.setUpdateBy(SecurityUtils.getUsername());
- entity.setUpdateTime(DateUtils.getNowDate());
- int insertResult = this.baseMapper.insert(entity);
- if (insertResult <= 0) {
- throw new ServiceException("新增购物卡失败");
- }
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void updateGiftCard(GiftCardManageUpdateDTO dto) {
- if (ObjectUtil.isNull(dto) || ObjectUtil.isNull(dto.getId())) {
- throw new ServiceException("购物卡ID不能为空");
- }
- checkSaveParam(dto);
- GiftCard entity = getActiveGiftCard(dto.getId());
- Integer publishStatus = normalizePublishStatus(dto.getIsPublished(), entity.getIsPublished());
- fillGiftCard(entity, dto, publishStatus);
- entity.setUpdateBy(SecurityUtils.getUsername());
- entity.setUpdateTime(DateUtils.getNowDate());
- int updateResult = this.baseMapper.updateById(entity);
- if (updateResult <= 0) {
- throw new ServiceException("编辑购物卡失败");
- }
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void updatePublishStatus(GiftCardPublishStatusDTO dto) {
- if (ObjectUtil.isNull(dto) || ObjectUtil.isNull(dto.getId())) {
- throw new ServiceException("购物卡ID不能为空");
- }
- Integer publishStatus = normalizePublishStatus(dto.getIsPublished(), null);
- GiftCard entity = getActiveGiftCard(dto.getId());
- entity.setIsPublished(publishStatus);
- entity.setUpdateBy(SecurityUtils.getUsername());
- entity.setUpdateTime(DateUtils.getNowDate());
- int updateResult = this.baseMapper.updateById(entity);
- if (updateResult <= 0) {
- throw new ServiceException("修改购物卡上架状态失败");
- }
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void deleteGiftCard(Long id) {
- int deleteResult = this.baseMapper.deleteById(id);
- if (deleteResult <= 0) {
- throw new ServiceException("删除购物卡失败");
- }
- }
- /**
- * 校验购物卡有效性
- */
- private void validateGiftCard(GiftCard card, Integer quantity) {
- if (ObjectUtil.isNull(card)) {
- throw new ServiceException("商品不存在");
- }
- if (card.getIsDelete() != NOT_DELETE) {
- log.warn("购买失败,购物卡已删除,ID: {}", card.getId());
- throw new ServiceException("购物卡已删除");
- }
- if (card.getIsPublished() != PUBLISHED) {
- log.warn("购买失败,购物卡未上架,ID: {}", card.getId());
- throw new ServiceException("购物卡未上架");
- }
- if (card.getStock() < quantity) {
- log.warn("购买失败,库存不足,ID: {},库存: {},需求数量: {}", card.getId(), card.getStock(), quantity);
- throw new ServiceException("库存不足");
- }
- }
- /**
- * 乐观锁扣减库存(增加状态条件,确保只更新有效记录)
- */
- private int deductStockOptimisticLock(Long cardId, Integer quantity) {
- LambdaUpdateWrapper<GiftCard> wrapper = new LambdaUpdateWrapper<>();
- wrapper.eq(GiftCard::getId, cardId)
- .eq(GiftCard::getIsDelete, NOT_DELETE)
- .eq(GiftCard::getIsPublished, PUBLISHED)
- .ge(GiftCard::getStock, quantity) // 库存充足时才更新
- .setSql("stock = stock - " + quantity + ", sales = sales + " + quantity);
- return baseMapper.update(null, wrapper);
- }
- /**
- * 补偿回滚库存
- */
- private void recoverStock(Long cardId, Integer num) {
- GiftCard updateEntity = new GiftCard();
- LambdaUpdateWrapper<GiftCard> updateWrapper = new LambdaUpdateWrapper<>();
- updateWrapper.eq(GiftCard::getId, cardId)
- .eq(GiftCard::getIsDelete, NOT_DELETE);
- updateWrapper.setSql("stock = stock + #{num}, sales = sales - #{num}");
- // 数据放到实体里
- updateEntity.setStock(num);
- baseMapper.update(updateEntity, updateWrapper);
- }
- /**
- * 创建微信支付订单(事务外执行,减少事务时长)
- */
- private Map<String, Object> createWxPayOrder(GiftCardOrder order, WxLoginUser wxLoginUser) {
- try {
- return wxPayV3Service.createV3JsapiOrder(
- order.getOrderNo(),
- order.getPayAmount(),
- "购物卡购买",
- wxLoginUser.getCOpenid(),
- WxPayTypeEnum.GIFT_CARD.getCode()
- );
- } catch (Exception e) {
- log.error("微信支付下单失败,订单号: {}", order.getOrderNo(), e);
- // 支付失败:恢复库存、修改订单为取消
- recoverStock(order.getGiftCardId(), order.getPurchaseQuantity());
- this.giftCardOrderService.cancelOrder(order.getId());
- throw new ServiceException("支付服务异常,请稍后重试");
- }
- }
- private GiftCard getActiveGiftCard(Long id) {
- if (ObjectUtil.isNull(id)) {
- throw new ServiceException("购物卡ID不能为空");
- }
- if (id <= 0) {
- throw new ServiceException("购物卡ID不正确");
- }
- LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(GiftCard::getId, id).eq(GiftCard::getIsDelete, NOT_DELETE);
- GiftCard entity = this.baseMapper.selectOne(wrapper);
- if (ObjectUtil.isNull(entity)) {
- throw new ServiceException("购物卡不存在或已删除");
- }
- return entity;
- }
- private void checkSaveParam(GiftCardManageSaveDTO dto) {
- if (ObjectUtil.isNull(dto)) {
- throw new ServiceException("购物卡参数不能为空");
- }
- if (StrUtil.isBlank(dto.getName())) {
- throw new ServiceException("购物卡名称不能为空");
- }
- if (StrUtil.length(StrUtil.trim(dto.getName())) > 20) {
- throw new ServiceException("购物卡名称不能超过20个字符");
- }
- if (StrUtil.isBlank(dto.getImageUrl())) {
- throw new ServiceException("图片不能为空");
- }
- if (ObjectUtil.isNull(dto.getAmount()) || dto.getAmount().signum() <= 0) {
- throw new ServiceException("购物卡金额必须大于0");
- }
- if (ObjectUtil.isNull(dto.getCommissionRate())
- || dto.getCommissionRate().signum() < 0
- || dto.getCommissionRate().compareTo(new BigDecimal("100")) > 0) {
- throw new ServiceException("商户提成比例必须在0到100之间");
- }
- if (ObjectUtil.isNull(dto.getStock()) || dto.getStock() < 0) {
- throw new ServiceException("库存不能小于0");
- }
- if (ObjectUtil.isNull(dto.getValidStartDate()) || ObjectUtil.isNull(dto.getValidEndDate())) {
- throw new ServiceException("有效期不能为空");
- }
- if (dto.getValidStartDate().isAfter(dto.getValidEndDate())) {
- throw new ServiceException("有效期开始日期不能晚于结束日期");
- }
- }
- private void checkCreateDateRange(LocalDate beginDate, LocalDate endDate) {
- if (ObjectUtil.isNotNull(beginDate) && ObjectUtil.isNotNull(endDate) && beginDate.isAfter(endDate)) {
- throw new ServiceException("创建开始日期不能晚于结束日期");
- }
- }
- private LambdaQueryWrapper<GiftCard> buildManageQueryWrapper(GiftCardManageQueryDTO dto) {
- GiftCardManageQueryDTO query = ObjectUtil.isNull(dto) ? new GiftCardManageQueryDTO() : dto;
- checkCreateDateRange(query.getBeginCreateDate(), query.getEndCreateDate());
- LambdaQueryWrapper<GiftCard> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(GiftCard::getIsDelete, NOT_DELETE)
- .like(StrUtil.isNotBlank(query.getName()), GiftCard::getName, StrUtil.trim(query.getName()))
- .eq(ObjectUtil.isNotNull(query.getIsPublished()), GiftCard::getIsPublished, query.getIsPublished())
- .ge(ObjectUtil.isNotNull(query.getBeginCreateDate()), GiftCard::getCreateTime, toStartDate(query.getBeginCreateDate()))
- .le(ObjectUtil.isNotNull(query.getEndCreateDate()), GiftCard::getCreateTime, toEndDate(query.getEndCreateDate()))
- .orderByDesc(GiftCard::getCreateTime)
- .orderByDesc(GiftCard::getId);
- return wrapper;
- }
- /**
- * 填充购物卡实体
- * @param entity
- * @param dto
- * @param publishStatus
- */
- private void fillGiftCard(GiftCard entity, GiftCardManageSaveDTO dto, Integer publishStatus) {
- entity.setMerchantId(StrUtil.trim(dto.getMerchantId()));
- entity.setName(StrUtil.trim(dto.getName()));
- entity.setImageUrl(StrUtil.trim(dto.getImageUrl()));
- entity.setAmount(dto.getAmount());
- entity.setCommissionRate(dto.getCommissionRate());
- entity.setStock(dto.getStock());
- entity.setValidStartDate(dto.getValidStartDate());
- entity.setValidEndDate(dto.getValidEndDate());
- entity.setDescription(dto.getDescription());
- entity.setIsPublished(publishStatus);
- }
- /**
- * 规范化上架状态值
- * @param value
- * @param defaultValue
- * @return Integer
- */
- private Integer normalizePublishStatus(Integer value, Integer defaultValue) {
- if (ObjectUtil.isNull(value)) {
- if (ObjectUtil.isNull(defaultValue)) {
- throw new ServiceException("上架状态不能为空");
- }
- return defaultValue;
- }
- if (value == UNPUBLISHED || value == PUBLISHED) {
- return value;
- }
- throw new ServiceException("上架状态值不正确");
- }
- private GiftCardManagePageVO toManagePageVO(GiftCard entity) {
- GiftCardManagePageVO vo = new GiftCardManagePageVO();
- BeanUtil.copyProperties(entity, vo);
- return vo;
- }
- private GiftCardManageExportVO toManageExportVO(GiftCard entity) {
- GiftCardManageExportVO vo = new GiftCardManageExportVO();
- BeanUtil.copyProperties(entity, vo);
- return vo;
- }
- private Date toStartDate(LocalDate date) {
- if (ObjectUtil.isNull(date)) {
- return null;
- }
- return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
- }
- private Date toEndDate(LocalDate date) {
- if (ObjectUtil.isNull(date)) {
- return null;
- }
- return Date.from(date.plusDays(1).atStartOfDay(ZoneId.systemDefault()).minusNanos(1).toInstant());
- }
- }
|