OrderService.java 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. package com.izouma.nineth.service;
  2. import com.alibaba.excel.EasyExcel;
  3. import com.alibaba.fastjson.JSON;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.alibaba.fastjson.serializer.SerializerFeature;
  6. import com.alipay.api.AlipayClient;
  7. import com.alipay.api.request.AlipayTradeWapPayRequest;
  8. import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
  9. import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
  10. import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  11. import com.github.binarywang.wxpay.constant.WxPayConstants;
  12. import com.github.binarywang.wxpay.exception.WxPayException;
  13. import com.github.binarywang.wxpay.service.WxPayService;
  14. import com.google.common.base.Splitter;
  15. import com.huifu.adapay.core.exception.BaseAdaPayException;
  16. import com.huifu.adapay.model.AdapayCommon;
  17. import com.huifu.adapay.model.Payment;
  18. import com.izouma.nineth.config.*;
  19. import com.izouma.nineth.domain.Collection;
  20. import com.izouma.nineth.domain.*;
  21. import com.izouma.nineth.dto.MarketSettlement;
  22. import com.izouma.nineth.dto.PageQuery;
  23. import com.izouma.nineth.dto.UserBankCard;
  24. import com.izouma.nineth.enums.*;
  25. import com.izouma.nineth.event.*;
  26. import com.izouma.nineth.exception.BusinessException;
  27. import com.izouma.nineth.repo.*;
  28. import com.izouma.nineth.security.Authority;
  29. import com.izouma.nineth.service.sms.SmsService;
  30. import com.izouma.nineth.utils.*;
  31. import io.github.bucket4j.Bandwidth;
  32. import io.github.bucket4j.Bucket;
  33. import io.github.bucket4j.BucketConfiguration;
  34. import io.github.bucket4j.Refill;
  35. import io.github.bucket4j.distributed.proxy.ProxyManager;
  36. import lombok.extern.slf4j.Slf4j;
  37. import org.apache.commons.codec.EncoderException;
  38. import org.apache.commons.codec.net.URLCodec;
  39. import org.apache.commons.collections.MapUtils;
  40. import org.apache.commons.lang3.ObjectUtils;
  41. import org.apache.commons.lang3.RandomStringUtils;
  42. import org.apache.commons.lang3.RandomUtils;
  43. import org.apache.commons.lang3.StringUtils;
  44. import org.apache.rocketmq.client.producer.SendResult;
  45. import org.apache.rocketmq.spring.core.RocketMQTemplate;
  46. import org.springframework.cache.annotation.Cacheable;
  47. import org.springframework.context.event.EventListener;
  48. import org.springframework.core.env.Environment;
  49. import org.springframework.data.domain.Page;
  50. import org.springframework.data.domain.Pageable;
  51. import org.springframework.data.redis.core.BoundSetOperations;
  52. import org.springframework.data.redis.core.BoundValueOperations;
  53. import org.springframework.data.redis.core.RedisTemplate;
  54. import org.springframework.scheduling.annotation.Scheduled;
  55. import org.springframework.stereotype.Service;
  56. import org.springframework.ui.Model;
  57. import javax.persistence.criteria.Join;
  58. import java.io.OutputStream;
  59. import java.math.BigDecimal;
  60. import java.math.BigInteger;
  61. import java.math.RoundingMode;
  62. import java.time.Duration;
  63. import java.time.LocalDate;
  64. import java.time.LocalDateTime;
  65. import java.time.LocalTime;
  66. import java.time.format.DateTimeFormatter;
  67. import java.util.*;
  68. import java.util.concurrent.TimeUnit;
  69. import java.util.concurrent.atomic.AtomicInteger;
  70. import java.util.stream.Collectors;
  71. @Service
  72. @Slf4j
  73. public class OrderService {
  74. private final OrderRepo orderRepo;
  75. private final CollectionRepo collectionRepo;
  76. private final UserAddressRepo userAddressRepo;
  77. private final UserRepo userRepo;
  78. private final Environment env;
  79. private final AlipayClient alipayClient;
  80. private final AlipayProperties alipayProperties;
  81. private final WxPayService wxPayService;
  82. private final WxPayProperties wxPayProperties;
  83. private final AssetService assetService;
  84. private final SysConfigService sysConfigService;
  85. private final AssetRepo assetRepo;
  86. private final UserCouponRepo userCouponRepo;
  87. private final CollectionService collectionService;
  88. private final CommissionRecordRepo commissionRecordRepo;
  89. private final AdapayProperties adapayProperties;
  90. private final GeneralProperties generalProperties;
  91. private final RocketMQTemplate rocketMQTemplate;
  92. private final RedisTemplate<String, Object> redisTemplate;
  93. private final SnowflakeIdWorker snowflakeIdWorker;
  94. private final SmsService smsService;
  95. private final ErrorOrderRepo errorOrderRepo;
  96. private final ShowCollectionRepo showCollectionRepo;
  97. private final ShowroomService showroomService;
  98. private final CollectionPrivilegeRepo collectionPrivilegeRepo;
  99. private final UserBankCardRepo userBankCardRepo;
  100. private final CacheService cacheService;
  101. private final UserPropertyRepo userPropertyRepo;
  102. private final UserBalanceService userBalanceService;
  103. private final ProxyManager<String> buckets;
  104. public OrderService(OrderRepo orderRepo, CollectionRepo collectionRepo, UserAddressRepo userAddressRepo,
  105. UserRepo userRepo, Environment env, AlipayClient alipayClient,
  106. AlipayProperties alipayProperties, WxPayService wxPayService, WxPayProperties wxPayProperties,
  107. AssetService assetService, SysConfigService sysConfigService, AssetRepo assetRepo,
  108. UserCouponRepo userCouponRepo, CollectionService collectionService,
  109. CommissionRecordRepo commissionRecordRepo, AdapayProperties adapayProperties,
  110. GeneralProperties generalProperties, RocketMQTemplate rocketMQTemplate,
  111. RedisTemplate<String, Object> redisTemplate, SnowflakeIdWorker snowflakeIdWorker,
  112. SmsService smsService, ErrorOrderRepo errorOrderRepo, ShowCollectionRepo showCollectionRepo,
  113. ShowroomService showroomService, CollectionPrivilegeRepo collectionPrivilegeRepo,
  114. UserBankCardRepo userBankCardRepo, CacheService cacheService, UserPropertyRepo userPropertyRepo,
  115. UserBalanceService userBalanceService, ProxyManager<String> proxyManager) {
  116. this.orderRepo = orderRepo;
  117. this.collectionRepo = collectionRepo;
  118. this.userAddressRepo = userAddressRepo;
  119. this.userRepo = userRepo;
  120. this.env = env;
  121. this.alipayClient = alipayClient;
  122. this.alipayProperties = alipayProperties;
  123. this.wxPayService = wxPayService;
  124. this.wxPayProperties = wxPayProperties;
  125. this.assetService = assetService;
  126. this.sysConfigService = sysConfigService;
  127. this.assetRepo = assetRepo;
  128. this.userCouponRepo = userCouponRepo;
  129. this.collectionService = collectionService;
  130. this.commissionRecordRepo = commissionRecordRepo;
  131. this.adapayProperties = adapayProperties;
  132. this.generalProperties = generalProperties;
  133. this.rocketMQTemplate = rocketMQTemplate;
  134. this.redisTemplate = redisTemplate;
  135. this.snowflakeIdWorker = snowflakeIdWorker;
  136. this.smsService = smsService;
  137. this.errorOrderRepo = errorOrderRepo;
  138. this.showCollectionRepo = showCollectionRepo;
  139. this.showroomService = showroomService;
  140. this.collectionPrivilegeRepo = collectionPrivilegeRepo;
  141. this.userBankCardRepo = userBankCardRepo;
  142. this.cacheService = cacheService;
  143. this.userPropertyRepo = userPropertyRepo;
  144. this.userBalanceService = userBalanceService;
  145. this.buckets = proxyManager;
  146. }
  147. public Page<Order> all(PageQuery pageQuery) {
  148. return orderRepo.findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
  149. }
  150. public String mqCreate(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor,
  151. String sign, boolean vip, int vipPurchase, int vipPoint) {
  152. String qs = null;
  153. try {
  154. qs = AESEncryptUtil.decrypt(sign);
  155. } catch (Exception e) {
  156. throw new BusinessException("签名错误");
  157. }
  158. final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(qs);
  159. if (Math.abs(MapUtils.getLong(map, "ts") - System.currentTimeMillis()) > 90000) {
  160. throw new BusinessException("签名已过期");
  161. }
  162. if (redisTemplate.opsForValue().get(RedisKeys.BLACK_LIST + userId) != null) {
  163. throw new BusinessException("频繁操作,请稍后再试");
  164. }
  165. BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.LIMIT_USER + userId);
  166. ops.setIfAbsent(0, Duration.ofSeconds(10));
  167. long val = Optional.ofNullable(ops.increment()).orElse(0L);
  168. if (val > 5) {
  169. if (val > 10) {
  170. redisTemplate.opsForValue().set(RedisKeys.BLACK_LIST + userId, 1, Duration.ofSeconds(60 * 10));
  171. }
  172. throw new BusinessException("频繁操作,请稍后再试");
  173. }
  174. limitReq(collectionId);
  175. Integer stock = collectionService.getStock(collectionId);
  176. if (stock == null || stock <= 0) {
  177. throw new BusinessException("藏品已售罄", ErrorCode.SOLD_OUT);
  178. }
  179. Long id = snowflakeIdWorker.nextId();
  180. SendResult result = rocketMQTemplate.syncSend(generalProperties.getCreateOrderTopic(),
  181. new CreateOrderEvent(id, userId, collectionId, qty, addressId, userCouponId, invitor, vip), 100000);
  182. log.info("发送订单到队列: {}, userId={}, result={}", id, userId, result);
  183. return String.valueOf(id);
  184. }
  185. public void limitReq(Long collectionId) {
  186. Bucket bucket = buckets.builder().build("limit::" + collectionId, () -> (BucketConfiguration.builder()
  187. .addLimit(Bandwidth.classic(3000, Refill.intervally(3000, Duration.ofSeconds(3000))))
  188. .build()));
  189. if (!bucket.tryConsume(1)) {
  190. throw new BusinessException("前方拥堵,请稍后再试");
  191. }
  192. }
  193. public Order create(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor,
  194. Long id, boolean vip) {
  195. long t = System.currentTimeMillis();
  196. qty = 1;
  197. int stock = Optional.ofNullable(collectionService.decreaseStock(collectionId, qty))
  198. .map(Math::toIntExact)
  199. .orElseThrow(new BusinessException("很遗憾,藏品已售罄", ErrorCode.SOLD_OUT));
  200. int usePoint = 0;
  201. // 创建订单出错后需要回滚库存,所以需要try-catch
  202. try {
  203. if (stock < 0) {
  204. throw new BusinessException("很遗憾,藏品已售罄", ErrorCode.SOLD_OUT);
  205. }
  206. Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("藏品不存在"));
  207. if (collection.getAssetId() != null && collection.getAssetId().equals(778359L)) {
  208. throw new BusinessException("很遗憾,藏品已售罄", ErrorCode.SOLD_OUT);
  209. }
  210. if (collection.getSource() == CollectionSource.TRANSFER) {
  211. Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("藏品不存在"));
  212. if (Objects.equals(asset.getUserId(), userId)) {
  213. throw new BusinessException("不能购买自己的藏品");
  214. }
  215. if (asset.getStatus() != AssetStatus.NORMAL) {
  216. throw new BusinessException("藏品已下架");
  217. }
  218. }
  219. User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("铸造者不存在"));
  220. UserCoupon coupon = null;
  221. if (collection.isCouponPayment()) {
  222. if (userCouponId == null) {
  223. throw new BusinessException("必须使用优惠券支付");
  224. }
  225. coupon = userCouponRepo.findById(userCouponId).orElseThrow(new BusinessException("兑换券不存在"));
  226. if (!coupon.getUserId().equals(userId)) {
  227. throw new BusinessException("兑换券不属于您");
  228. }
  229. if (coupon.isUsed()) {
  230. throw new BusinessException("该兑换券已使用");
  231. }
  232. if (coupon.isLimited() && !coupon.getCollectionIds().contains(collectionId)) {
  233. throw new BusinessException("该兑换券不可用");
  234. }
  235. }
  236. if (collection.isScheduleSale()) {
  237. if (collection.getStartTime().isAfter(LocalDateTime.now())) {
  238. throw new BusinessException("当前还未开售");
  239. }
  240. }
  241. if (!collection.isOnShelf()) {
  242. if (!collection.isScanCode()) {
  243. throw new BusinessException("藏品已下架");
  244. }
  245. }
  246. if (!collection.isSalable()) {
  247. throw new BusinessException("该藏品当前不可购买");
  248. }
  249. if (collection.getMaxCount() > 0) {
  250. int userMax = userPropertyRepo.findById(userId)
  251. .map(UserProperty::getMaxCount)
  252. .orElse(collection.getMaxCount());
  253. int count;
  254. if (StringUtils.isNotBlank(collection.getCountId())) {
  255. count = orderRepo.countByUserIdAndCountIdAndStatusIn(userId, collection.getCountId(),
  256. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  257. } else {
  258. count = orderRepo.countByUserIdAndCollectionIdAndStatusIn(userId, collectionId,
  259. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  260. }
  261. if (count >= userMax) {
  262. throw new BusinessException("限购" + userMax + "件");
  263. }
  264. }
  265. User user = null;
  266. if (ObjectUtils.isNotEmpty(collection.getMinimumCharge()) && collection.getMinimumCharge()
  267. .compareTo(BigDecimal.ZERO) > 0) {
  268. user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
  269. if (!user.isCanSale()) {
  270. throw new BusinessException("绿洲石不足");
  271. }
  272. }
  273. //查询是否有拉新任务,只算官方购买
  274. if (collection.getSource() != CollectionSource.TRANSFER && collection.getAssignment() > 0) {
  275. //延迟销售
  276. if (!vip && collection.getTimeDelay()) {
  277. if (collection.getSaleTime().isAfter(LocalDateTime.now())) {
  278. throw new BusinessException("当前还未开售");
  279. }
  280. }
  281. if (user == null) {
  282. user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
  283. }
  284. if (vip) {
  285. // int purchase = orderRepo
  286. // .countByUserIdAndCollectionIdAndVipTrueAndStatusIn(userId, collectionId, Arrays
  287. // .asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  288. if (user.getVipPurchase() < 1) {
  289. throw new BusinessException("非vip!");
  290. }
  291. // // vip扣除额度
  292. // if (ObjectUtils.isNotEmpty(collection.getVipQuota())) {
  293. // collectionService.decreaseQuota(collectionId, 1);
  294. // }
  295. } else {
  296. if (user.getVipPoint() < 1) {
  297. throw new BusinessException("没有购买名额");
  298. }
  299. usePoint = 1;
  300. }
  301. }
  302. UserAddress userAddress = null;
  303. if (addressId != null) {
  304. userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
  305. }
  306. BigDecimal gasFee = sysConfigService.getBigDecimal("gas_fee");
  307. Order order = Order.builder()
  308. .id(Optional.ofNullable(id).orElse(snowflakeIdWorker.nextId()))
  309. .userId(userId)
  310. .collectionId(collectionId)
  311. .name(collection.getName())
  312. .pic(collection.getPic())
  313. .detail(collection.getDetail())
  314. .properties(collection.getProperties())
  315. .category(collection.getCategory())
  316. .canResale(collection.isCanResale())
  317. .royalties(collection.getRoyalties())
  318. .serviceCharge(collection.getServiceCharge())
  319. .type(collection.getType())
  320. .source(collection.getSource())
  321. .minterId(collection.getMinterId())
  322. .minter(minter.getNickname())
  323. .minterAvatar(minter.getAvatar())
  324. .qty(qty)
  325. .price(collection.getPrice())
  326. .gasPrice(gasFee)
  327. .totalPrice(collection.getPrice().multiply(BigDecimal.valueOf(qty)).add(gasFee))
  328. .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
  329. .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
  330. .address(Optional.ofNullable(userAddress).map(u ->
  331. u.getProvinceName() + " " + u.getCityName() + " " + u.getDistrictName() + " " + u
  332. .getAddress())
  333. .orElse(null))
  334. .status(OrderStatus.NOT_PAID)
  335. .assetId(collection.getAssetId())
  336. .couponId(userCouponId)
  337. .invitor(invitor)
  338. .countId(collection.getCountId())
  339. .vip(vip)
  340. .vipPoint(usePoint)
  341. .build();
  342. if (coupon != null) {
  343. coupon.setUsed(true);
  344. coupon.setUseTime(LocalDateTime.now());
  345. if (coupon.isNeedGas()) {
  346. order.setTotalPrice(order.getGasPrice());
  347. } else {
  348. order.setTotalPrice(BigDecimal.ZERO);
  349. }
  350. userCouponRepo.save(coupon);
  351. }
  352. if (collection.getSource() == CollectionSource.TRANSFER) {
  353. Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  354. asset.setStatus(AssetStatus.TRADING);
  355. assetRepo.save(asset);
  356. collectionRepo.setOnShelf(collectionId, false);
  357. }
  358. order = orderRepo.save(order);
  359. if (order.getTotalPrice().compareTo(BigDecimal.ZERO) == 0) {
  360. notifyOrder(order.getId(), PayMethod.WEIXIN, null);
  361. }
  362. if (usePoint > 0) {
  363. // 扣除积分
  364. userRepo.addVipPoint(userId, -usePoint);
  365. cacheService.clearUserMy(userId);
  366. }
  367. rocketMQTemplate.syncSend(generalProperties.getUpdateStockTopic(), collectionId, 10000);
  368. log.info("订单创建完成, id={}, {}ms", order.getId(), System.currentTimeMillis() - t);
  369. return order;
  370. } catch (Exception e) {
  371. collectionService.increaseStock(collectionId, qty);
  372. if (usePoint > 0) {
  373. // 扣除积分
  374. userRepo.addVipPoint(userId, usePoint);
  375. cacheService.clearUserMy(userId);
  376. log.info("订单失败加积分用户ID:{}, 积分:{}", userId, usePoint);
  377. }
  378. if (vip) {
  379. collectionService.decreaseQuota(collectionId, 1);
  380. log.info("订单失败加藏品额度CollectionId:{}", collectionId);
  381. }
  382. throw e;
  383. }
  384. }
  385. public Object checkLimit(Long collectionId, Long userId) {
  386. Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("藏品不存在"));
  387. int count = 0;
  388. AtomicInteger userMax = new AtomicInteger();
  389. userPropertyRepo.findById(userId).ifPresent(userProperty -> userMax.set(userProperty.getMaxCount()));
  390. if (collection.getMaxCount() > 0) {
  391. if (StringUtils.isNotBlank(collection.getCountId())) {
  392. count = orderRepo.countByUserIdAndCountIdAndStatusIn(userId, collection.getCountId(),
  393. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  394. } else {
  395. count = orderRepo.countByUserIdAndCollectionIdAndStatusIn(userId, collectionId,
  396. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  397. }
  398. }
  399. Map<String, Object> map = new HashMap<>();
  400. map.put("limit", userMax.get() > 0 ? userMax.get() : collection.getMaxCount());
  401. map.put("count", count);
  402. return map;
  403. }
  404. public void payOrderAlipay(Long id, Model model) {
  405. try {
  406. Order order = orderRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("订单不存在"));
  407. if (order.getStatus() != OrderStatus.NOT_PAID) {
  408. throw new BusinessException("订单状态错误");
  409. }
  410. JSONObject bizContent = new JSONObject();
  411. bizContent.put("notifyUrl", alipayProperties.getNotifyUrl());
  412. bizContent.put("returnUrl", alipayProperties.getReturnUrl());
  413. bizContent.put("out_trade_no", String.valueOf(snowflakeIdWorker.nextId()));
  414. bizContent.put("total_amount", order.getTotalPrice().stripTrailingZeros().toPlainString());
  415. bizContent.put("disable_pay_channels", "pcredit,creditCard");
  416. if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
  417. // 测试环境设为1分
  418. bizContent.put("total_amount", "0.01");
  419. }
  420. bizContent.put("subject", order.getName());
  421. bizContent.put("product_code", "QUICK_WAP_PAY");
  422. JSONObject body = new JSONObject();
  423. body.put("action", "payOrder");
  424. body.put("userId", order.getUserId());
  425. body.put("orderId", order.getId());
  426. bizContent.put("body", body.toJSONString());
  427. AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
  428. alipayRequest.setReturnUrl(alipayProperties.getReturnUrl());
  429. alipayRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
  430. alipayRequest.setBizContent(JSON.toJSONString(bizContent));
  431. String form = alipayClient.pageExecute(alipayRequest).getBody();
  432. model.addAttribute("form", form);
  433. } catch (BusinessException err) {
  434. model.addAttribute("errMsg", err.getError());
  435. } catch (Exception e) {
  436. model.addAttribute("errMsg", e.getMessage());
  437. }
  438. }
  439. public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException {
  440. Order order = orderRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("订单不存在"));
  441. if (order.getStatus() != OrderStatus.NOT_PAID) {
  442. throw new BusinessException("订单状态错误");
  443. }
  444. WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
  445. request.setBody(order.getName());
  446. request.setOutTradeNo(String.valueOf(new SnowflakeIdWorker(1, 1).nextId()));
  447. request.setTotalFee(order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue());
  448. if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
  449. // 测试环境设为1分
  450. // request.setTotalFee(1);
  451. }
  452. request.setSpbillCreateIp("180.102.110.170");
  453. request.setNotifyUrl(wxPayProperties.getNotifyUrl());
  454. request.setTradeType(tradeType);
  455. request.setOpenid(openId);
  456. request.setSignType("MD5");
  457. JSONObject body = new JSONObject();
  458. body.put("action", "payOrder");
  459. body.put("userId", order.getUserId());
  460. body.put("orderId", order.getId());
  461. request.setAttach(body.toJSONString());
  462. if (WxPayConstants.TradeType.MWEB.equals(tradeType)) {
  463. WxPayMwebOrderResult result = wxPayService.createOrder(request);
  464. return result.getMwebUrl() + "&redirect_url=" + new URLCodec().encode(wxPayProperties.getReturnUrl());
  465. } else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) {
  466. return wxPayService.<WxPayMpOrderResult>createOrder(request);
  467. }
  468. throw new BusinessException("不支持此付款方式");
  469. }
  470. @Cacheable(value = "adapay", key = "#id+'_'+#payChannel")
  471. public Object payAdapay(Long id, String payChannel, String openId) throws BaseAdaPayException {
  472. List<String> aliChannels = Arrays.asList("alipay", "alipay_qr", "alipay_wap");
  473. List<String> wxChannels = Arrays.asList("wx_pub", "wx_lite");
  474. if (!aliChannels.contains(payChannel) && !wxChannels.contains(payChannel)) {
  475. throw new BusinessException("不支持此渠道");
  476. }
  477. Order order = orderRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("订单不存在"));
  478. if (SecurityUtils.getAuthenticatedUser() != null
  479. && !SecurityUtils.getAuthenticatedUser().getId().equals(order.getUserId())) {
  480. log.error("payAdapay userId错误 requestUserId={} orderUserId={}",
  481. SecurityUtils.getAuthenticatedUser().getId(), order.getUserId());
  482. }
  483. Collection collection = collectionRepo.findById(order.getCollectionId())
  484. .orElseThrow(new BusinessException("藏品不存在"));
  485. User invitor = null;
  486. if (order.getInvitor() != null) {
  487. invitor = userRepo.findById(order.getInvitor()).orElse(null);
  488. }
  489. if (invitor != null && StringUtils.isBlank(invitor.getSettleAccountId())) {
  490. invitor = null;
  491. }
  492. if (order.getStatus() != OrderStatus.NOT_PAID) {
  493. throw new BusinessException("订单状态错误");
  494. }
  495. Map<String, Object> paymentParams = new HashMap<>();
  496. paymentParams.put("order_no", String.valueOf(snowflakeIdWorker.nextId()));
  497. paymentParams.put("pay_amt", order.getTotalPrice().setScale(2, RoundingMode.HALF_UP).toPlainString());
  498. paymentParams.put("app_id", adapayProperties.getAppId());
  499. paymentParams.put("pay_channel", payChannel);
  500. paymentParams.put("goods_title", collection.getName());
  501. paymentParams.put("goods_desc", collection.getName());
  502. paymentParams.put("time_expire", DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
  503. .format(LocalDateTime.now().plusMinutes(3)));
  504. paymentParams.put("notify_url", adapayProperties.getNotifyUrl() + "/order/" + adapayProperties
  505. .getMerchant() + "/" + order.getId());
  506. List<Map<String, Object>> divMembers = new ArrayList<>();
  507. BigDecimal totalAmount = order.getTotalPrice().subtract(order.getGasPrice());
  508. BigDecimal restAmount = order.getTotalPrice().multiply(BigDecimal.valueOf(1));
  509. if (collection.getSource().equals(CollectionSource.TRANSFER)) {
  510. Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("无记录"));
  511. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("拥有者用户不存在"));
  512. if (collection.getServiceCharge() + collection.getRoyalties() > 0) {
  513. // 扣除手续费、服务费、GAS费
  514. restAmount = divMoney(totalAmount, restAmount, divMembers, owner.getMemberId(),
  515. 100 - (collection.getServiceCharge() + collection.getRoyalties()), false);
  516. }
  517. restAmount = divMoney(restAmount, divMembers, "0", restAmount, true);
  518. } else {
  519. if (invitor != null && invitor.getShareRatio() != null
  520. && invitor.getShareRatio().compareTo(BigDecimal.ZERO) > 0) {
  521. restAmount = divMoney(totalAmount, restAmount, divMembers, invitor.getMemberId(),
  522. invitor.getShareRatio().intValue(), false);
  523. }
  524. restAmount = divMoney(restAmount, divMembers, "0", restAmount, true);
  525. }
  526. if (restAmount.compareTo(BigDecimal.ZERO) != 0) {
  527. log.error("分账出错 {}", JSON.toJSONString(divMembers, SerializerFeature.PrettyFormat));
  528. throw new BusinessException("分账出错");
  529. }
  530. if (divMembers.size() > 1) {
  531. paymentParams.put("div_members", divMembers);
  532. }
  533. Map<String, Object> expend = new HashMap<>();
  534. paymentParams.put("expend", expend);
  535. if ("wx_pub".equals(payChannel)) {
  536. if (StringUtils.isBlank(openId)) {
  537. throw new BusinessException("缺少openId");
  538. }
  539. expend.put("open_id", openId);
  540. expend.put("limit_pay", "1");
  541. }
  542. Map<String, Object> response;
  543. if ("wx_lite".equals(payChannel)) {
  544. paymentParams.put("adapay_func_code", "wxpay.createOrder");
  545. paymentParams.put("callback_url", generalProperties.getHost() + "/9th/orders");
  546. response = AdapayCommon.requestAdapayUits(paymentParams);
  547. log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
  548. } else {
  549. response = Payment.create(paymentParams);
  550. log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
  551. AdapayService.checkSuccess(response);
  552. // 保存adapay的订单id,用于后续取消订单时的查询
  553. BoundSetOperations<String, Object> ops = redisTemplate.boundSetOps(RedisKeys.PAY_RECORD + order.getId());
  554. ops.add(adapayProperties.getMerchant() + "#" + MapUtils.getString(response, "id"));
  555. ops.expire(7, TimeUnit.DAYS);
  556. }
  557. switch (payChannel) {
  558. case "alipay_wap":
  559. case "alipay":
  560. return MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info");
  561. case "alipay_qr":
  562. return MapUtils.getString(MapUtils.getMap(response, "expend"), "qrcode_url");
  563. case "wx_pub":
  564. JSONObject payParams = JSON
  565. .parseObject(MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info"));
  566. payParams.put("timestamp", payParams.get("timeStamp"));
  567. payParams.remove("timeStamp");
  568. return payParams;
  569. default:
  570. return MapUtils.getMap(response, "expend");
  571. }
  572. }
  573. public static BigDecimal divMoney(BigDecimal totalAmount, BigDecimal restAmount, List<Map<String, Object>> divMembers,
  574. String memberId, int ratio, boolean feeFlag) {
  575. if (ratio == -1 || (ratio > 0 && ratio < 100)) {
  576. BigDecimal divAmount = ratio == -1 ? restAmount :
  577. totalAmount.multiply(BigDecimal.valueOf(ratio))
  578. .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
  579. Map<String, Object> divMem = new HashMap<>();
  580. divMem.put("member_id", memberId);
  581. divMem.put("amount", divAmount.toPlainString());
  582. divMem.put("fee_flag", feeFlag ? "Y" : "N");
  583. divMembers.add(divMem);
  584. return restAmount.subtract(divAmount);
  585. } else {
  586. throw new BusinessException("分账比例错误");
  587. }
  588. }
  589. public static BigDecimal divMoney(BigDecimal restAmount, List<Map<String, Object>> divMembers,
  590. String memberId, BigDecimal divAmount, boolean feeFlag) {
  591. if (divAmount.compareTo(BigDecimal.ZERO) > 0) {
  592. Map<String, Object> divMem = new HashMap<>();
  593. divMem.put("member_id", memberId);
  594. divMem.put("amount", divAmount.toPlainString());
  595. divMem.put("fee_flag", feeFlag ? "Y" : "N");
  596. divMembers.add(divMem);
  597. }
  598. return restAmount.subtract(divAmount);
  599. }
  600. public void notifyOrder(Long orderId, PayMethod payMethod, String transactionId) {
  601. log.info("订单回调 orderId: {}, payMethod: {}, transactionId: {}", orderId, payMethod, transactionId);
  602. // 取消订单与订单回调不能同时进行,需要抢锁
  603. if (!getOrderLock(orderId)) {
  604. log.info("订单回调失败 orderId: {} redis锁定, 重新发送到队列", orderId);
  605. rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
  606. new OrderNotifyEvent(orderId, payMethod, transactionId, System.currentTimeMillis()));
  607. return;
  608. }
  609. try {
  610. Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
  611. Collection collection = collectionRepo.findDetailById(order.getCollectionId())
  612. .orElseThrow(new BusinessException("藏品不存在"));
  613. User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  614. if (order.getStatus() == OrderStatus.NOT_PAID) {
  615. order.setStatus(OrderStatus.PROCESSING);
  616. order.setPayTime(LocalDateTime.now());
  617. order.setTransactionId(transactionId);
  618. order.setPayMethod(payMethod);
  619. if (order.getType() == CollectionType.BLIND_BOX) {
  620. log.info("开始盲盒抽卡 orderId: {}, collectionId: {}", orderId, collection.getId());
  621. BlindBoxItem winItem = collectionService.draw(collection.getId());
  622. log.info("抽卡成功 orderId: {}, collectionId: {}, winCollectionId: {}", orderId, collection
  623. .getId(), winItem.getCollectionId());
  624. order.setWinCollectionId(winItem.getCollectionId());
  625. orderRepo.save(order);
  626. //藏品其他信息/是否vip
  627. CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo.findByCollectionId(order.getCollectionId());
  628. if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
  629. if (collectionPrivilege.isVip()) {
  630. //更新vip信息
  631. userRepo.updateVipPurchase(order.getUserId(), 1);
  632. }
  633. }
  634. assetService.createAsset(winItem, user, order.getId(), order.getPrice(), "出售",
  635. winItem.getTotal() > 1 ? collectionService.getNextNumber(winItem.getCollectionId()) : null,
  636. collection.getHoldDays());
  637. } else {
  638. if (collection.getSource() == CollectionSource.TRANSFER) {
  639. orderRepo.save(order);
  640. Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
  641. assetService.transfer(asset, order.getPrice(), user, TransferReason.TRANSFER, order.getId());
  642. order.setStatus(OrderStatus.FINISH);
  643. orderRepo.save(order);
  644. collectionRepo.delete(collection);
  645. // 如果展厅有此藏品
  646. showCollectionRepo.deleteAllByCollectionId(order.getCollectionId());
  647. // 发送短信提醒用户转让成功
  648. if (asset != null && asset.getUserId() != null) {
  649. smsService.sellOut(userRepo.findPhoneById(asset.getUserId()));
  650. }
  651. userBalanceService.realtimeSettleOrder(order);
  652. } else {
  653. orderRepo.save(order);
  654. //藏品其他信息/是否vip
  655. CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo.findByCollectionId(order.getCollectionId());
  656. if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
  657. if (collectionPrivilege.isVip()) {
  658. //更新vip信息
  659. userRepo.updateVipPurchase(order.getUserId(), 1);
  660. }
  661. }
  662. Asset asset = assetService.createAsset(collection, user, order.getId(), order.getPrice(), "出售",
  663. collection.getTotal() > 1 ? collectionService
  664. .getNextNumber(order.getCollectionId()) : null);
  665. if (collection.getType() == CollectionType.SHOWROOM) {
  666. showroomService.save(asset);
  667. }
  668. }
  669. }
  670. commission(order);
  671. if (collection.getAssetId() == null) {
  672. collectionService.increaseSale(order.getCollectionId(), order.getQty());
  673. }
  674. } else {
  675. throw new BusinessException("状态错误 " + order.getStatus());
  676. }
  677. } catch (Exception e) {
  678. ErrorOrder errorOrder = ErrorOrder.builder()
  679. .orderId(orderId)
  680. .transactionId(transactionId)
  681. .payMethod(payMethod)
  682. .build();
  683. if (e instanceof BusinessException) {
  684. log.error("订单回调出错 orderId: {} {}", orderId, e.getMessage());
  685. } else {
  686. log.error("订单回调出错 orderId: " + orderId, e);
  687. }
  688. errorOrder.setErrorMessage(e.getMessage());
  689. errorOrderRepo.save(errorOrder);
  690. }
  691. releaseOrderLock(orderId);
  692. }
  693. @EventListener
  694. public void onCreateAsset(CreateAssetEvent event) {
  695. Asset asset = event.getAsset();
  696. if (asset.getOrderId() != null) {
  697. Order order = orderRepo.findById(asset.getOrderId()).orElse(null);
  698. if (event.isSuccess() && order != null) {
  699. order.setTxHash(asset.getTxHash());
  700. order.setGasUsed(asset.getGasUsed());
  701. order.setBlockNumber(asset.getBlockNumber());
  702. order.setStatus(OrderStatus.FINISH);
  703. orderRepo.save(order);
  704. }
  705. }
  706. }
  707. @EventListener
  708. public void onHcMinted(MintedEvent event) {
  709. Asset asset = assetRepo.findById(event.getAssetId()).orElse(null);
  710. if (asset != null && asset.getOrderId() != null) {
  711. orderRepo.findById(asset.getOrderId()).ifPresent(order -> {
  712. order.setHcTxHash(asset.getTxHash());
  713. order.setHcGasUsed(asset.getGasUsed());
  714. order.setHcBlockNumber(asset.getBlockNumber());
  715. orderRepo.save(order);
  716. });
  717. }
  718. }
  719. @EventListener
  720. public void onTransferAsset(TransferAssetEvent event) {
  721. Asset asset = event.getAsset();
  722. Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException("订单不存在"));
  723. if (event.isSuccess()) {
  724. order.setTxHash(asset.getTxHash());
  725. order.setGasUsed(asset.getGasUsed());
  726. order.setBlockNumber(asset.getBlockNumber());
  727. order.setStatus(OrderStatus.FINISH);
  728. orderRepo.save(order);
  729. } else {
  730. log.error("创建asset失败");
  731. }
  732. }
  733. public void cancel(Long id) {
  734. Order order = orderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
  735. cancel(order);
  736. }
  737. public void cancel(Order order) {
  738. // 取消订单与订单回调不能同时进行,需要抢锁
  739. if (!getOrderLock(order.getId())) {
  740. log.error("订单取消失败 {}, redis锁了", order.getId());
  741. return;
  742. }
  743. try {
  744. if (order.getStatus() != OrderStatus.NOT_PAID) {
  745. throw new BusinessException("当前订单状态无法取消[" + order.getStatus().name() + "]");
  746. }
  747. CollectionSource source = Optional.ofNullable(order.getSource()).orElseGet(() ->
  748. collectionRepo.findById(order.getCollectionId()).map(Collection::getSource).orElse(null));
  749. if (source == CollectionSource.TRANSFER) {
  750. Asset asset = assetRepo.findById(order.getAssetId()).orElse(null);
  751. if (asset != null) {
  752. log.info("set normal cancelOrder {}", order.getId());
  753. asset.setStatus(AssetStatus.NORMAL);
  754. assetRepo.save(asset);
  755. }
  756. collectionRepo.setOnShelf(order.getCollectionId(), true);
  757. }
  758. collectionService.increaseStock(order.getCollectionId(), order.getQty());
  759. order.setStatus(OrderStatus.CANCELLED);
  760. order.setCancelTime(LocalDateTime.now());
  761. orderRepo.save(order);
  762. if (order.getCouponId() != null) {
  763. userCouponRepo.findById(order.getCouponId()).ifPresent(coupon -> {
  764. coupon.setUsed(false);
  765. coupon.setUseTime(null);
  766. userCouponRepo.save(coupon);
  767. });
  768. }
  769. //加上积分
  770. if (ObjectUtils.isNotEmpty(order.getVipPoint()) && order.getVipPoint() > 0) {
  771. userRepo.updateVipPoint(order.getUserId(), order.getVipPoint());
  772. cacheService.clearUserMy(order.getUserId());
  773. log.info("取消加积分用户ID:{},订单ID:{},积分:{}", order.getUserId(), order.getId(), order.getVipPoint());
  774. }
  775. if (order.isVip()) {
  776. collectionService.decreaseQuota(order.getCollectionId(), 1);
  777. log.info("取消加藏品额度CollectionId:{}", order.getCollectionId());
  778. }
  779. rocketMQTemplate.syncSend(generalProperties.getUpdateQuotaTopic(), order.getCollectionId(), 10000);
  780. log.info("取消订单{}", order.getId());
  781. } catch (Exception e) {
  782. if (e instanceof BusinessException) {
  783. log.error(e.getMessage());
  784. } else {
  785. log.error("订单取消错误 orderId: " + order.getId(), e);
  786. }
  787. }
  788. releaseOrderLock(order.getId());
  789. }
  790. public void refundCancelled(Order order) {
  791. }
  792. public void setNumber() {
  793. for (Collection collection : collectionRepo.findAll()) {
  794. if (collection.getSource() != CollectionSource.OFFICIAL) continue;
  795. collection.setCurrentNumber(0);
  796. collectionRepo.save(collection);
  797. for (Asset asset : assetRepo.findByCollectionId(collection.getId())) {
  798. if (asset.getStatus() == AssetStatus.GIFTED || asset.getStatus() == AssetStatus.TRANSFERRED) {
  799. } else {
  800. asset.setNumber(collectionService.getNextNumber(collection.getId()));
  801. assetRepo.save(asset);
  802. }
  803. }
  804. }
  805. }
  806. public void setNumberRecursive(Asset asset) {
  807. }
  808. @Scheduled(cron = "0 0 4 * * ?")
  809. public void setSales() {
  810. if (generalProperties.isNotifyServer()) {
  811. return;
  812. }
  813. List<User> minters = userRepo.findByAuthoritiesContains(Authority.get(AuthorityName.ROLE_MINTER));
  814. for (User minter : minters) {
  815. userRepo.setSales(minter.getId(), (int) orderRepo.countSales(minter.getId()));
  816. }
  817. }
  818. public void commission(Order order) {
  819. if (order.getInvitor() != null) {
  820. userRepo.findById(order.getInvitor()).ifPresent(user -> {
  821. BigDecimal shareRatio = user.getShareRatio();
  822. if (StringUtils.isNotBlank(user.getSettleAccountId()) &&
  823. shareRatio != null && shareRatio.compareTo(BigDecimal.ZERO) > 0) {
  824. BigDecimal totalPrice = order.getTotalPrice().subtract(order.getGasPrice());
  825. commissionRecordRepo.save(CommissionRecord.builder()
  826. .orderId(order.getId())
  827. .collectionId(order.getCollectionId())
  828. .name(order.getName())
  829. .totalPrice(totalPrice)
  830. .nickname(user.getNickname())
  831. .userId(user.getId())
  832. .shareRatio(user.getShareRatio())
  833. .phone(user.getPhone())
  834. .shareAmount(totalPrice.multiply(shareRatio)
  835. .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))
  836. .build());
  837. }
  838. });
  839. }
  840. }
  841. public Object queryCreateOrder(String id) {
  842. Object res = redisTemplate.opsForValue().get(RedisKeys.CREATE_ORDER + id);
  843. if (res != null) {
  844. if (res instanceof Map) {
  845. if (MapUtils.getBooleanValue((Map) res, "success", false)) {
  846. Order order = (Order) MapUtils.getObject((Map) res, "data");
  847. if (!SecurityUtils.getAuthenticatedUser().getId().equals(order.getUserId())) {
  848. log.error("queryCreateOrder userId错误 requestUserId={} orderUserId={}",
  849. SecurityUtils.getAuthenticatedUser().getId(), order.getUserId());
  850. return null;
  851. }
  852. }
  853. }
  854. }
  855. return res;
  856. }
  857. // 获取订单锁,有效时间1小时
  858. public boolean getOrderLock(Long orderId) {
  859. BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.ORDER_LOCK + orderId);
  860. Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.HOURS);
  861. return Boolean.TRUE.equals(flag);
  862. }
  863. // 释放订单锁
  864. public void releaseOrderLock(Long orderId) {
  865. redisTemplate.delete(RedisKeys.ORDER_LOCK + orderId);
  866. }
  867. public void calcSettle(LocalDateTime start, LocalDateTime end, OutputStream outputStream) {
  868. List<Order> orders = orderRepo
  869. .findByCreatedAtBetweenAndSourceAndStatusIn(start, end, CollectionSource.TRANSFER, Arrays
  870. .asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
  871. List<Asset> assets = assetRepo.findAllById(orders.stream().map(Order::getAssetId).collect(Collectors.toSet()));
  872. List<UserBankCard> bankCards = userBankCardRepo
  873. .findByUserIdIn(assets.stream().map(Asset::getUserId).collect(Collectors.toSet()));
  874. List<MarketSettlement> settlements = new ArrayList<>();
  875. for (Order order : orders) {
  876. BigDecimal amount = order.getTotalPrice()
  877. .subtract(order.getGasPrice())
  878. .multiply(new BigDecimal("100")
  879. .subtract(BigDecimal.valueOf(order.getServiceCharge()))
  880. .subtract(BigDecimal.valueOf(order.getRoyalties()))
  881. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP))
  882. .setScale(2, RoundingMode.HALF_UP);
  883. Long userId = assets.stream().filter(a -> a.getId().equals(order.getAssetId())).map(Asset::getUserId)
  884. .findAny().orElse(null);
  885. if (userId != null) {
  886. UserBankCard userBankCard = bankCards.stream().filter(b -> b.getUserId().equals(userId)).findAny()
  887. .orElse(null);
  888. MarketSettlement marketSettlement = settlements.stream().filter(s -> s.getUserId().equals(userId))
  889. .findAny().orElse(null);
  890. if (marketSettlement == null) {
  891. marketSettlement = new MarketSettlement(userId,
  892. Optional.ofNullable(userBankCard).map(UserBankCard::getRealName).orElse(null),
  893. Optional.ofNullable(userBankCard).map(UserBankCard::getBankNo).orElse(null),
  894. amount);
  895. settlements.add(marketSettlement);
  896. } else {
  897. marketSettlement.setAmount(marketSettlement.getAmount()
  898. .add(amount));
  899. }
  900. }
  901. }
  902. EasyExcel.write(outputStream, MarketSettlement.class).sheet("sheet").doWrite(settlements);
  903. }
  904. @Scheduled(cron = "0 0/10 * * * ?")
  905. public void setBlackList() {
  906. List<Long> userIds = orderRepo
  907. .checkBlackList(LocalDate.now().atStartOfDay(), LocalDate.now().atTime(LocalTime.MAX));
  908. userIds.forEach(userId -> {
  909. redisTemplate.opsForValue().set(RedisKeys.BLACK_LIST + userId, 1, Duration.ofSeconds(60 * 120));
  910. });
  911. }
  912. public List<Order> addOrder(Long collectionId, List<Long> userIds, LocalDateTime time, boolean notify) {
  913. Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("藏品不存在"));
  914. User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("用户不存在"));
  915. int i = 0;
  916. List<Order> list = new ArrayList<>();
  917. for (Long userId : userIds) {
  918. time = time.plusSeconds(i++);
  919. BigDecimal gasFee = new BigDecimal("1");
  920. Order order = Order.builder()
  921. .id(snowflakeIdWorker.nextId())
  922. .userId(userId)
  923. .collectionId(collectionId)
  924. .name(collection.getName())
  925. .pic(collection.getPic())
  926. .detail(collection.getDetail())
  927. .properties(collection.getProperties())
  928. .category(collection.getCategory())
  929. .canResale(collection.isCanResale())
  930. .royalties(collection.getRoyalties())
  931. .serviceCharge(collection.getServiceCharge())
  932. .type(collection.getType())
  933. .source(collection.getSource())
  934. .minterId(collection.getMinterId())
  935. .minter(minter.getNickname())
  936. .minterAvatar(minter.getAvatar())
  937. .qty(1)
  938. .price(collection.getPrice())
  939. .gasPrice(gasFee)
  940. .totalPrice(collection.getPrice().multiply(BigDecimal.valueOf(1)).add(gasFee))
  941. .status(notify ? OrderStatus.NOT_PAID : OrderStatus.FINISH)
  942. .assetId(collection.getAssetId())
  943. .countId(collection.getCountId())
  944. .build();
  945. orderRepo.saveAndFlush(order);
  946. String txHash = RandomStringUtils.randomAlphanumeric(64).toLowerCase(Locale.ROOT);
  947. String transactionId = DateTimeUtils.format(LocalDateTime.now(), "yyyyMMdd") +
  948. RandomStringUtils.randomNumeric(24);
  949. BigInteger gas = new BigInteger("157377");
  950. if (notify) {
  951. rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
  952. new OrderNotifyEvent(order.getId(), PayMethod.ALIPAY, order.getTransactionId()
  953. , System.currentTimeMillis()));
  954. } else {
  955. order.setCreatedAt(ObjectUtils.clone(time));
  956. order.setPayTime(time.plusSeconds(RandomUtils.nextInt(5, 50)));
  957. order.setTransactionId(transactionId);
  958. order.setTxHash(txHash);
  959. order.setGasUsed(gas);
  960. order.setPayMethod(PayMethod.ALIPAY);
  961. order.setStatus(OrderStatus.FINISH);
  962. order.setModifiedAt(order.getPayTime());
  963. order.setModifiedBy("system");
  964. order.setCreatedBy("system");
  965. orderRepo.save(order);
  966. }
  967. list.add(order);
  968. }
  969. return list;
  970. }
  971. public Page<Order> byTag(Long tagId, List<Long> excludeUserId, Pageable pageable) {
  972. if (excludeUserId.isEmpty()) {
  973. excludeUserId.add(0L);
  974. }
  975. return orderRepo.findAll((root, query, criteriaBuilder) -> {
  976. Join join = root.join("tags");
  977. return criteriaBuilder.and(criteriaBuilder.equal(join.get("id"), tagId),
  978. criteriaBuilder.not(root.get("ownerId").in(excludeUserId)));
  979. }, pageable);
  980. }
  981. }