refactor: 盲盒功能代码优化和完善
修复和改进:
- 修复字段映射:blind_box_gift_id -> blind_box_id
- 移除不必要的 @Version 乐观锁字段
- 优化 Mapper 方法:统一使用 listActiveEntries,简化查询逻辑
- 新增客户端接口:盲盒购买、奖励查询和兑现
- 增强权限校验:奖励兑现时验证客户身份
- 完善单元测试:增加客户身份验证测试用例
- 代码格式化:调整 import 顺序,优化代码结构
客户端 API:
- GET /wx/blind-box/config/list - 查询可用盲盒列表
- POST /wx/blind-box/order/purchase - 购买盲盒
- GET /wx/blind-box/reward/list - 查询我的盲盒奖励
- POST /wx/blind-box/reward/{id}/dispatch - 兑现盲盒奖励
其他优化:
- 增强 SQL 查询安全性,添加 deleted 字段过滤
- 优化店员提成计算逻辑
- 改进参数可选性(levelId 参数改为可选)
This commit is contained in:
@@ -4,8 +4,8 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxConfigEntity;
|
||||
import com.starry.admin.modules.blindbox.module.constant.BlindBoxConfigStatus;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxConfigEntity;
|
||||
import com.starry.admin.modules.blindbox.service.BlindBoxConfigService;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import com.starry.common.result.R;
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.starry.admin.modules.blindbox.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.starry.admin.modules.blindbox.module.dto.BlindBoxCandidate;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxPoolEntity;
|
||||
import com.starry.admin.modules.blindbox.module.constant.BlindBoxPoolStatus;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@@ -17,24 +16,24 @@ public interface BlindBoxPoolMapper extends BaseMapper<BlindBoxPoolEntity> {
|
||||
@Select({
|
||||
"SELECT id AS poolId,",
|
||||
" tenant_id AS tenantId,",
|
||||
" blind_box_gift_id AS blindBoxId,",
|
||||
" blind_box_id AS blindBoxId,",
|
||||
" reward_gift_id AS rewardGiftId,",
|
||||
" reward_price AS rewardPrice,",
|
||||
" weight,",
|
||||
" remaining_stock AS remainingStock",
|
||||
"FROM blind_box_pool",
|
||||
"WHERE tenant_id = #{tenantId}",
|
||||
" AND blind_box_gift_id = #{blindBoxId}",
|
||||
" AND status = #{enabledStatus}",
|
||||
" AND blind_box_id = #{blindBoxId}",
|
||||
" AND status = 1",
|
||||
" AND deleted = 0",
|
||||
" AND (valid_from IS NULL OR valid_from <= #{now})",
|
||||
" AND (valid_to IS NULL OR valid_to >= #{now})",
|
||||
" AND (remaining_stock IS NULL OR remaining_stock > 0)"
|
||||
})
|
||||
List<BlindBoxCandidate> listEntries(
|
||||
List<BlindBoxCandidate> listActiveEntries(
|
||||
@Param("tenantId") String tenantId,
|
||||
@Param("blindBoxId") String blindBoxId,
|
||||
@Param("now") LocalDateTime now,
|
||||
@Param("enabledStatus") int enabledStatus);
|
||||
@Param("now") LocalDateTime now);
|
||||
|
||||
@Update({
|
||||
"UPDATE blind_box_pool",
|
||||
@@ -45,6 +44,7 @@ public interface BlindBoxPoolMapper extends BaseMapper<BlindBoxPoolEntity> {
|
||||
"WHERE tenant_id = #{tenantId}",
|
||||
" AND id = #{poolId}",
|
||||
" AND reward_gift_id = #{rewardGiftId}",
|
||||
" AND deleted = 0",
|
||||
" AND (remaining_stock IS NULL OR remaining_stock > 0)"
|
||||
})
|
||||
int consumeRewardStock(
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.starry.admin.modules.blindbox.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxRewardEntity;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
@@ -29,4 +30,17 @@ public interface BlindBoxRewardMapper extends BaseMapper<BlindBoxRewardEntity> {
|
||||
@Param("clerkId") String clerkId,
|
||||
@Param("orderId") String orderId,
|
||||
@Param("usedTime") LocalDateTime usedTime);
|
||||
|
||||
@Select({
|
||||
"SELECT * FROM blind_box_reward",
|
||||
"WHERE tenant_id = #{tenantId}",
|
||||
" AND customer_id = #{customerId}",
|
||||
" AND deleted = 0",
|
||||
" AND (#{status} IS NULL OR status = #{status})",
|
||||
"ORDER BY created_time DESC"
|
||||
})
|
||||
List<BlindBoxRewardEntity> listByCustomer(
|
||||
@Param("tenantId") String tenantId,
|
||||
@Param("customerId") String customerId,
|
||||
@Param("status") String status);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.starry.admin.modules.blindbox.module.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.Version;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.starry.common.domain.BaseEntity;
|
||||
import java.math.BigDecimal;
|
||||
@@ -20,7 +19,7 @@ public class BlindBoxRewardEntity extends BaseEntity<BlindBoxRewardEntity> {
|
||||
private String tenantId;
|
||||
private String customerId;
|
||||
|
||||
@TableField("blind_box_gift_id")
|
||||
@TableField("blind_box_id")
|
||||
private String blindBoxId;
|
||||
private String rewardGiftId;
|
||||
private BigDecimal rewardPrice;
|
||||
@@ -42,6 +41,4 @@ public class BlindBoxRewardEntity extends BaseEntity<BlindBoxRewardEntity> {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime usedTime;
|
||||
|
||||
@Version
|
||||
private Integer version;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||
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.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
@@ -26,6 +27,7 @@ public class BlindBoxDispatchService {
|
||||
private final IOrderLifecycleService orderLifecycleService;
|
||||
private final IPlayOrderInfoService orderInfoService;
|
||||
private final IPlayGiftInfoService giftInfoService;
|
||||
private final IPlayClerkGiftInfoService clerkGiftInfoService;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public OrderPlacementResult dispatchRewardOrder(BlindBoxRewardEntity reward, String clerkId) {
|
||||
@@ -71,9 +73,13 @@ public class BlindBoxDispatchService {
|
||||
.remark("盲盒奖励兑现")
|
||||
.build();
|
||||
|
||||
return orderLifecycleService.placeOrder(OrderPlacementCommand.builder()
|
||||
OrderPlacementResult result = orderLifecycleService.placeOrder(OrderPlacementCommand.builder()
|
||||
.orderContext(context)
|
||||
.balanceOperationAction("盲盒奖励兑现")
|
||||
.build());
|
||||
if (clerkId != null) {
|
||||
clerkGiftInfoService.incrementGiftCount(clerkId, giftInfo.getId(), reward.getTenantId(), 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.starry.admin.modules.blindbox.service;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.blindbox.mapper.BlindBoxPoolMapper;
|
||||
import com.starry.admin.modules.blindbox.mapper.BlindBoxRewardMapper;
|
||||
import com.starry.admin.modules.blindbox.module.constant.BlindBoxPoolStatus;
|
||||
import com.starry.admin.modules.blindbox.module.constant.BlindBoxRewardStatus;
|
||||
import com.starry.admin.modules.blindbox.module.dto.BlindBoxCandidate;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxConfigEntity;
|
||||
@@ -76,11 +75,7 @@ public class BlindBoxService {
|
||||
if (!tenantId.equals(config.getTenantId())) {
|
||||
throw new CustomException("盲盒不存在或已下架");
|
||||
}
|
||||
List<BlindBoxCandidate> candidates = poolMapper.listEntries(
|
||||
tenantId,
|
||||
blindBoxId,
|
||||
now,
|
||||
BlindBoxPoolStatus.ENABLED.getCode());
|
||||
List<BlindBoxCandidate> candidates = poolMapper.listActiveEntries(tenantId, blindBoxId, now);
|
||||
if (CollectionUtils.isEmpty(candidates)) {
|
||||
throw new CustomException("盲盒奖池暂无可用奖励");
|
||||
}
|
||||
@@ -111,15 +106,18 @@ public class BlindBoxService {
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public OrderPlacementResult dispatchReward(String rewardId, String clerkId) {
|
||||
public OrderPlacementResult dispatchReward(String rewardId, String clerkId, String customerId) {
|
||||
BlindBoxRewardEntity reward = rewardMapper.lockByIdForUpdate(rewardId);
|
||||
if (reward == null) {
|
||||
throw new CustomException("盲盒奖励不存在");
|
||||
}
|
||||
if (customerId != null && !customerId.equals(reward.getCustomerId())) {
|
||||
throw new CustomException("无权操作该盲盒奖励");
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now(clock);
|
||||
if (!BlindBoxRewardStatus.UNUSED.getCode().equals(reward.getStatus())) {
|
||||
throw new CustomException("盲盒奖励已使用");
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now(clock);
|
||||
if (reward.getExpiresAt() != null && reward.getExpiresAt().isBefore(now)) {
|
||||
throw new CustomException("盲盒奖励已过期");
|
||||
}
|
||||
@@ -160,6 +158,10 @@ public class BlindBoxService {
|
||||
: value.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
public java.util.List<BlindBoxRewardEntity> listRewards(String tenantId, String customerId, String status) {
|
||||
return rewardMapper.listByCustomer(tenantId, customerId, status);
|
||||
}
|
||||
|
||||
public interface RandomAdapter {
|
||||
double nextDouble();
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package com.starry.admin.modules.blindbox.service.impl;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.blindbox.mapper.BlindBoxConfigMapper;
|
||||
import com.starry.admin.modules.blindbox.module.constant.BlindBoxConfigStatus;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxConfigEntity;
|
||||
import com.starry.admin.modules.blindbox.service.BlindBoxConfigService;
|
||||
import java.util.List;
|
||||
import com.starry.admin.modules.blindbox.module.constant.BlindBoxConfigStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.starry.admin.modules.weichat.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
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.blindbox.module.entity.BlindBoxConfigEntity;
|
||||
import com.starry.admin.modules.blindbox.service.BlindBoxConfigService;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxConfigView;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxPurchaseRequest;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxPurchaseResult;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxRewardDispatchRequest;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxRewardView;
|
||||
import com.starry.admin.modules.weichat.service.WxBlindBoxOrderService;
|
||||
import com.starry.common.result.R;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/wx/blind-box")
|
||||
@Validated
|
||||
public class WxBlindBoxController {
|
||||
|
||||
@Resource
|
||||
private BlindBoxConfigService blindBoxConfigService;
|
||||
|
||||
@Resource
|
||||
private WxBlindBoxOrderService wxBlindBoxOrderService;
|
||||
|
||||
@CustomUserLogin
|
||||
@GetMapping("/config/list")
|
||||
public R listConfigs() {
|
||||
PlayCustomUserInfoEntity user = ThreadLocalRequestDetail.getCustomUserInfo();
|
||||
if (user == null) {
|
||||
throw new CustomException("用户未登录");
|
||||
}
|
||||
List<BlindBoxConfigEntity> configs = blindBoxConfigService.listActiveByTenant(user.getTenantId());
|
||||
List<BlindBoxConfigView> views = CollUtil.emptyIfNull(configs).stream()
|
||||
.map(this::toConfigView)
|
||||
.collect(Collectors.toList());
|
||||
return R.ok(views);
|
||||
}
|
||||
|
||||
@CustomUserLogin
|
||||
@PostMapping("/order/purchase")
|
||||
public R purchase(@Valid @RequestBody BlindBoxPurchaseRequest request) {
|
||||
BlindBoxPurchaseResult result = wxBlindBoxOrderService.purchase(request);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@CustomUserLogin
|
||||
@PostMapping("/reward/{id}/dispatch")
|
||||
public R dispatch(@PathVariable("id") String rewardId, @Valid @RequestBody BlindBoxRewardDispatchRequest body) {
|
||||
BlindBoxRewardView view = wxBlindBoxOrderService.dispatchReward(rewardId, body.getClerkId());
|
||||
return R.ok(view);
|
||||
}
|
||||
|
||||
@CustomUserLogin
|
||||
@GetMapping("/reward/list")
|
||||
public R listRewards(@RequestParam(value = "status", required = false) String status) {
|
||||
PlayCustomUserInfoEntity user = ThreadLocalRequestDetail.getCustomUserInfo();
|
||||
if (user == null) {
|
||||
throw new CustomException("用户未登录");
|
||||
}
|
||||
List<BlindBoxRewardView> rewards = wxBlindBoxOrderService.listRewards(user.getTenantId(), user.getId(), status);
|
||||
return R.ok(rewards);
|
||||
}
|
||||
|
||||
private BlindBoxConfigView toConfigView(BlindBoxConfigEntity entity) {
|
||||
BlindBoxConfigView view = new BlindBoxConfigView();
|
||||
view.setId(entity.getId());
|
||||
view.setName(entity.getName());
|
||||
view.setCoverUrl(entity.getCoverUrl());
|
||||
view.setDescription(entity.getDescription());
|
||||
view.setPrice(entity.getPrice());
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class WxClerkCommodityController {
|
||||
@ApiResponses({
|
||||
@ApiResponse(code = 200, message = "操作成功", response = PlayCommodityReturnVo.class, responseContainer = "List")})
|
||||
@GetMapping("/custom/queryClerkAllCommodityByLevel")
|
||||
public R queryClerkAllCommodityByLevel(@RequestParam("id") String levelId) {
|
||||
public R queryClerkAllCommodityByLevel(@RequestParam(value = "id", required = false) String levelId) {
|
||||
List<PlayCommodityAndLevelInfoEntity> levelInfoEntities = iPlayCommodityAndLevelInfoService.selectAll();
|
||||
List<PlayCommodityReturnVo> tree = playCommodityInfoService.selectTree();
|
||||
if (levelId == null || levelId.isEmpty()) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.starry.admin.modules.weichat.entity.blindbox;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 盲盒配置前端视图。
|
||||
*/
|
||||
@Data
|
||||
public class BlindBoxConfigView {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String coverUrl;
|
||||
|
||||
private String description;
|
||||
|
||||
private BigDecimal price;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.starry.admin.modules.weichat.entity.blindbox;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BlindBoxPurchaseRequest {
|
||||
|
||||
@NotBlank(message = "盲盒ID不能为空")
|
||||
private String blindBoxId;
|
||||
|
||||
@NotBlank(message = "店员ID不能为空")
|
||||
private String clerkId;
|
||||
|
||||
private String weiChatCode;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.starry.admin.modules.weichat.entity.blindbox;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class BlindBoxPurchaseResult {
|
||||
|
||||
private String orderId;
|
||||
|
||||
private BlindBoxRewardInfo reward;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class BlindBoxRewardInfo {
|
||||
|
||||
private String rewardId;
|
||||
|
||||
private String blindBoxId;
|
||||
|
||||
private String blindBoxName;
|
||||
|
||||
private String blindBoxCover;
|
||||
|
||||
private String rewardGiftId;
|
||||
|
||||
private String rewardGiftName;
|
||||
|
||||
private String rewardGiftImage;
|
||||
|
||||
private BigDecimal rewardGiftPrice;
|
||||
|
||||
private BigDecimal boxPrice;
|
||||
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
private String status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.starry.admin.modules.weichat.entity.blindbox;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BlindBoxRewardDispatchRequest {
|
||||
|
||||
@NotBlank(message = "店员ID不能为空")
|
||||
private String clerkId;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.starry.admin.modules.weichat.entity.blindbox;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class BlindBoxRewardView {
|
||||
|
||||
private String id;
|
||||
|
||||
private String blindBoxId;
|
||||
|
||||
private String blindBoxName;
|
||||
|
||||
private String blindBoxCover;
|
||||
|
||||
private String rewardGiftId;
|
||||
|
||||
private String rewardGiftName;
|
||||
|
||||
private String rewardGiftImage;
|
||||
|
||||
private BigDecimal rewardGiftPrice;
|
||||
|
||||
private BigDecimal boxPrice;
|
||||
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
private String status;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
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.modules.blindbox.mapper.BlindBoxRewardMapper;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxConfigEntity;
|
||||
import com.starry.admin.modules.blindbox.module.entity.BlindBoxRewardEntity;
|
||||
import com.starry.admin.modules.blindbox.service.BlindBoxConfigService;
|
||||
import com.starry.admin.modules.blindbox.service.BlindBoxService;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
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.OrderActor;
|
||||
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.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||
import com.starry.admin.modules.shop.mapper.PlayGiftInfoMapper;
|
||||
import com.starry.admin.modules.shop.module.entity.PlayGiftInfoEntity;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxPurchaseRequest;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxPurchaseResult;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxPurchaseResult.BlindBoxRewardInfo;
|
||||
import com.starry.admin.modules.weichat.entity.blindbox.BlindBoxRewardView;
|
||||
import com.starry.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class WxBlindBoxOrderService {
|
||||
|
||||
@Resource
|
||||
private BlindBoxConfigService blindBoxConfigService;
|
||||
|
||||
@Resource
|
||||
private BlindBoxService blindBoxService;
|
||||
|
||||
@Resource
|
||||
private IOrderLifecycleService orderLifecycleService;
|
||||
|
||||
@Resource
|
||||
private IPlayOrderInfoService playOrderInfoService;
|
||||
|
||||
@Resource
|
||||
private IPlayCustomUserInfoService customUserInfoService;
|
||||
|
||||
@Resource
|
||||
private PlayGiftInfoMapper playGiftInfoMapper;
|
||||
|
||||
@Resource
|
||||
private BlindBoxRewardMapper blindBoxRewardMapper;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public BlindBoxPurchaseResult purchase(BlindBoxPurchaseRequest request) {
|
||||
PlayCustomUserInfoEntity sessionUser = ThreadLocalRequestDetail.getCustomUserInfo();
|
||||
if (sessionUser == null || sessionUser.getId() == null) {
|
||||
throw new CustomException("用户未登录");
|
||||
}
|
||||
PlayCustomUserInfoEntity customer = customUserInfoService.selectById(sessionUser.getId());
|
||||
if (customer == null) {
|
||||
throw new CustomException("用户不存在");
|
||||
}
|
||||
|
||||
BlindBoxConfigEntity config = blindBoxConfigService.requireById(request.getBlindBoxId());
|
||||
if (!Objects.equals(config.getTenantId(), customer.getTenantId())) {
|
||||
throw new CustomException("盲盒不存在或已下架");
|
||||
}
|
||||
if (!Objects.equals(config.getStatus(), 1)) {
|
||||
throw new CustomException("盲盒已下架");
|
||||
}
|
||||
|
||||
BigDecimal boxPrice = Objects.requireNonNull(config.getPrice(), "盲盒价格未配置");
|
||||
if (boxPrice.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new CustomException("盲盒价格异常");
|
||||
}
|
||||
|
||||
String orderId = IdUtils.getUuid();
|
||||
OrderCreationContext orderRequest = buildOrderRequest(orderId, request, customer, config, boxPrice);
|
||||
OrderPlacementResult result = orderLifecycleService.placeOrder(OrderPlacementCommand.builder()
|
||||
.orderContext(orderRequest)
|
||||
.balanceOperationAction("购买盲盒")
|
||||
.build());
|
||||
PlayOrderInfoEntity order = result.getOrder();
|
||||
|
||||
BlindBoxRewardEntity reward = blindBoxService.drawReward(
|
||||
customer.getTenantId(),
|
||||
orderId,
|
||||
customer.getId(),
|
||||
config.getId(),
|
||||
UUID.randomUUID().toString());
|
||||
|
||||
PlayGiftInfoEntity giftInfo = playGiftInfoMapper.selectById(reward.getRewardGiftId());
|
||||
|
||||
return BlindBoxPurchaseResult.builder()
|
||||
.orderId(order.getId())
|
||||
.reward(buildRewardInfo(reward, config, giftInfo))
|
||||
.build();
|
||||
}
|
||||
|
||||
public List<BlindBoxRewardView> listRewards(String tenantId, String customerId, String status) {
|
||||
return blindBoxService.listRewards(tenantId, customerId, status).stream()
|
||||
.map(this::toRewardView)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public BlindBoxRewardView dispatchReward(String rewardId, String clerkId) {
|
||||
PlayCustomUserInfoEntity sessionUser = ThreadLocalRequestDetail.getCustomUserInfo();
|
||||
if (sessionUser == null || sessionUser.getId() == null) {
|
||||
throw new CustomException("用户未登录");
|
||||
}
|
||||
blindBoxService.dispatchReward(rewardId, clerkId, sessionUser.getId());
|
||||
BlindBoxRewardEntity updated = blindBoxRewardMapper.selectById(rewardId);
|
||||
if (updated == null) {
|
||||
throw new CustomException("盲盒奖励不存在");
|
||||
}
|
||||
return toRewardView(updated);
|
||||
}
|
||||
|
||||
public BlindBoxRewardView toRewardView(BlindBoxRewardEntity reward) {
|
||||
BlindBoxConfigEntity config = blindBoxConfigService.getById(reward.getBlindBoxId());
|
||||
PlayGiftInfoEntity gift = playGiftInfoMapper.selectById(reward.getRewardGiftId());
|
||||
return BlindBoxRewardView.builder()
|
||||
.id(reward.getId())
|
||||
.blindBoxId(reward.getBlindBoxId())
|
||||
.blindBoxName(config != null ? config.getName() : null)
|
||||
.blindBoxCover(config != null ? config.getCoverUrl() : null)
|
||||
.rewardGiftId(reward.getRewardGiftId())
|
||||
.rewardGiftName(gift != null ? gift.getName() : reward.getRewardGiftId())
|
||||
.rewardGiftImage(gift != null ? gift.getUrl() : null)
|
||||
.rewardGiftPrice(reward.getRewardPrice())
|
||||
.boxPrice(reward.getBoxPrice())
|
||||
.expiresAt(reward.getExpiresAt())
|
||||
.status(reward.getStatus())
|
||||
.build();
|
||||
}
|
||||
|
||||
private BlindBoxRewardInfo buildRewardInfo(
|
||||
BlindBoxRewardEntity reward,
|
||||
BlindBoxConfigEntity config,
|
||||
PlayGiftInfoEntity giftInfo) {
|
||||
return BlindBoxRewardInfo.builder()
|
||||
.rewardId(reward.getId())
|
||||
.blindBoxId(reward.getBlindBoxId())
|
||||
.blindBoxName(config != null ? config.getName() : null)
|
||||
.blindBoxCover(config != null ? config.getCoverUrl() : null)
|
||||
.rewardGiftId(reward.getRewardGiftId())
|
||||
.rewardGiftName(giftInfo != null ? giftInfo.getName() : reward.getRewardGiftId())
|
||||
.rewardGiftImage(giftInfo != null ? giftInfo.getUrl() : null)
|
||||
.rewardGiftPrice(reward.getRewardPrice())
|
||||
.boxPrice(reward.getBoxPrice())
|
||||
.expiresAt(reward.getExpiresAt())
|
||||
.status(reward.getStatus())
|
||||
.build();
|
||||
}
|
||||
|
||||
private OrderCreationContext buildOrderRequest(
|
||||
String orderId,
|
||||
BlindBoxPurchaseRequest request,
|
||||
PlayCustomUserInfoEntity customer,
|
||||
BlindBoxConfigEntity config,
|
||||
BigDecimal boxPrice) {
|
||||
|
||||
return OrderCreationContext.builder()
|
||||
.orderId(orderId)
|
||||
.orderNo(playOrderInfoService.getOrderNo())
|
||||
.orderStatus(OrderConstant.OrderStatus.COMPLETED)
|
||||
.orderType(OrderConstant.OrderType.BLIND_BOX_PURCHASE)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(RewardType.GIFT)
|
||||
.isFirstOrder(false)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(customer.getId())
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId(config.getId())
|
||||
.commodityType(OrderConstant.CommodityType.GIFT)
|
||||
.commodityPrice(config.getPrice())
|
||||
.commodityName(config.getName())
|
||||
.commodityNumber("1")
|
||||
.serviceDuration("")
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(boxPrice)
|
||||
.finalAmount(boxPrice)
|
||||
.discountAmount(BigDecimal.ZERO)
|
||||
.couponIds(Collections.emptyList())
|
||||
.payMethod(OrderConstant.PayMethod.BALANCE.getCode())
|
||||
.paymentSource(OrderConstant.PaymentSource.BALANCE)
|
||||
.build())
|
||||
.purchaserBy(customer.getId())
|
||||
.acceptBy(request.getClerkId())
|
||||
.weiChatCode(request.getWeiChatCode())
|
||||
.remark(request.getRemark())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user