GiftCardOrderServiceImpl.java 12 KB

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