RegisterOrderDonServiceImpl.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. package com.cyksj.service.register.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.date.DateTime;
  4. import cn.hutool.core.date.DateUtil;
  5. import cn.hutool.core.util.StrUtil;
  6. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  7. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  8. import com.cyksj.common.exception.BusinessRuntimeException;
  9. import com.cyksj.common.snowflake.Sequence;
  10. import com.cyksj.common.util.Codec;
  11. import com.cyksj.common.util.Jsons;
  12. import com.cyksj.common.util.Xmls;
  13. import com.cyksj.config.WeChatConfig;
  14. import com.cyksj.dto.RedisKey;
  15. import com.cyksj.dto.Result;
  16. import com.cyksj.enums.GatewayResponse;
  17. import com.cyksj.mapper.AppleAutoRegisterDataMapper;
  18. import com.cyksj.mapper.RegisterOrderDonMapper;
  19. import com.cyksj.mapper.SpotifyUnLimitRecordMapper;
  20. import com.cyksj.mapper.UserMapper;
  21. import com.cyksj.mapper.channel.ShopConfigMapper;
  22. import com.cyksj.mapper.manage.coupon.CouponMapper;
  23. import com.cyksj.model.dto.H5JsPayParams;
  24. import com.cyksj.model.entity.*;
  25. import com.cyksj.model.manage.request.RegisterReq;
  26. import com.cyksj.model.request.OrderAttach;
  27. import com.cyksj.model.request.OrderRefundReq;
  28. import com.cyksj.model.views.CouponUserView;
  29. import com.cyksj.redis.RedisService;
  30. import com.cyksj.service.mange.coupon.CouponCommonService;
  31. import com.cyksj.service.register.RegisterOrderDonService;
  32. import com.cyksj.service.register.SpotifyService;
  33. import com.cyksj.service.sys.SysConfigService;
  34. import com.cyksj.service.wechat.WeChatService;
  35. import com.cyksj.task.JobManager;
  36. import com.ejlchina.searcher.BeanSearcher;
  37. import com.ejlchina.searcher.param.Operator;
  38. import com.ejlchina.searcher.util.MapUtils;
  39. import lombok.RequiredArgsConstructor;
  40. import lombok.extern.slf4j.Slf4j;
  41. import org.apache.commons.lang3.StringUtils;
  42. import org.springframework.dao.DuplicateKeyException;
  43. import org.springframework.stereotype.Service;
  44. import org.springframework.transaction.annotation.Transactional;
  45. import java.math.BigDecimal;
  46. import java.util.Map;
  47. import java.util.Optional;
  48. /**
  49. * @author chan
  50. * @date 2022/11/7 2:39 PM
  51. */
  52. @Service
  53. @Slf4j
  54. @RequiredArgsConstructor
  55. public class RegisterOrderDonServiceImpl extends ServiceImpl<RegisterOrderDonMapper ,RegisterOrderDon> implements RegisterOrderDonService {
  56. private static final Sequence SEQUENCE = new Sequence(0);
  57. private static final String REGISTER_PRICE_KEY = "register_price";
  58. private final SpotifyService spotifyService;
  59. private final RedisService redisService;
  60. private final WeChatService weChatService;
  61. private final WeChatConfig weChatConfig;
  62. private final SysConfigService sysConfigService;
  63. private final UserMapper userMapper;
  64. private final AppleAutoRegisterDataMapper appleAutoRegisterDataMapper;
  65. private final JobManager jobManager;
  66. private final RegisterOrderDonMapper registerOrderDonMapper;
  67. private final CouponMapper couponMapper;
  68. private final CouponCommonService couponCommonService;
  69. private final SpotifyUnLimitRecordMapper spotifyUnLimitRecordMapper;
  70. private final BeanSearcher beanSearcher;
  71. private final ShopConfigMapper shopConfigMapper;
  72. private static final Long EXPIRY_TIME = 1000 * 60 * 5L;
  73. /**
  74. * 判断是否免费
  75. * @author chan
  76. * @date 2022-11-07 3:34 PM
  77. */
  78. @Override
  79. public boolean isFree(long userId, String platform) {
  80. int count = count(Wrappers.lambdaQuery(RegisterOrderDon.class)
  81. .eq(RegisterOrderDon::getUserId, userId)
  82. .ne(RegisterOrderDon::getStatus, RegisterOrderDon.Status.noPayment)
  83. .eq(RegisterOrderDon::getIsFree, true));
  84. if(count > 0){
  85. return false;
  86. }
  87. Long num = redisService.decr(RedisKey.SPOTIFY_FREE_NUM, 1L);
  88. if(num >= 0){
  89. return true;
  90. }else {
  91. redisService.incr(RedisKey.SPOTIFY_FREE_NUM, 1L);
  92. }
  93. return false;
  94. }
  95. /**
  96. * 保存注册资料
  97. * @author chan
  98. * @date 2022-11-07 3:34 PM
  99. */
  100. @Override
  101. @Transactional(rollbackFor = Throwable.class)
  102. public RegisterOrderDon saveOrder(long userId, RegisterReq req, boolean isFree) throws Exception {
  103. RegisterOrderDon orderDon = new RegisterOrderDon();
  104. User user = userMapper.selectById(userId);
  105. BeanUtil.copyProperties(req,orderDon);
  106. SysConfig register_price = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "register_price").last("limit 1"));
  107. Map<String, Integer> map = Jsons.toMap(register_price.getSysValue());
  108. orderDon.setOrderNo(SEQUENCE.nextId() + "");
  109. orderDon.setUserId(userId);
  110. orderDon.setOpenId(user.getOpenId());
  111. orderDon.setIsFree(isFree);
  112. orderDon.setMoney(isFree ? BigDecimal.ZERO : BigDecimal.valueOf(("apple".equals(req.getPlatform()) && "auto".equals(req.getAppleRegisterType())) ? map.get("appleAuto") : map.get(req.getPlatform())));
  113. orderDon.setStatus(isFree ? RegisterOrderDon.Status.hasPayment : RegisterOrderDon.Status.noPayment);
  114. save(orderDon);
  115. //apple
  116. handleAppleIdAuto(userId, req, orderDon);
  117. return orderDon;
  118. }
  119. /**
  120. * Apple ID自动注册
  121. */
  122. private void handleAppleIdAuto(long userId, RegisterReq req, RegisterOrderDon orderDon) {
  123. if ("apple".equals(req.getPlatform()) && "auto".equals(req.getAppleRegisterType())) {
  124. //是否有未支付的国家或区域的订单
  125. Integer exists = appleAutoRegisterDataMapper.checkNoPayOrder(userId, req.getCountryOrArea());
  126. if (exists != null) {
  127. throw BusinessRuntimeException.getInstance(String.format("您有%s随机生成Apple ID未支付的订单", req.getCountryOrArea().getDesc()));
  128. }
  129. if (req.getCountryOrArea() == null) {
  130. throw BusinessRuntimeException.getInstance("请选择对应的国家或地区");
  131. }
  132. //查看是否有空余账号
  133. AppleAutoRegisterData appleAutoRegisterData = appleAutoRegisterDataMapper.selectOne(Wrappers.lambdaQuery(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getCountryOrArea, req.getCountryOrArea()).eq(AppleAutoRegisterData::getStatus, false).select(AppleAutoRegisterData::getId).last("limit 1"));
  134. if (appleAutoRegisterData == null) {
  135. throw BusinessRuntimeException.getInstance("自动生成Apple ID库存不足");
  136. }
  137. appleAutoRegisterData.setUserId(userId);
  138. appleAutoRegisterData.setStatus(true);
  139. appleAutoRegisterData.setOrderId(orderDon.getId());
  140. int update = appleAutoRegisterDataMapper.update(appleAutoRegisterData, Wrappers.lambdaUpdate(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getStatus, false).eq(AppleAutoRegisterData::getId, appleAutoRegisterData.getId()));
  141. if (update != 1) {
  142. throw BusinessRuntimeException.getInstance("系统异常,请重新操作");
  143. }
  144. jobManager.addJob(EXPIRY_TIME, () -> {
  145. updateAppleAutoStatus(orderDon.getId());
  146. });
  147. }
  148. }
  149. @Override
  150. public void updateAppleAutoStatus(Long orderId) {
  151. log.info("校验appleID自动注册订单 orderId:{}", orderId);
  152. RegisterOrderDon registerOrderDon = this.getById(orderId);
  153. if (registerOrderDon != null && RegisterOrderDon.Status.noPayment == registerOrderDon.getStatus()) {
  154. AppleAutoRegisterData appleAutoRegisterData = appleAutoRegisterDataMapper.selectOne(Wrappers.lambdaQuery(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getOrderId, orderId).last("limit 1"));
  155. if (appleAutoRegisterData != null) {
  156. appleAutoRegisterData.setStatus(false);
  157. appleAutoRegisterData.setUserId(0l);
  158. appleAutoRegisterData.setOrderId(0l);
  159. appleAutoRegisterDataMapper.updateById(appleAutoRegisterData);
  160. //订单关闭
  161. registerOrderDon.setStatus(RegisterOrderDon.Status.close);
  162. registerOrderDonMapper.updateById(registerOrderDon);
  163. log.info("未支付appleID自动注册订单:{},修改appleID使用状态", orderId);
  164. }
  165. }
  166. }
  167. @Override
  168. public CouponUserView spotifyUnLimit(SpotifyUnLimitRecord spotifyUnLimitRecord) {
  169. Boolean isFlag = false;
  170. Long spotifyUnLimitCouponId = null;
  171. //是否配置了优惠券
  172. SysConfig spotify_unLimit_cid = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "spotify_unLimit_cid").last("limit 1"));
  173. if (spotify_unLimit_cid != null && StrUtil.isNotBlank(spotify_unLimit_cid.getSysValue())) {
  174. spotifyUnLimitCouponId = Long.parseLong(spotify_unLimit_cid.getSysValue());
  175. Coupon coupon = couponMapper.selectById(spotifyUnLimitCouponId);
  176. if (coupon != null) {
  177. isFlag = couponCommonService.sendCouponToUser(spotifyUnLimitRecord.getUserId(), spotifyUnLimitCouponId, null, null, null, CouponUser.Channel.manual);
  178. }
  179. }
  180. //14天内是否重复提交过该账号
  181. SpotifyUnLimitRecord exists = spotifyUnLimitRecordMapper.selectOne(Wrappers.lambdaQuery(SpotifyUnLimitRecord.class)
  182. .eq(SpotifyUnLimitRecord::getUserId, spotifyUnLimitRecord.getUserId())
  183. .eq(SpotifyUnLimitRecord::getAccount, spotifyUnLimitRecord.getAccount())
  184. .last("limit 1"));
  185. if (exists != null && exists.getUpdateTime().after(DateUtil.offsetDay(DateTime.now(), -14))) {
  186. throw BusinessRuntimeException.getInstance("您已提交过,请勿重复提交");
  187. }
  188. if (exists != null) {
  189. spotifyUnLimitRecord.setId(exists.getId());
  190. spotifyUnLimitRecord.setIsSend(false);
  191. spotifyUnLimitRecordMapper.updateById(spotifyUnLimitRecord);
  192. } else {
  193. try {
  194. spotifyUnLimitRecordMapper.insert(spotifyUnLimitRecord);
  195. } catch (DuplicateKeyException e) {
  196. log.info("用户:{}重复提交解除限制spotify账号:{}", spotifyUnLimitRecord.getUserId(), spotifyUnLimitRecord.getAccount());
  197. }
  198. }
  199. return isFlag ? beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
  200. .field(CouponUserView::getUserId, spotifyUnLimitRecord.getUserId()).op(Operator.Equal)
  201. .field(CouponUserView::getCouponId, spotifyUnLimitCouponId).op(Operator.Equal)
  202. .build()) : null;
  203. }
  204. @Override
  205. public CouponUserView getAvailableSpotifyCoupon(long userId) {
  206. SysConfig spotify_register_success_cid = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "spotify_register_success_cid").last("limit 1"));
  207. CouponUserView couponUserView = null;
  208. if (spotify_register_success_cid != null && StrUtil.isNotBlank(spotify_register_success_cid.getSysValue())) {
  209. DateTime now = DateTime.now();
  210. Long spotifyRegisterSucCid = Long.parseLong(spotify_register_success_cid.getSysValue());
  211. couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
  212. .field(CouponUserView::getUserId, userId).op(Operator.Equal)
  213. .field(CouponUserView::getCouponId, spotifyRegisterSucCid).op(Operator.Equal)
  214. .field(CouponUserView::getCouponUserStatus, CouponUser.Status.unused.name()).op(Operator.Equal)
  215. .field(CouponUserView::getValidStartTime, now).op(Operator.LessThan)
  216. .field(Coupon::getValidEndTime, now).op(Operator.GreaterThan)
  217. .build());
  218. }
  219. return couponUserView;
  220. }
  221. @Override
  222. public H5JsPayParams pay(Long orderId) throws Exception {
  223. log.info("注册 账号调起微信支付[start] orderId:{}", orderId);
  224. RegisterOrderDon orderDon = this.getById(orderId);
  225. if (orderDon.getStatus() == RegisterOrderDon.Status.close) {
  226. throw BusinessRuntimeException.getInstance("订单已关闭");
  227. }
  228. OrderAttach attach = new OrderAttach();
  229. attach.setO(orderDon.getId());
  230. //获取支付默认商户
  231. String shopId = sysConfigService.getDefaultShopConfigByType("wx");
  232. String toWechatAttach = Codec.DoBase64.custom()
  233. .setData(Jsons.toJson(attach)).encodeAsText();
  234. H5JsPayParams params = weChatService.getJsPayParams(
  235. weChatConfig.getAppid(),
  236. shopId,
  237. orderDon.getOpenId(),
  238. orderDon.getMoney().intValue(),
  239. orderDon.getOrderNo(),
  240. weChatConfig.getDomain() + "applets/register/prepay/notify",
  241. toWechatAttach,
  242. "注册服务",
  243. "JSAPI",
  244. null
  245. );
  246. log.info("微信支付成功[end]");
  247. return params;
  248. }
  249. @Override
  250. @Transactional(rollbackFor = Throwable.class)
  251. public void prepayNotify(Map<String, String> map) throws Exception {
  252. /* 此处处理订单等业务逻辑 */
  253. // 获取订单 和 微信订单号 和 支付的金额
  254. String attach = Codec.DoBase64.custom().setData(map.get("attach")).decodeAsText();
  255. OrderAttach orderAttach = Jsons.parseObject(attach, OrderAttach.class);
  256. final Long oid = orderAttach.getO();
  257. //final Long shareId = orderAttach.getShared();
  258. RegisterOrderDon order = this.getById(oid);
  259. if (null == order) {
  260. throw BusinessRuntimeException.getInstance("订单不存在.");
  261. }
  262. String appId = map.get("mch_id");
  263. // 校验签名
  264. boolean f = weChatService.checkSignWithMd5(map, shopConfigMapper.getByAppId(appId).getSecret());
  265. if (!f) {
  266. log.error("回调数据:{}", map);
  267. throw BusinessRuntimeException.getInstance("支付成功回调, 校验签名失败.");
  268. }
  269. String transactionId = map.get("transaction_id");
  270. if (order.getStatus().equals(RegisterOrderDon.Status.hasPayment)) {
  271. log.info("已回调,重复修改状态 id:{}", order.getId());
  272. return;
  273. }
  274. // 更改订单状态
  275. order.setTransactionId(transactionId);
  276. order.setStatus(RegisterOrderDon.Status.hasPayment);
  277. AppleAutoRegisterData appleAutoRegisterData = appleAutoRegisterDataMapper.selectOne(Wrappers.lambdaQuery(AppleAutoRegisterData.class).eq(AppleAutoRegisterData::getOrderId, order.getId()).eq(AppleAutoRegisterData::getStatus, true));
  278. if (appleAutoRegisterData != null) {
  279. order.setStatus(RegisterOrderDon.Status.complete);
  280. }
  281. order.setPayTime(DateUtil.parse(map.get("time_end"), "yyyyMMddHHmmss"));
  282. if (StringUtils.isBlank(order.getOpenId())) {
  283. order.setOpenId(map.get("openid"));
  284. }
  285. this.updateById(order);
  286. }
  287. @Override
  288. public Result<String> wxRefund(OrderRefundReq refundReq) throws Exception {
  289. RegisterOrderDon registerOrderDon = this.getById(refundReq.getOrderId());
  290. if (registerOrderDon == null) {
  291. throw BusinessRuntimeException.getInstance("该订单不存在");
  292. }
  293. if (registerOrderDon.getStatus() == RegisterOrderDon.Status.refund) {
  294. throw BusinessRuntimeException.getInstance("该订单已退款");
  295. }
  296. if (registerOrderDon.getStatus() != RegisterOrderDon.Status.error && registerOrderDon.getStatus() != RegisterOrderDon.Status.hasPayment) {
  297. throw BusinessRuntimeException.getInstance("该订单状态不允许退款");
  298. }
  299. if (registerOrderDon.getMoney().compareTo(BigDecimal.ZERO) <= 0) {
  300. throw BusinessRuntimeException.getInstance("该订单金额为0,无需退款");
  301. }
  302. if (!redisService.setNx(String.format("%s", refundReq.getOrderId()), refundReq.getOrderId(), 10l)) {
  303. throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中..");
  304. }
  305. long time = System.currentTimeMillis();
  306. //退款订单号
  307. String outRefundNo = Codec.DoDigest.custom()
  308. .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
  309. .setStringData(time + String.valueOf(registerOrderDon.getId()))
  310. .toHexString();
  311. refundReq.setRefundMoney(registerOrderDon.getMoney());
  312. weChatService.refundWxOrder(weChatConfig.getAppid(),
  313. Optional.ofNullable(registerOrderDon.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID),
  314. registerOrderDon.getTransactionId(),
  315. outRefundNo,
  316. registerOrderDon.getMoney(),
  317. refundReq.getRefundMoney(),
  318. weChatConfig.getDomain() + "manage/register/wx/refund/notify");
  319. //修改订单状态
  320. registerOrderDon.setStatus(RegisterOrderDon.Status.refund);
  321. this.updateById(registerOrderDon);
  322. return GatewayResponse.SUCCESS.newBuilder().toResult("退款成功");
  323. }
  324. @Override
  325. public void wxRefundNotify(Map<String, String> map) throws Exception {
  326. // 对退款数据 解密
  327. String md5Key = Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(shopConfigMapper.getByAppId(map.get("mch_id")).getSecret()).toHexString();
  328. String reqInfoXml = Codec.DoCipher.custom().setAlgorithm(Codec.DoCipher.Trans.AES_ECB_PKCS7Padding).setBase64Data(map.get("req_info")).setStringKey(md5Key).ofDecrypt().toText();
  329. log.info("=== 退款数据解密: \n {}", reqInfoXml);
  330. Map<String, String> reqInfoMap = Xmls.toMap(reqInfoXml);
  331. // 退款失败
  332. if (StrUtil.equals("CHANGE", reqInfoMap.get("refund_status"))) {
  333. throw BusinessRuntimeException.getInstance("退款异常.");
  334. } else if (StrUtil.equals("REFUNDCLOSE", reqInfoMap.get("refund_status"))) {
  335. throw BusinessRuntimeException.getInstance("退款关闭.");
  336. }
  337. /* 此处处理业务逻辑 */
  338. String outRefundNo = reqInfoMap.get("out_refund_no");
  339. String transactionId = reqInfoMap.get("transaction_id");
  340. RegisterOrderDon registerOrderDon = this.getOne(Wrappers.lambdaQuery(RegisterOrderDon.class).eq(RegisterOrderDon::getTransactionId, transactionId).last("limit 1"));
  341. if (registerOrderDon == null) {
  342. log.error("退款回调,transactionId:{}订单不存在", transactionId);
  343. }
  344. if (registerOrderDon.getStatus() != RegisterOrderDon.Status.refund) {
  345. registerOrderDon.setStatus(RegisterOrderDon.Status.refund);
  346. this.updateById(registerOrderDon);
  347. }
  348. log.info("微信退款: 退款单号[{}] 退款成功.", outRefundNo);
  349. }
  350. }