diff --git a/play-admin/src/main/java/com/starry/admin/modules/common/controller/CommonController.java b/play-admin/src/main/java/com/starry/admin/modules/common/controller/CommonController.java new file mode 100644 index 0000000..77ed823 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/common/controller/CommonController.java @@ -0,0 +1,44 @@ +package com.starry.admin.modules.common.controller; + +import cn.hutool.core.util.StrUtil; +import com.starry.admin.common.exception.CustomException; +import com.starry.admin.common.oss.service.IOssFileService; +import com.starry.admin.utils.SecurityUtils; +import com.starry.common.result.R; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import java.io.IOException; +import javax.annotation.Resource; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * 后台通用能力接口 + */ +@Api(tags = "后台通用接口", description = "提供后台常用的公共能力,例如文件上传") +@RestController +@RequestMapping("/common") +public class CommonController { + + @Resource + private IOssFileService ossFileService; + + @ApiOperation(value = "上传文件", notes = "上传文件到 OSS 并返回访问地址") + @PostMapping("/upload") + public R upload(@ApiParam(value = "上传文件", required = true) @RequestParam("file") MultipartFile file) + throws IOException { + if (file == null || file.isEmpty()) { + throw new CustomException("上传文件不能为空"); + } + String tenantId = SecurityUtils.getTenantId(); + if (StrUtil.isBlank(tenantId)) { + throw new CustomException("租户信息缺失,请重新登录"); + } + String fileUrl = ossFileService.upload(file.getInputStream(), tenantId, file.getOriginalFilename()); + return R.ok(fileUrl); + } +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/custom/mapper/PlayCustomGiftInfoMapper.java b/play-admin/src/main/java/com/starry/admin/modules/custom/mapper/PlayCustomGiftInfoMapper.java index 28520be..0518f2d 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/custom/mapper/PlayCustomGiftInfoMapper.java +++ b/play-admin/src/main/java/com/starry/admin/modules/custom/mapper/PlayCustomGiftInfoMapper.java @@ -2,6 +2,8 @@ package com.starry.admin.modules.custom.mapper; import com.github.yulichang.base.MPJBaseMapper; import com.starry.admin.modules.custom.module.entity.PlayCustomGiftInfoEntity; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; /** * 顾客和礼物关系Mapper接口 @@ -11,4 +13,20 @@ import com.starry.admin.modules.custom.module.entity.PlayCustomGiftInfoEntity; */ public interface PlayCustomGiftInfoMapper extends MPJBaseMapper { + /** + * 原子递增顾客礼物数量 + * + * @param customId 顾客ID + * @param giftId 礼物ID + * @param tenantId 租户ID + * @param delta 增量 + * @return 受影响行数 + */ + @Update("UPDATE play_custom_gift_info " + + "SET giff_number = giff_number + #{delta} " + + "WHERE custom_id = #{customId} AND giff_id = #{giftId} " + + "AND (tenant_id = #{tenantId} OR tenant_id IS NULL)") + int incrementGiftCount(@Param("customId") String customId, @Param("giftId") String giftId, + @Param("tenantId") String tenantId, @Param("delta") long delta); + } diff --git a/play-admin/src/main/java/com/starry/admin/modules/custom/service/IPlayCustomGiftInfoService.java b/play-admin/src/main/java/com/starry/admin/modules/custom/service/IPlayCustomGiftInfoService.java index c817e83..8693872 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/custom/service/IPlayCustomGiftInfoService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/custom/service/IPlayCustomGiftInfoService.java @@ -31,7 +31,7 @@ public interface IPlayCustomGiftInfoService extends IService selectBtyCustomId(String customId); + List selectByCustomId(String customId, String tenantId); /** * 查询顾客和礼物关系 @@ -69,6 +69,16 @@ public interface IPlayCustomGiftInfoService extends IService selectBtyCustomId(String customId) { + public List selectByCustomId(String customId, String tenantId) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(PlayCustomGiftInfoEntity::getCustomId, customId); + if (StrUtil.isNotBlank(tenantId)) { + lambdaQueryWrapper.eq(PlayCustomGiftInfoEntity::getTenantId, tenantId); + } return this.baseMapper.selectList(lambdaQueryWrapper); } @@ -101,6 +105,39 @@ public class PlayCustomGiftInfoServiceImpl extends ServiceImpl { + /** + * 原子递增店员礼物数量 + * + * @param clerkId 店员ID + * @param giftId 礼物ID + * @param tenantId 租户ID + * @param delta 增量 + * @return 受影响行数 + */ + @Update("UPDATE play_clerk_gift_info " + + "SET giff_number = giff_number + #{delta} " + + "WHERE clerk_id = #{clerkId} AND giff_id = #{giftId} " + + "AND (tenant_id = #{tenantId} OR tenant_id IS NULL)") + int incrementGiftCount(@Param("clerkId") String clerkId, @Param("giftId") String giftId, + @Param("tenantId") String tenantId, @Param("delta") long delta); + } diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/service/IPlayClerkGiftInfoService.java b/play-admin/src/main/java/com/starry/admin/modules/shop/service/IPlayClerkGiftInfoService.java index cea5719..6209cbb 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/shop/service/IPlayClerkGiftInfoService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/service/IPlayClerkGiftInfoService.java @@ -32,7 +32,7 @@ public interface IPlayClerkGiftInfoService extends IService selectBtyClerkId(String clerkId); + List selectBtyClerkId(String clerkId, String tenantId); /** * 查询店员和礼物关系 @@ -70,6 +70,16 @@ public interface IPlayClerkGiftInfoService extends IService { * @author admin * @since 2024/4/25 15:56 **/ - List clerkListByAll(String clerkId, String obtained); + List clerkListByAll(String tenantId, String clerkId, String obtained); /** * 店员查询所有礼物 @@ -57,7 +57,7 @@ public interface IPlayGiftInfoService extends IService { * @author admin * @since 2024/4/25 15:56 **/ - List customListByAll(String customId, String obtained); + List customListByAll(String tenantId, String customId, String obtained); /** * 查询礼物列表 diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayClerkGiftInfoServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayClerkGiftInfoServiceImpl.java index 6d896b0..9c17cf7 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayClerkGiftInfoServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayClerkGiftInfoServiceImpl.java @@ -12,6 +12,7 @@ import com.starry.common.utils.IdUtils; import java.util.Arrays; import java.util.List; import javax.annotation.Resource; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; /** @@ -43,9 +44,12 @@ public class PlayClerkGiftInfoServiceImpl extends ServiceImpl selectBtyClerkId(String clerkId) { + public List selectBtyClerkId(String clerkId, String tenantId) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(PlayClerkGiftInfoEntity::getClerkId, clerkId); + if (StrUtil.isNotBlank(tenantId)) { + lambdaQueryWrapper.eq(PlayClerkGiftInfoEntity::getTenantId, tenantId); + } return this.baseMapper.selectList(lambdaQueryWrapper); } @@ -101,6 +105,39 @@ public class PlayClerkGiftInfoServiceImpl extends ServiceImpl customListByAll(String customId, String obtained) { + public List customListByAll(String tenantId, String customId, String obtained) { if ("0".equals(obtained)) { - // 查询所有礼物,然后减去已获得礼物 - MPJLambdaWrapper lambdaWrapper = new MPJLambdaWrapper<>(); - lambdaWrapper.selectAll(PlayGiftInfoEntity.class); - List list = this.baseMapper.selectJoinList(PlayClerkGiftReturnVo.class, - lambdaWrapper); - List giftInfoEntities = customGiftInfoService.selectBtyCustomId(customId); - // 使用迭代器安全地移除元素 - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - PlayClerkGiftReturnVo item = iterator.next(); - for (PlayCustomGiftInfoEntity giftInfoEntity : giftInfoEntities) { - if (giftInfoEntity.getGiffId().equals(item.getId())) { - iterator.remove(); - break; - } - } + LambdaQueryWrapper giftWrapper = new LambdaQueryWrapper<>(); + giftWrapper.eq(PlayGiftInfoEntity::getState, "0"); + giftWrapper.eq(PlayGiftInfoEntity::getHistory, "0"); + giftWrapper.eq(StrUtil.isNotBlank(tenantId), PlayGiftInfoEntity::getTenantId, tenantId); + List activeGifts = this.baseMapper.selectList(giftWrapper); - } - return list; + List obtainedRecords = + customGiftInfoService.selectByCustomId(customId, tenantId); + Set obtainedGiftIds = obtainedRecords.stream() + .map(PlayCustomGiftInfoEntity::getGiffId) + .collect(Collectors.toSet()); + + return activeGifts.stream() + .filter(gift -> !obtainedGiftIds.contains(gift.getId())) + .map(this::toGiftReturnVoWithZeroCount) + .collect(Collectors.toList()); } if ("1".equals(obtained)) { MPJLambdaWrapper lambdaWrapper = new MPJLambdaWrapper<>(); @@ -88,6 +87,10 @@ public class PlayGiftInfoServiceImpl extends ServiceImpl(); @@ -95,27 +98,24 @@ public class PlayGiftInfoServiceImpl extends ServiceImpl clerkListByAll(String clerkId, String obtained) { + public List clerkListByAll(String tenantId, String clerkId, String obtained) { if ("0".equals(obtained)) { - // 查询所有礼物,然后减去已获得礼物 - MPJLambdaWrapper lambdaWrapper = new MPJLambdaWrapper<>(); - lambdaWrapper.selectAll(PlayGiftInfoEntity.class); - List list = this.baseMapper.selectJoinList(PlayClerkGiftReturnVo.class, - lambdaWrapper); - List clerkGiftInfoEntities = clerkGiftInfoService.selectBtyClerkId(clerkId); - // 使用迭代器安全地移除元素 - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - PlayClerkGiftReturnVo item = iterator.next(); - for (PlayClerkGiftInfoEntity clerkGiftInfoEntity : clerkGiftInfoEntities) { - if (clerkGiftInfoEntity.getGiffId().equals(item.getId())) { - iterator.remove(); - break; - } - } + LambdaQueryWrapper giftWrapper = new LambdaQueryWrapper<>(); + giftWrapper.eq(PlayGiftInfoEntity::getState, "0"); + giftWrapper.eq(PlayGiftInfoEntity::getHistory, "0"); + giftWrapper.eq(StrUtil.isNotBlank(tenantId), PlayGiftInfoEntity::getTenantId, tenantId); + List activeGifts = this.baseMapper.selectList(giftWrapper); - } - return list; + List obtainedRecords = + clerkGiftInfoService.selectBtyClerkId(clerkId, tenantId); + Set obtainedGiftIds = obtainedRecords.stream() + .map(PlayClerkGiftInfoEntity::getGiffId) + .collect(Collectors.toSet()); + + return activeGifts.stream() + .filter(gift -> !obtainedGiftIds.contains(gift.getId())) + .map(this::toGiftReturnVoWithZeroCount) + .collect(Collectors.toList()); } if ("1".equals(obtained)) { MPJLambdaWrapper lambdaWrapper = new MPJLambdaWrapper<>(); @@ -125,6 +125,10 @@ public class PlayGiftInfoServiceImpl extends ServiceImpl(); @@ -132,7 +136,24 @@ public class PlayGiftInfoServiceImpl extends ServiceImpl listByAll() { - return this.baseMapper.selectList(new LambdaQueryWrapper<>()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PlayGiftInfoEntity::getState, "0"); + wrapper.eq(PlayGiftInfoEntity::getHistory, "0"); + wrapper.orderByAsc(PlayGiftInfoEntity::getListingTime); + return this.baseMapper.selectList(wrapper); + } + + private PlayClerkGiftReturnVo toGiftReturnVoWithZeroCount(PlayGiftInfoEntity gift) { + PlayClerkGiftReturnVo vo = new PlayClerkGiftReturnVo(); + vo.setId(gift.getId()); + vo.setName(gift.getName()); + vo.setType(gift.getType()); + vo.setUrl(gift.getUrl()); + vo.setPrice(gift.getPrice()); + vo.setUnit(gift.getUnit()); + vo.setState(gift.getState()); + vo.setGiffNumber(0L); + return vo; } /** diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java index e795ba9..7b0c24e 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java @@ -1,5 +1,6 @@ package com.starry.admin.modules.weichat.controller; +import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import com.starry.admin.common.aspect.ClerkUserLogin; @@ -51,6 +52,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.transaction.annotation.Transactional; @@ -416,9 +418,13 @@ public class WxClerkController { throw new CustomException("用户不存在"); } // 获取所有礼物列表 - List giftInfoEntities = giftInfoService.listByAll(); + List giftInfoEntities = giftInfoService.listByAll().stream() + .filter(gift -> StrUtil.isBlank(entity.getTenantId()) + || entity.getTenantId().equals(gift.getTenantId())) + .collect(Collectors.toList()); // 获取已点亮礼物 - List clerkListByAll = giftInfoService.clerkListByAll(id, "1"); + List clerkListByAll = + giftInfoService.clerkListByAll(entity.getTenantId(), id, "1"); // 组装数据 List result = new ArrayList<>(); for (PlayGiftInfoEntity giftInfoEntity : giftInfoEntities) { diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java index aafba37..cbf96ca 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java @@ -12,7 +12,6 @@ import com.starry.admin.common.task.OverdueOrderHandlerTask; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity; import com.starry.admin.modules.clerk.service.IPlayClerkCommodityService; import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService; -import com.starry.admin.modules.custom.module.entity.PlayCustomGiftInfoEntity; import com.starry.admin.modules.custom.module.entity.PlayCustomLeaveMsgEntity; import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; import com.starry.admin.modules.custom.service.IPlayCustomFollowInfoService; @@ -31,10 +30,8 @@ import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity; import com.starry.admin.modules.order.service.IPlayOrderComplaintInfoService; import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService; import com.starry.admin.modules.order.service.IPlayOrderInfoService; -import com.starry.admin.modules.shop.module.entity.PlayClerkGiftInfoEntity; import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity; import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity; -import com.starry.admin.modules.shop.module.entity.PlayGiftInfoEntity; import com.starry.admin.modules.shop.module.vo.PlayCommodityInfoVo; import com.starry.admin.modules.shop.service.*; import com.starry.admin.modules.weichat.entity.*; @@ -49,6 +46,7 @@ import com.starry.admin.modules.weichat.entity.order.PlayOrderInfoCommodityAdd; import com.starry.admin.modules.weichat.entity.user.PlayCustomUserReturnDetailVo; import com.starry.admin.modules.weichat.service.WxCustomMpService; import com.starry.admin.modules.weichat.service.WxCustomUserService; +import com.starry.admin.modules.weichat.service.WxGiftOrderService; import com.starry.admin.utils.MoneyUtils; import com.starry.admin.utils.SecurityUtils; import com.starry.common.result.R; @@ -128,6 +126,9 @@ public class WxCustomController { @Resource private WxCustomMpService wxCustomMpService; + @Resource + private WxGiftOrderService wxGiftOrderService; + @Resource OverdueOrderHandlerTask overdueOrderHandlerTask; @@ -271,73 +272,8 @@ public class WxCustomController { @CustomUserLogin @PostMapping("/order/gift") public R giftToOdder(@ApiParam(value = "礼物信息", required = true) @Validated @RequestBody PlayOrderInfoGiftAdd vo) { - String userId = ThreadLocalRequestDetail.getCustomUserInfo().getId(); - PlayGiftInfoEntity giftInfo = giftInfoService.selectPlayGiftInfoById(vo.getGiftId()); - PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(userId); - BigDecimal money = giftInfo.getPrice().multiply(new BigDecimal(vo.getGiftQuantity())); - if (money.compareTo(customUserInfo.getAccountBalance()) > 0) { - throw new ServiceException("余额不足", 998); - } - String orderId = IdUtils.getUuid(); - // 记录订单信息 - OrderCreationRequest orderRequest = OrderCreationRequest.builder() - .orderId(orderId) - .orderNo(playOrderInfoService.getOrderNo()) - .orderStatus(OrderConstant.OrderStatus.COMPLETED) - .orderType(OrderConstant.OrderType.NORMAL) - .placeType(OrderConstant.PlaceType.REWARD) - .rewardType(RewardType.GIFT) - .isFirstOrder(true) - .commodityInfo(CommodityInfo.builder() - .commodityId(giftInfo.getId()) - .commodityType(OrderConstant.CommodityType.GIFT) - .commodityPrice(giftInfo.getPrice()) - .serviceDuration("") - .commodityName(giftInfo.getName()) - .commodityNumber(String.valueOf(vo.getGiftQuantity())) - .build()) - .paymentInfo(PaymentInfo.builder() - .orderMoney(money) - .finalAmount(money) - .discountAmount(BigDecimal.ZERO) - .couponIds(new ArrayList<>()) - .build()) - .purchaserBy(userId) - .acceptBy(vo.getClerkId()) - .weiChatCode(vo.getWeiChatCode()) - .remark(vo.getRemark()) - .build(); - playOrderInfoService.createOrderInfo(orderRequest); - // 顾客减少余额 - customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(), customUserInfo.getAccountBalance().subtract(money), "1", "赠送礼物", money, BigDecimal.ZERO, orderId); - // 陪聊增加余额 - // 修改顾客和礼物消息 - PlayCustomGiftInfoEntity customGiftInfoEntity = playCustomGiftInfoService.selectByGiftIdAndCustomId(vo.getGiftId(), userId); - if (customGiftInfoEntity == null) { - customGiftInfoEntity = new PlayCustomGiftInfoEntity(); - customGiftInfoEntity.setGiffId(vo.getGiftId()); - customGiftInfoEntity.setCustomId(userId); - customGiftInfoEntity.setGiffNumber(1L); - playCustomGiftInfoService.save(customGiftInfoEntity); - } else { - customGiftInfoEntity.setGiffNumber(customGiftInfoEntity.getGiffNumber() + vo.getGiftQuantity()); - playCustomGiftInfoService.update(customGiftInfoEntity); - } - - // 修改陪玩和礼物数据 - PlayClerkGiftInfoEntity clerkGiftInfoEntity = playClerkGiftInfoService.selectByGiftIdAndClerkId(vo.getGiftId(), vo.getClerkId()); - if (clerkGiftInfoEntity == null) { - clerkGiftInfoEntity = new PlayClerkGiftInfoEntity(); - clerkGiftInfoEntity.setGiffId(vo.getGiftId()); - clerkGiftInfoEntity.setClerkId(vo.getClerkId()); - clerkGiftInfoEntity.setGiffNumber(0L); - playClerkGiftInfoService.create(clerkGiftInfoEntity); - } else { - customGiftInfoEntity.setGiffNumber(customGiftInfoEntity.getGiffNumber() + vo.getGiftQuantity()); - playClerkGiftInfoService.update(clerkGiftInfoEntity); - } - - return R.ok("成功"); + String orderId = wxGiftOrderService.createGiftOrder(vo); + return R.ok().message("成功").data(orderId); } /** diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxGiftController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxGiftController.java index 4321a9c..a14a382 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxGiftController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxGiftController.java @@ -5,6 +5,8 @@ import com.starry.admin.common.aspect.ClerkUserLogin; import com.starry.admin.common.aspect.CustomUserLogin; import com.starry.admin.common.conf.ThreadLocalRequestDetail; import com.starry.admin.common.exception.CustomException; +import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity; +import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; import com.starry.admin.modules.shop.service.IPlayGiftInfoService; import com.starry.admin.modules.weichat.entity.PlayGiftInfoDto; import com.starry.admin.modules.weichat.entity.gift.PlayClerkGiftReturnVo; @@ -68,8 +70,9 @@ public class WxGiftController { if (!"0".equals(obtained) && !"1".equals(obtained)) { throw new CustomException("obtained参数异常"); } + PlayClerkUserInfoEntity clerk = ThreadLocalRequestDetail.getClerkUserInfo(); List list = giftInfoService - .clerkListByAll(ThreadLocalRequestDetail.getClerkUserInfo().getId(), obtained); + .clerkListByAll(clerk.getTenantId(), clerk.getId(), obtained); return R.ok(list); } @@ -94,8 +97,9 @@ public class WxGiftController { if (!"0".equals(obtained) && !"1".equals(obtained)) { throw new CustomException("obtained参数异常"); } + PlayCustomUserInfoEntity custom = ThreadLocalRequestDetail.getCustomUserInfo(); List list = giftInfoService - .customListByAll(ThreadLocalRequestDetail.getCustomUserInfo().getId(), obtained); + .customListByAll(custom.getTenantId(), custom.getId(), obtained); return R.ok(list); } diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxGiftOrderService.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxGiftOrderService.java new file mode 100644 index 0000000..f0adf50 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxGiftOrderService.java @@ -0,0 +1,131 @@ +package com.starry.admin.modules.weichat.service; + +import com.starry.admin.common.conf.ThreadLocalRequestDetail; +import com.starry.admin.common.exception.CustomException; +import com.starry.admin.common.exception.ServiceException; +import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; +import com.starry.admin.modules.custom.service.IPlayCustomGiftInfoService; +import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService; +import com.starry.admin.modules.order.module.constant.OrderConstant; +import com.starry.admin.modules.order.module.constant.OrderConstant.RewardType; +import com.starry.admin.modules.order.module.dto.CommodityInfo; +import com.starry.admin.modules.order.module.dto.OrderCreationRequest; +import com.starry.admin.modules.order.module.dto.PaymentInfo; +import com.starry.admin.modules.order.service.IPlayOrderInfoService; +import com.starry.admin.modules.shop.module.entity.PlayGiftInfoEntity; +import com.starry.admin.modules.shop.service.IPlayClerkGiftInfoService; +import com.starry.admin.modules.shop.service.IPlayGiftInfoService; +import com.starry.admin.modules.weichat.entity.PlayOrderInfoGiftAdd; +import com.starry.common.utils.IdUtils; +import java.math.BigDecimal; +import java.util.Collections; +import java.util.Objects; +import javax.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 微信礼物订单服务,负责处理顾客赠送礼物的事务逻辑。 + */ +@Service +public class WxGiftOrderService { + + @Resource + private IPlayGiftInfoService giftInfoService; + + @Resource + private IPlayOrderInfoService playOrderInfoService; + + @Resource + private IPlayCustomUserInfoService customUserInfoService; + + @Resource + private IPlayCustomGiftInfoService playCustomGiftInfoService; + + @Resource + private IPlayClerkGiftInfoService playClerkGiftInfoService; + + /** + * 创建礼物订单并处理余额与礼物计数。 + * + * @param request 礼物下单请求 + * @return 生成的订单ID + */ + @Transactional(rollbackFor = Exception.class) + public String createGiftOrder(PlayOrderInfoGiftAdd request) { + PlayCustomUserInfoEntity sessionUser = ThreadLocalRequestDetail.getCustomUserInfo(); + if (sessionUser == null || sessionUser.getId() == null) { + throw new CustomException("用户未登录"); + } + PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(sessionUser.getId()); + if (customUserInfo == null) { + throw new CustomException("用户不存在"); + } + + PlayGiftInfoEntity giftInfo = giftInfoService.selectPlayGiftInfoById(request.getGiftId()); + BigDecimal unitPrice = Objects.requireNonNull(giftInfo.getPrice(), "礼物价格未配置"); + int giftQuantity = request.getGiftQuantity(); + if (giftQuantity <= 0) { + throw new CustomException("礼物数量必须大于0"); + } + + BigDecimal totalAmount = unitPrice.multiply(BigDecimal.valueOf(giftQuantity)); + if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new CustomException("礼物金额必须大于0"); + } + + BigDecimal currentBalance = customUserInfo.getAccountBalance() == null + ? BigDecimal.ZERO : customUserInfo.getAccountBalance(); + if (totalAmount.compareTo(currentBalance) > 0) { + throw new ServiceException("余额不足", 998); + } + + String orderId = IdUtils.getUuid(); + OrderCreationRequest orderRequest = buildOrderCreationRequest(orderId, request, sessionUser.getId(), + giftInfo, totalAmount); + playOrderInfoService.createOrderInfo(orderRequest); + + BigDecimal newBalance = currentBalance.subtract(totalAmount); + customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), currentBalance, newBalance, "1", + "赠送礼物", totalAmount, BigDecimal.ZERO, orderId); + + String tenantId = customUserInfo.getTenantId(); + long delta = giftQuantity; + playCustomGiftInfoService.incrementGiftCount(customUserInfo.getId(), request.getGiftId(), tenantId, delta); + playClerkGiftInfoService.incrementGiftCount(request.getClerkId(), request.getGiftId(), tenantId, delta); + + return orderId; + } + + private OrderCreationRequest buildOrderCreationRequest(String orderId, PlayOrderInfoGiftAdd request, + String purchaserId, PlayGiftInfoEntity giftInfo, + BigDecimal totalAmount) { + return OrderCreationRequest.builder() + .orderId(orderId) + .orderNo(playOrderInfoService.getOrderNo()) + .orderStatus(OrderConstant.OrderStatus.COMPLETED) + .orderType(OrderConstant.OrderType.NORMAL) + .placeType(OrderConstant.PlaceType.REWARD) + .rewardType(RewardType.GIFT) + .isFirstOrder(true) + .commodityInfo(CommodityInfo.builder() + .commodityId(giftInfo.getId()) + .commodityType(OrderConstant.CommodityType.GIFT) + .commodityPrice(giftInfo.getPrice()) + .serviceDuration("") + .commodityName(giftInfo.getName()) + .commodityNumber(String.valueOf(request.getGiftQuantity())) + .build()) + .paymentInfo(PaymentInfo.builder() + .orderMoney(totalAmount) + .finalAmount(totalAmount) + .discountAmount(BigDecimal.ZERO) + .couponIds(Collections.emptyList()) + .build()) + .purchaserBy(purchaserId) + .acceptBy(request.getClerkId()) + .weiChatCode(request.getWeiChatCode()) + .remark(request.getRemark()) + .build(); + } +} diff --git a/play-admin/src/main/resources/db/migration/V11__harden_gift_uniqueness.sql b/play-admin/src/main/resources/db/migration/V11__harden_gift_uniqueness.sql new file mode 100644 index 0000000..1bcb41d --- /dev/null +++ b/play-admin/src/main/resources/db/migration/V11__harden_gift_uniqueness.sql @@ -0,0 +1,95 @@ +-- Consolidate duplicate clerk gift rows and enforce uniqueness + +-- Normalize null gift counts +UPDATE play_clerk_gift_info +SET giff_number = 0 +WHERE giff_number IS NULL; + +UPDATE play_custom_gift_info +SET giff_number = 0 +WHERE giff_number IS NULL; + +-- Remove invalid rows lacking required identifiers +DELETE FROM play_clerk_gift_info +WHERE clerk_id IS NULL OR giff_id IS NULL; + +DELETE FROM play_custom_gift_info +WHERE custom_id IS NULL OR giff_id IS NULL; + +-- Merge duplicate clerk gift rows and keep a single survivor per key +UPDATE play_clerk_gift_info t +JOIN ( + SELECT COALESCE(MAX(CASE WHEN deleted = 0 THEN id END), MAX(id)) AS keep_id, + tenant_id, + clerk_id, + giff_id, + SUM(giff_number) AS total + FROM play_clerk_gift_info + GROUP BY tenant_id, clerk_id, giff_id + HAVING COUNT(*) > 1 +) agg ON t.id = agg.keep_id +SET t.giff_number = agg.total; + +DELETE dup +FROM play_clerk_gift_info dup +JOIN ( + SELECT COALESCE(MAX(CASE WHEN deleted = 0 THEN id END), MAX(id)) AS keep_id, + tenant_id, + clerk_id, + giff_id + FROM play_clerk_gift_info + GROUP BY tenant_id, clerk_id, giff_id + HAVING COUNT(*) > 1 +) keepers + ON dup.tenant_id = keepers.tenant_id + AND dup.clerk_id = keepers.clerk_id + AND dup.giff_id = keepers.giff_id +WHERE dup.id <> keepers.keep_id; + +-- Merge duplicate customer gift rows and keep a single survivor per key +UPDATE play_custom_gift_info t +JOIN ( + SELECT COALESCE(MAX(CASE WHEN deleted = 0 THEN id END), MAX(id)) AS keep_id, + tenant_id, + custom_id, + giff_id, + SUM(giff_number) AS total + FROM play_custom_gift_info + GROUP BY tenant_id, custom_id, giff_id + HAVING COUNT(*) > 1 +) agg ON t.id = agg.keep_id +SET t.giff_number = agg.total; + +DELETE dup +FROM play_custom_gift_info dup +JOIN ( + SELECT COALESCE(MAX(CASE WHEN deleted = 0 THEN id END), MAX(id)) AS keep_id, + tenant_id, + custom_id, + giff_id + FROM play_custom_gift_info + GROUP BY tenant_id, custom_id, giff_id + HAVING COUNT(*) > 1 +) keepers + ON dup.tenant_id = keepers.tenant_id + AND dup.custom_id = keepers.custom_id + AND dup.giff_id = keepers.giff_id +WHERE dup.id <> keepers.keep_id; + +-- Enforce non-null identifiers and counts +ALTER TABLE play_clerk_gift_info + MODIFY clerk_id varchar(32) NOT NULL, + MODIFY giff_id varchar(32) NOT NULL, + MODIFY giff_number bigint NOT NULL DEFAULT 0; + +ALTER TABLE play_custom_gift_info + MODIFY custom_id varchar(32) NOT NULL, + MODIFY giff_id varchar(32) NOT NULL, + MODIFY giff_number bigint NOT NULL DEFAULT 0; + +-- Create unique constraints for tenant scoped gift counters +ALTER TABLE play_clerk_gift_info + ADD CONSTRAINT uk_play_clerk_gift UNIQUE KEY (tenant_id, clerk_id, giff_id); + +ALTER TABLE play_custom_gift_info + ADD CONSTRAINT uk_play_custom_gift UNIQUE KEY (tenant_id, custom_id, giff_id);