GiftCardOrderServiceImpl.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package com.ylx.giftCard.service.impl;
  2. import cn.hutool.core.util.ObjectUtil;
  3. import cn.hutool.core.util.RandomUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  7. import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
  8. import com.ylx.common.core.domain.model.WxLoginUser;
  9. import com.ylx.common.exception.ServiceException;
  10. import com.ylx.common.utils.DateUtils;
  11. import com.ylx.giftCard.domain.GiftCard;
  12. import com.ylx.giftCard.domain.GiftCardOrder;
  13. import com.ylx.giftCard.domain.dto.GiftCardOrderQueryDTO;
  14. import com.ylx.giftCard.domain.dto.UserShoppingFundsDetailQueryDTO;
  15. import com.ylx.giftCard.domain.vo.GiftCardOrderExportVO;
  16. import com.ylx.giftCard.domain.vo.GiftCardOrderPageVO;
  17. import com.ylx.giftCard.domain.vo.UserShoppingFundsDetailItemVO;
  18. import com.ylx.giftCard.domain.vo.UserShoppingFundsDetailVO;
  19. import com.ylx.giftCard.domain.vo.UserShoppingFundsSummaryVO;
  20. import com.ylx.giftCard.enums.GiftCardOrderStatusEnum;
  21. import com.ylx.giftCard.mapper.GiftCardOrderMapper;
  22. import com.ylx.giftCard.service.IGiftCardOrderService;
  23. import com.ylx.massage.domain.TJs;
  24. import com.ylx.massage.domain.TWxUser;
  25. import com.ylx.massage.service.TJsService;
  26. import com.ylx.massage.service.TWxUserService;
  27. import com.ylx.shopingfundsdetail.domain.vo.ShoppingFundsDetailAddDto;
  28. import com.ylx.shopingfundsdetail.enums.ShoppingFundsExpenseTypeEnum;
  29. import com.ylx.shopingfundsdetail.mapper.ShoppingFundsDetailMapper;
  30. import com.ylx.shopingfundsdetail.service.ShoppingFundsDetailService;
  31. import lombok.extern.slf4j.Slf4j;
  32. import org.apache.commons.lang3.StringUtils;
  33. import org.springframework.stereotype.Service;
  34. import org.springframework.transaction.annotation.Transactional;
  35. import javax.annotation.Resource;
  36. import java.math.BigDecimal;
  37. import java.math.RoundingMode;
  38. import java.util.List;
  39. @Slf4j // 添加日志注解
  40. @Service
  41. public class GiftCardOrderServiceImpl extends ServiceImpl<GiftCardOrderMapper, GiftCardOrder> implements IGiftCardOrderService {
  42. @Resource
  43. private TJsService jsService;
  44. @Resource
  45. private TWxUserService wxUserService;
  46. @Resource
  47. private ShoppingFundsDetailService shoppingFundsDetailService;
  48. @Resource
  49. private ShoppingFundsDetailMapper shoppingFundsDetailMapper;
  50. private static final int DETAIL_TYPE_PURCHASE = 0;
  51. private static final int DETAIL_TYPE_EXPENSE = 1;
  52. @Override
  53. @Transactional(rollbackFor = Exception.class)
  54. public GiftCardOrder buildOrder(GiftCard card, Integer quantity, String merchantId, WxLoginUser wxLoginUser) {
  55. // 1. 参数校验
  56. if (ObjectUtil.isNull(card)) {
  57. throw new ServiceException("购物卡信息不能为空");
  58. }
  59. if (ObjectUtil.isNull(quantity) || quantity <= 0) {
  60. throw new ServiceException("购买数量必须大于0");
  61. }
  62. // 2. 创建订单对象
  63. GiftCardOrder order = new GiftCardOrder();
  64. // 4. 生成唯一订单号(使用更安全的方式)
  65. String orderNo = generateUniqueOrderNo();
  66. order.setOrderNo(orderNo);
  67. // 5. 设置购物卡信息
  68. setGiftCardInfo(order, card);
  69. // 6. 设置用户信息
  70. setUserInfo(order, wxLoginUser);
  71. // 7. 设置商户信息
  72. setMerchantInfo(order, merchantId);
  73. // 8. 计算金额
  74. calculateAmount(order, card, quantity);
  75. order.setPurchaseQuantity(quantity);
  76. // 9. 设置订单状态和时间
  77. order.setStatus(GiftCardOrderStatusEnum.WAIT_PAY.getCode()); // 待支付
  78. order.setCreateTime(DateUtils.getNowDate());
  79. order.setUpdateTime(order.getCreateTime());
  80. int rowsAffected = this.baseMapper.insert(order);
  81. if (rowsAffected <= 0) {
  82. log.warn("购物卡订单创建失败,购物卡ID: {},下单人ID: {}", card.getId(), wxLoginUser.getId());
  83. return null;
  84. }
  85. return order;
  86. }
  87. @Override
  88. @Transactional(rollbackFor = Exception.class)
  89. public void cancelOrder(Long id) {
  90. // 1. 查询订单
  91. GiftCardOrder order = this.getById(id);
  92. if (ObjectUtil.isNull(order)) {
  93. throw new ServiceException("订单不存在");
  94. }
  95. // 仅待支付订单可取消
  96. if (!GiftCardOrderStatusEnum.PAID.getCode().equals(order.getStatus())) {
  97. throw new ServiceException("当前订单状态不支持取消");
  98. }
  99. // 2. 修改订单状态为已取消
  100. order.setStatus(GiftCardOrderStatusEnum.CANCEL.getCode());
  101. order.setUpdateTime(DateUtils.getNowDate());
  102. boolean update = this.updateById(order);
  103. if (!update) {
  104. log.error("订单取消失败,订单号:{}", order.getOrderNo());
  105. throw new ServiceException("订单取消失败");
  106. }
  107. log.info("订单取消成功,订单号:{},购物卡ID:{}", order.getOrderNo(), order.getGiftCardId());
  108. }
  109. @Override
  110. @Transactional(rollbackFor = Exception.class)
  111. public void processGiftCardPayment(WxPayOrderNotifyV3Result.DecryptNotifyResult result, TWxUser wxUser,GiftCardOrder cardOrder) {
  112. // 更新订单状态
  113. this.lambdaUpdate()
  114. .set(GiftCardOrder::getStatus, GiftCardOrderStatusEnum.PAID.getCode())
  115. .eq(GiftCardOrder::getId, cardOrder.getId())
  116. .update();
  117. // 计算充值金额
  118. Integer totalCent = result.getAmount().getTotal();
  119. BigDecimal payAmount = new BigDecimal(totalCent).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
  120. // 更新用户余额
  121. BigDecimal oldBalance = wxUser.getdBalance();
  122. BigDecimal newBalance = oldBalance.add(payAmount);
  123. this.wxUserService.lambdaUpdate()
  124. .set(TWxUser::getdBalance, newBalance)
  125. .eq(TWxUser::getId, wxUser.getId())
  126. .update();
  127. // 记录购物金明细
  128. ShoppingFundsDetailAddDto dto = new ShoppingFundsDetailAddDto();
  129. dto.setUserId(wxUser.getId());
  130. dto.setAmount(payAmount);
  131. dto.setOrderNo(result.getOutTradeNo());
  132. dto.setExpenseType(ShoppingFundsExpenseTypeEnum.RECHARGE.getCode());
  133. dto.setBalance(newBalance);
  134. dto.setGiftCardId(cardOrder.getGiftCardId());
  135. shoppingFundsDetailService.addShoppingFundsDetail(dto);
  136. }
  137. @Override
  138. public Page<GiftCardOrderPageVO> getAdminGiftCardOrderPage(Page<GiftCardOrderPageVO> page, GiftCardOrderQueryDTO dto) {
  139. GiftCardOrderQueryDTO query = dto == null ? new GiftCardOrderQueryDTO() : dto;
  140. normalizeOrderTimeRange(query);
  141. return baseMapper.selectAdminGiftCardOrderPage(page, query);
  142. }
  143. @Override
  144. public List<GiftCardOrderExportVO> getAdminGiftCardOrderExportList(GiftCardOrderQueryDTO dto) {
  145. GiftCardOrderQueryDTO query = dto == null ? new GiftCardOrderQueryDTO() : dto;
  146. normalizeOrderTimeRange(query);
  147. return baseMapper.selectAdminGiftCardOrderExportList(query);
  148. }
  149. @Override
  150. public UserShoppingFundsDetailVO getPcUserShoppingFundsDetail(Page<UserShoppingFundsDetailItemVO> page, UserShoppingFundsDetailQueryDTO dto) {
  151. validateShoppingFundsDetailQuery(dto);
  152. normalizeTimeRange(dto);
  153. TWxUser user = wxUserService.getById(dto.getUserId());
  154. if (ObjectUtil.isNull(user)) {
  155. throw new IllegalArgumentException("用户不存在");
  156. }
  157. UserShoppingFundsSummaryVO summary;
  158. Page<UserShoppingFundsDetailItemVO> detailPage;
  159. // 支出
  160. if (DETAIL_TYPE_EXPENSE == dto.getDetailType()) {
  161. summary = shoppingFundsDetailMapper.selectPcExpenseShoppingFundsSummary(dto);
  162. detailPage = shoppingFundsDetailMapper.selectPcExpenseShoppingFundsDetail(page, dto);
  163. } else {
  164. // 购买
  165. summary = baseMapper.selectPcPurchaseShoppingFundsSummary(dto);
  166. detailPage = baseMapper.selectPcPurchaseShoppingFundsDetail(page, dto);
  167. }
  168. UserShoppingFundsDetailVO vo = new UserShoppingFundsDetailVO();
  169. vo.setShoppingFundsBalance(defaultAmount(user.getdBalance()));
  170. vo.setTotalAmount(defaultAmount(summary == null ? null : summary.getTotalAmount()));
  171. vo.setTotalCount(summary == null || summary.getTotalCount() == null ? 0L : summary.getTotalCount());
  172. vo.setPage(detailPage);
  173. return vo;
  174. }
  175. /**
  176. * 校验购物金明细查询参数
  177. * @param dto
  178. */
  179. private void validateShoppingFundsDetailQuery(UserShoppingFundsDetailQueryDTO dto) {
  180. if (dto.getDetailType() == null) {
  181. dto.setDetailType(DETAIL_TYPE_PURCHASE);
  182. }
  183. if (dto.getDetailType() != DETAIL_TYPE_PURCHASE && dto.getDetailType() != DETAIL_TYPE_EXPENSE) {
  184. throw new IllegalArgumentException("明细类型不正确");
  185. }
  186. }
  187. /**
  188. * 格式化时间范围
  189. * @param dto
  190. */
  191. private void normalizeTimeRange(UserShoppingFundsDetailQueryDTO dto) {
  192. if (StringUtils.isNotBlank(dto.getStartTime()) && dto.getStartTime().trim().length() == 10) {
  193. dto.setStartTime(dto.getStartTime().trim() + " 00:00:00");
  194. }
  195. if (StringUtils.isNotBlank(dto.getEndTime()) && dto.getEndTime().trim().length() == 10) {
  196. dto.setEndTime(dto.getEndTime().trim() + " 23:59:59");
  197. }
  198. }
  199. /**
  200. * 格式化购物卡订单查询时间范围
  201. * @param dto
  202. */
  203. private void normalizeOrderTimeRange(GiftCardOrderQueryDTO dto) {
  204. if (StringUtils.isNotBlank(dto.getStartTime()) && dto.getStartTime().trim().length() == 10) {
  205. dto.setStartTime(dto.getStartTime().trim() + " 00:00:00");
  206. }
  207. if (StringUtils.isNotBlank(dto.getEndTime()) && dto.getEndTime().trim().length() == 10) {
  208. dto.setEndTime(dto.getEndTime().trim() + " 23:59:59");
  209. }
  210. }
  211. private BigDecimal defaultAmount(BigDecimal amount) {
  212. return amount == null ? BigDecimal.ZERO : amount;
  213. }
  214. /**
  215. * 生成唯一订单号
  216. */
  217. private String generateUniqueOrderNo() {
  218. // 使用时间戳 + 随机数 + 更多信息避免冲突
  219. long timestamp = System.currentTimeMillis();
  220. String randomNum = RandomUtil.randomNumbers(6);
  221. // 可以加入用户ID后几位、线程ID等进一步降低冲突概率
  222. String suffix = String.valueOf(timestamp % 1000000).substring(0, 3); // 取时间戳后3位
  223. return "GC" + timestamp + randomNum + suffix;
  224. }
  225. /**
  226. * 设置购物卡信息
  227. */
  228. private void setGiftCardInfo(GiftCardOrder order, GiftCard card) {
  229. order.setGiftCardId(card.getId());
  230. order.setGiftCardName(card.getName());
  231. order.setGiftCardAmount(card.getAmount());
  232. order.setCommissionRate(card.getCommissionRate());
  233. }
  234. /**
  235. * 设置用户信息
  236. */
  237. private void setUserInfo(GiftCardOrder order, WxLoginUser wxLoginUser) {
  238. order.setUserId(wxLoginUser.getId());
  239. order.setUserName(wxLoginUser.getCNickName());
  240. order.setUserPhone(wxLoginUser.getCPhone());
  241. }
  242. /**
  243. * 设置商户信息
  244. */
  245. private void setMerchantInfo(GiftCardOrder order, String merchantId) {
  246. if (StrUtil.isEmpty(merchantId)) {
  247. log.warn("商户ID为空,跳过商户信息查询");
  248. return;
  249. }
  250. TJs merchant = this.jsService.getById(merchantId);
  251. if (ObjectUtil.isNotNull(merchant)) {
  252. order.setMerchantId(merchantId);
  253. order.setMerchantName(merchant.getcName());
  254. order.setMerchantNickName(merchant.getcNickName());
  255. // 注意:如果 TJs 表有收款账号字段,可以在这里设置
  256. // order.setMerchantAccount(merchant.getAccount());
  257. }
  258. }
  259. /**
  260. * 计算金额
  261. */
  262. private void calculateAmount(GiftCardOrder order, GiftCard card, Integer quantity) {
  263. BigDecimal payAmount = card.getAmount()
  264. .multiply(new BigDecimal(quantity))
  265. .setScale(2, RoundingMode.HALF_UP); // 保留两位小数
  266. order.setPayAmount(payAmount);
  267. BigDecimal commissionAmount = payAmount
  268. .multiply(card.getCommissionRate())
  269. .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
  270. order.setCommissionAmount(commissionAmount);
  271. }
  272. }