OrderService.java 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. package com.izouma.nineth.service;
  2. import cn.hutool.core.collection.CollUtil;
  3. import com.alibaba.excel.EasyExcel;
  4. import com.alibaba.fastjson.JSON;
  5. import com.alibaba.fastjson.JSONObject;
  6. import com.alibaba.fastjson.serializer.SerializerFeature;
  7. import com.alipay.api.AlipayClient;
  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.domain.netease.NeteaseMessage;
  22. import com.izouma.nineth.domain.nftdomain.Cart;
  23. import com.izouma.nineth.dto.MarketSettlement;
  24. import com.izouma.nineth.dto.PageQuery;
  25. import com.izouma.nineth.dto.UserBankCard;
  26. import com.izouma.nineth.dto.nftdomain.DomainOrderDTO;
  27. import com.izouma.nineth.enums.*;
  28. import com.izouma.nineth.event.*;
  29. import com.izouma.nineth.exception.BusinessException;
  30. import com.izouma.nineth.repo.*;
  31. import com.izouma.nineth.repo.nftdomain.CartRepo;
  32. import com.izouma.nineth.security.Authority;
  33. import com.izouma.nineth.service.netease.NeteaseMessageService;
  34. import com.izouma.nineth.service.sms.SmsService;
  35. import com.izouma.nineth.utils.*;
  36. import com.sun.xml.bind.v2.runtime.NameBuilder;
  37. import io.github.bucket4j.Bandwidth;
  38. import io.github.bucket4j.Bucket;
  39. import io.github.bucket4j.BucketConfiguration;
  40. import io.github.bucket4j.Refill;
  41. import io.github.bucket4j.distributed.proxy.ProxyManager;
  42. import lombok.extern.slf4j.Slf4j;
  43. import org.apache.commons.codec.EncoderException;
  44. import org.apache.commons.codec.net.URLCodec;
  45. import org.apache.commons.collections.MapUtils;
  46. import org.apache.commons.lang3.ObjectUtils;
  47. import org.apache.commons.lang3.RandomStringUtils;
  48. import org.apache.commons.lang3.RandomUtils;
  49. import org.apache.commons.lang3.StringUtils;
  50. import org.apache.rocketmq.client.producer.SendResult;
  51. import org.apache.rocketmq.spring.core.RocketMQTemplate;
  52. import org.springframework.cache.annotation.Cacheable;
  53. import org.springframework.context.event.EventListener;
  54. import org.springframework.core.env.Environment;
  55. import org.springframework.data.domain.Page;
  56. import org.springframework.data.domain.PageImpl;
  57. import org.springframework.data.domain.Pageable;
  58. import org.springframework.data.redis.core.BoundSetOperations;
  59. import org.springframework.data.redis.core.BoundValueOperations;
  60. import org.springframework.data.redis.core.RedisTemplate;
  61. import org.springframework.scheduling.annotation.Async;
  62. import org.springframework.scheduling.annotation.Scheduled;
  63. import org.springframework.stereotype.Service;
  64. import javax.crypto.interfaces.PBEKey;
  65. import javax.persistence.criteria.Join;
  66. import javax.transaction.Transactional;
  67. import java.io.OutputStream;
  68. import java.math.BigDecimal;
  69. import java.math.BigInteger;
  70. import java.math.RoundingMode;
  71. import java.time.Duration;
  72. import java.time.LocalDateTime;
  73. import java.time.format.DateTimeFormatter;
  74. import java.util.*;
  75. import java.util.concurrent.ExecutionException;
  76. import java.util.concurrent.ForkJoinPool;
  77. import java.util.concurrent.TimeUnit;
  78. import java.util.concurrent.atomic.AtomicInteger;
  79. import java.util.stream.Collectors;
  80. @Service
  81. @Slf4j
  82. public class OrderService {
  83. private final OrderRepo orderRepo;
  84. private final CollectionRepo collectionRepo;
  85. private final UserAddressRepo userAddressRepo;
  86. private final UserRepo userRepo;
  87. private final Environment env;
  88. private final AlipayClient alipayClient;
  89. private final AlipayProperties alipayProperties;
  90. private final WxPayService wxPayService;
  91. private final WxPayProperties wxPayProperties;
  92. private final AssetService assetService;
  93. private final SysConfigService sysConfigService;
  94. private final AssetRepo assetRepo;
  95. private final UserCouponRepo userCouponRepo;
  96. private final CollectionService collectionService;
  97. private final CommissionRecordRepo commissionRecordRepo;
  98. private final AdapayProperties adapayProperties;
  99. private final GeneralProperties generalProperties;
  100. private final RocketMQTemplate rocketMQTemplate;
  101. private final RedisTemplate<String, Object> redisTemplate;
  102. private final SnowflakeIdWorker snowflakeIdWorker;
  103. private final SmsService smsService;
  104. private final ErrorOrderRepo errorOrderRepo;
  105. private final ShowCollectionRepo showCollectionRepo;
  106. private final ShowroomService showroomService;
  107. private final CollectionPrivilegeRepo collectionPrivilegeRepo;
  108. private final UserBankCardRepo userBankCardRepo;
  109. private final CacheService cacheService;
  110. private final UserPropertyRepo userPropertyRepo;
  111. private final UserBalanceService userBalanceService;
  112. private final ProxyManager<String> buckets;
  113. private final HeatInfoRepo heatInfoRepo;
  114. private final ShowroomRepo showroomRepo;
  115. private final NeteaseMessageService neteaseMessageService;
  116. private final CartRepo cartRepo;
  117. // private final RiceRepo riceRepo;
  118. private final RiceService riceService;
  119. // private RiceOperationRecordRepo riceOperationRecordRepo;
  120. // private RiceOperationRecordService riceOperationRecordService;
  121. public OrderService(OrderRepo orderRepo, CollectionRepo collectionRepo, UserAddressRepo userAddressRepo,
  122. UserRepo userRepo, Environment env, AlipayClient alipayClient,
  123. AlipayProperties alipayProperties, WxPayService wxPayService, WxPayProperties wxPayProperties,
  124. AssetService assetService, SysConfigService sysConfigService, AssetRepo assetRepo,
  125. UserCouponRepo userCouponRepo, CollectionService collectionService,
  126. CommissionRecordRepo commissionRecordRepo, AdapayProperties adapayProperties,
  127. GeneralProperties generalProperties, RocketMQTemplate rocketMQTemplate,
  128. RedisTemplate<String, Object> redisTemplate, SnowflakeIdWorker snowflakeIdWorker,
  129. SmsService smsService, ErrorOrderRepo errorOrderRepo, ShowCollectionRepo showCollectionRepo,
  130. ShowroomService showroomService, CollectionPrivilegeRepo collectionPrivilegeRepo,
  131. UserBankCardRepo userBankCardRepo, CacheService cacheService, UserPropertyRepo userPropertyRepo,
  132. UserBalanceService userBalanceService, ProxyManager<String> proxyManager,
  133. HeatInfoRepo heatInfoRepo, ShowroomRepo showroomRepo, NeteaseMessageService neteaseMessageService,
  134. CartRepo cartRepo, RiceService riceService) {
  135. this.orderRepo = orderRepo;
  136. this.collectionRepo = collectionRepo;
  137. this.userAddressRepo = userAddressRepo;
  138. this.userRepo = userRepo;
  139. this.env = env;
  140. this.alipayClient = alipayClient;
  141. this.alipayProperties = alipayProperties;
  142. this.wxPayService = wxPayService;
  143. this.wxPayProperties = wxPayProperties;
  144. this.assetService = assetService;
  145. this.sysConfigService = sysConfigService;
  146. this.assetRepo = assetRepo;
  147. this.userCouponRepo = userCouponRepo;
  148. this.collectionService = collectionService;
  149. this.commissionRecordRepo = commissionRecordRepo;
  150. this.adapayProperties = adapayProperties;
  151. this.generalProperties = generalProperties;
  152. this.rocketMQTemplate = rocketMQTemplate;
  153. this.redisTemplate = redisTemplate;
  154. this.snowflakeIdWorker = snowflakeIdWorker;
  155. this.smsService = smsService;
  156. this.errorOrderRepo = errorOrderRepo;
  157. this.showCollectionRepo = showCollectionRepo;
  158. this.showroomService = showroomService;
  159. this.collectionPrivilegeRepo = collectionPrivilegeRepo;
  160. this.userBankCardRepo = userBankCardRepo;
  161. this.cacheService = cacheService;
  162. this.userPropertyRepo = userPropertyRepo;
  163. this.userBalanceService = userBalanceService;
  164. this.buckets = proxyManager;
  165. this.heatInfoRepo = heatInfoRepo;
  166. this.showroomRepo = showroomRepo;
  167. this.neteaseMessageService = neteaseMessageService;
  168. this.cartRepo = cartRepo;
  169. this.riceService = riceService;
  170. }
  171. public Page<Order> all(PageQuery pageQuery) {
  172. Page<Order> all = orderRepo
  173. .findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
  174. List<Order> content = all.getContent();
  175. content.forEach(order -> {
  176. Long userId = orderRepo.selectUserId(order.getAssetId());
  177. order.setSellerId(userId);
  178. });
  179. return orderRepo.findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
  180. }
  181. public String mqCreate(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor,
  182. String sign, boolean vip, boolean safeFlag, Long showroomId, OrderType orderType, String collectionIds) {
  183. String qs = null;
  184. try {
  185. qs = AESEncryptUtil.decrypt(sign);
  186. } catch (Exception e) {
  187. throw new BusinessException("签名错误");
  188. }
  189. final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(qs);
  190. if (Math.abs(MapUtils.getLong(map, "ts") - System.currentTimeMillis()) > 90000) {
  191. throw new BusinessException("签名已过期");
  192. }
  193. if (redisTemplate.opsForValue().get(RedisKeys.BLACK_LIST + userId) != null) {
  194. throw new BusinessException("频繁操作,请稍后再试");
  195. }
  196. BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.LIMIT_USER + userId);
  197. ops.setIfAbsent(0, Duration.ofSeconds(10));
  198. long val = Optional.ofNullable(ops.increment()).orElse(0L);
  199. if (val > 5) {
  200. if (val > 10) {
  201. redisTemplate.opsForValue().set(RedisKeys.BLACK_LIST + userId, 1, Duration.ofSeconds(60 * 10));
  202. }
  203. throw new BusinessException("频繁操作,请稍后再试");
  204. }
  205. limitReq(collectionId);
  206. if (orderType != null) {
  207. if (!orderType.equals(OrderType.MIX)) {
  208. Integer stock = collectionService.getStock(collectionId);
  209. if (stock == null || stock <= 0) {
  210. throw new BusinessException("藏品已售罄", ErrorCode.SOLD_OUT);
  211. }
  212. }
  213. } else {
  214. orderType = OrderType.NORMAL;
  215. }
  216. Long id = snowflakeIdWorker.nextId();
  217. redisTemplate.opsForValue().set("safeFlag::" + id, safeFlag, 10, TimeUnit.MINUTES);
  218. SendResult result = rocketMQTemplate.syncSend(generalProperties.getCreateOrderTopic(),
  219. new CreateOrderEvent(id, userId, collectionId, qty, addressId, userCouponId, invitor, vip, showroomId, orderType, collectionIds),
  220. 100000);
  221. log.info("发送订单到队列: {}, userId={}, result={}", id, userId, result);
  222. return String.valueOf(id);
  223. }
  224. private void batchCreateOrder() {
  225. }
  226. public void limitReq(Long collectionId) {
  227. Bucket bucket = buckets.builder().build("limit::" + collectionId, () -> (BucketConfiguration.builder()
  228. .addLimit(Bandwidth
  229. .classic(3000, Refill
  230. .intervally(3000, Duration
  231. .ofSeconds(3000))))
  232. .build()));
  233. if (!bucket.tryConsume(1)) {
  234. throw new BusinessException("前方拥堵,请稍后再试");
  235. }
  236. }
  237. public Order create(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor,
  238. Long id, boolean vip, Long showroomId, OrderType orderType, Long parentOrderId, String collectionIds, Long newId) {
  239. if (orderType.equals(OrderType.MIX)) {
  240. Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("暂无藏品"));
  241. // Cart cart = cartRepo
  242. // .findFirstByCollectionIdAndUserIdAndDel(collectionId, userId, false);
  243. // if (cart != null)
  244. // cartRepo.delete(cart);
  245. User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("铸造者不存在"));
  246. Order order = Order.builder()
  247. .id(Optional.ofNullable(id).orElse(snowflakeIdWorker.nextId()))
  248. .userId(userId)
  249. .collectionId(collectionId)
  250. .name(collection.getName())
  251. .pic(collection.getPic())
  252. .detail(collection.getDetail())
  253. .properties(collection.getProperties())
  254. .category(collection.getCategory())
  255. .canResale(collection.isCanResale())
  256. .royalties(collection.getRoyalties())
  257. .serviceCharge(collection.getServiceCharge())
  258. .type(collection.getType())
  259. .source(collection.getSource())
  260. .minterId(collection.getMinterId())
  261. .minter(minter.getNickname())
  262. .minterAvatar(minter.getAvatar())
  263. .qty(qty)
  264. // .price(collection.getPrice())
  265. // .gasPrice(gasFee)
  266. // .totalPrice(collection.getPrice().multiply(BigDecimal.valueOf(qty)).add(gasFee))
  267. // .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
  268. // .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
  269. // .address(Optional.ofNullable(userAddress).map(u ->
  270. // u.getProvinceName() + " " + u.getCityName() + " " + u.getDistrictName() + " " + u
  271. // .getAddress())
  272. // .orElse(null))
  273. .status(OrderStatus.NOT_PAID)
  274. // .assetId(collection.getAssetId())
  275. .couponId(userCouponId)
  276. .invitor(invitor)
  277. // .countId(collection.getCountId())
  278. .vip(vip)
  279. .vipPoint(0)
  280. .companyId(1L)
  281. .orderType(OrderType.MIX)
  282. .collectionIds(collectionIds)
  283. .build();
  284. return orderRepo.save(order);
  285. } else {
  286. long t = System.currentTimeMillis();
  287. qty = 1;
  288. int stock = Optional.ofNullable(collectionService.decreaseStock(collectionId, qty))
  289. .map(Math::toIntExact)
  290. .orElseThrow(new BusinessException("很遗憾,藏品已售罄", ErrorCode.SOLD_OUT));
  291. int usePoint = 0;
  292. // 创建订单出错后需要回滚库存,所以需要try-catch
  293. try {
  294. if (stock < 0) {
  295. throw new BusinessException("很遗憾,藏品已售罄", ErrorCode.SOLD_OUT);
  296. }
  297. Collection collection = collectionRepo.findById(collectionId)
  298. .orElseThrow(new BusinessException("藏品不存在"));
  299. if (collection.isInPaying()) {
  300. throw new BusinessException("当然藏品正在支付中");
  301. }
  302. if (collection.getAssetId() != null && collection.getAssetId().equals(778359L)) {
  303. throw new BusinessException("很遗憾,藏品已售罄", ErrorCode.SOLD_OUT);
  304. }
  305. if (collection.getSource() == CollectionSource.TRANSFER) {
  306. Asset asset = assetRepo.findById(collection.getAssetId())
  307. .orElseThrow(new BusinessException("藏品不存在"));
  308. if (Objects.equals(asset.getUserId(), userId)) {
  309. throw new BusinessException("不能购买自己的藏品");
  310. }
  311. if (asset.getStatus() != AssetStatus.NORMAL) {
  312. throw new BusinessException("藏品已下架");
  313. }
  314. if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
  315. throw new BusinessException("此藏品已锁仓");
  316. }
  317. }
  318. User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("铸造者不存在"));
  319. UserCoupon coupon = null;
  320. if (collection.isCouponPayment()) {
  321. if (userCouponId == null) {
  322. throw new BusinessException("必须使用优惠券支付");
  323. }
  324. coupon = userCouponRepo.findById(userCouponId).orElseThrow(new BusinessException("兑换券不存在"));
  325. if (!coupon.getUserId().equals(userId)) {
  326. throw new BusinessException("兑换券不属于您");
  327. }
  328. if (coupon.isUsed()) {
  329. throw new BusinessException("该兑换券已使用");
  330. }
  331. if (coupon.isLimited() && !coupon.getCollectionIds().contains(collectionId)) {
  332. throw new BusinessException("该兑换券不可用");
  333. }
  334. }
  335. if (collection.isScheduleSale()) {
  336. if (collection.getStartTime().isAfter(LocalDateTime.now())) {
  337. throw new BusinessException("当前还未开售");
  338. }
  339. }
  340. if (!collection.isOnShelf()) {
  341. if (!collection.isScanCode()) {
  342. throw new BusinessException("藏品已下架");
  343. }
  344. }
  345. if (!collection.isSalable()) {
  346. throw new BusinessException("该藏品当前不可购买");
  347. }
  348. if (collection.getMaxCount() > 0) {
  349. int userMax = userPropertyRepo.findById(userId)
  350. .map(UserProperty::getMaxCount)
  351. .orElse(collection.getMaxCount());
  352. int count;
  353. if (StringUtils.isNotBlank(collection.getCountId())) {
  354. count = orderRepo.countByUserIdAndCountIdAndStatusIn(userId, collection.getCountId(),
  355. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  356. } else {
  357. count = orderRepo.countByUserIdAndCollectionIdAndStatusIn(userId, collectionId,
  358. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  359. }
  360. if (count >= userMax) {
  361. throw new BusinessException("限购" + userMax + "件");
  362. }
  363. }
  364. User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
  365. //设置了最低消费
  366. if (ObjectUtils.isNotEmpty(collection.getMinimumCharge()) && collection.getMinimumCharge()
  367. .compareTo(BigDecimal.ZERO) > 0) {
  368. if (!user.isCanSale()) {
  369. throw new BusinessException("绿洲石不足");
  370. }
  371. }
  372. //查询是否有拉新任务,只算官方购买
  373. if (collection.getSource() != CollectionSource.TRANSFER && collection.getAssignment() > 0) {
  374. //延迟销售
  375. if (!vip && collection.getTimeDelay()) {
  376. if (collection.getSaleTime().isAfter(LocalDateTime.now())) {
  377. throw new BusinessException("当前还未开售");
  378. }
  379. }
  380. if (vip) {
  381. if (user.getVipPurchase() < 1) {
  382. throw new BusinessException("非vip!");
  383. }
  384. } else {
  385. if (user.getVipPoint() < 1) {
  386. throw new BusinessException("没有购买名额");
  387. }
  388. usePoint = 1;
  389. }
  390. }
  391. UserAddress userAddress = null;
  392. if (addressId != null) {
  393. userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
  394. }
  395. BigDecimal gasFee = sysConfigService.getBigDecimal("gas_fee");
  396. Order order = Order.builder()
  397. .id(Optional.ofNullable(id).orElse(snowflakeIdWorker.nextId()))
  398. .userId(userId)
  399. .collectionId(collectionId)
  400. .name(collection.getName())
  401. .pic(collection.getPic())
  402. .detail(collection.getDetail())
  403. .properties(collection.getProperties())
  404. .category(collection.getCategory())
  405. .canResale(collection.isCanResale())
  406. .royalties(collection.getRoyalties())
  407. .serviceCharge(collection.getServiceCharge())
  408. .type(collection.getType())
  409. .source(collection.getSource())
  410. .minterId(collection.getMinterId())
  411. .minter(minter.getNickname())
  412. .minterAvatar(minter.getAvatar())
  413. .qty(qty)
  414. .price(collection.getPrice())
  415. .gasPrice(gasFee)
  416. .totalPrice(collection.getPrice().multiply(BigDecimal.valueOf(qty)).add(gasFee))
  417. .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
  418. .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone)
  419. .orElse(null))
  420. .address(Optional.ofNullable(userAddress).map(u ->
  421. u.getProvinceName() + " " + u.getCityName() + " " + u
  422. .getDistrictName() + " " + u
  423. .getAddress())
  424. .orElse(null))
  425. .status(OrderStatus.NOT_PAID)
  426. .assetId(collection.getAssetId())
  427. .couponId(userCouponId)
  428. .invitor(invitor)
  429. .countId(collection.getCountId())
  430. .vip(vip)
  431. .vipPoint(usePoint)
  432. .companyId(collection.getCompanyId())
  433. .build();
  434. if (orderType == OrderType.SUB) {
  435. order.setId(newId);
  436. order.setOrderType(orderType);
  437. order.setParentOrderId(parentOrderId);
  438. }
  439. if (coupon != null) {
  440. coupon.setUsed(true);
  441. coupon.setUseTime(LocalDateTime.now());
  442. if (coupon.isNeedGas()) {
  443. order.setTotalPrice(order.getGasPrice());
  444. } else {
  445. order.setTotalPrice(BigDecimal.ZERO);
  446. }
  447. userCouponRepo.save(coupon);
  448. }
  449. if (collection.getSource() == CollectionSource.TRANSFER) {
  450. Asset asset = assetRepo.findById(collection.getAssetId())
  451. .orElseThrow(new BusinessException("资产不存在"));
  452. asset.setStatus(AssetStatus.TRADING);
  453. assetRepo.save(asset);
  454. // collectionRepo.setOnShelf(collectionId, false);
  455. collectionRepo.setInPaying(collectionId, true);
  456. //拥有指定藏品降税
  457. Long sellerId = asset.getUserId();
  458. order.setRoyalties(assetService.getRoyalties(minter.getId(), collection.getRoyalties(), sellerId));
  459. if (asset.getType().equals(CollectionType.DOMAIN)) {
  460. order.setServiceCharge(assetService.getDomainServiceCharge(sellerId));
  461. } else {
  462. order.setServiceCharge(assetService.getServicecharge(collection.getServiceCharge(), sellerId));
  463. }
  464. }
  465. order = orderRepo.save(order);
  466. if (order.getTotalPrice().compareTo(BigDecimal.ZERO) == 0) {
  467. notifyOrder(order.getId(), PayMethod.WEIXIN, null);
  468. }
  469. // if (usePoint > 0) {
  470. // // 扣除积分
  471. // userRepo.addVipPoint(userId, -usePoint);
  472. // cacheService.clearUserMy(userId);
  473. // }
  474. cacheService.clearPrefixNameCount(collection.getPrefixName());
  475. if (ObjectUtils.isNotEmpty(showroomId)) {
  476. //通过展厅的购买数量
  477. heatInfoRepo.save(HeatInfo.builder()
  478. .showroomId(showroomId)
  479. .userId(userId)
  480. .type(HeatType.BUY)
  481. .value(0)
  482. .orderId(order.getId())
  483. .build());
  484. }
  485. // Cart cart = cartRepo
  486. // .findFirstByCollectionIdAndUserIdAndDel(collectionId, userId, false);
  487. // if (cart != null)
  488. // cartRepo.delete(cart);
  489. rocketMQTemplate.syncSend(generalProperties.getUpdateStockTopic(), collectionId, 10000);
  490. log.info("订单创建完成, id={}, {}ms", order.getId(), System.currentTimeMillis() - t);
  491. return order;
  492. } catch (Exception e) {
  493. collectionService.increaseStock(collectionId, qty);
  494. // if (usePoint > 0) {
  495. // // 扣除积分
  496. // userRepo.addVipPoint(userId, usePoint);
  497. // cacheService.clearUserMy(userId);
  498. // log.info("订单失败加积分用户ID:{}, 积分:{}", userId, usePoint);
  499. // }
  500. // if (vip) {
  501. // collectionService.decreaseQuota(collectionId, 1);
  502. // log.info("订单失败加藏品额度CollectionId:{}", collectionId);
  503. // }
  504. throw e;
  505. }
  506. }
  507. }
  508. public Object checkLimit(Long collectionId, Long userId) {
  509. Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("藏品不存在"));
  510. int count = 0;
  511. AtomicInteger userMax = new AtomicInteger();
  512. userPropertyRepo.findById(userId).ifPresent(userProperty -> userMax.set(userProperty.getMaxCount()));
  513. if (collection.getMaxCount() > 0) {
  514. if (StringUtils.isNotBlank(collection.getCountId())) {
  515. count = orderRepo.countByUserIdAndCountIdAndStatusIn(userId, collection.getCountId(),
  516. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  517. } else {
  518. count = orderRepo.countByUserIdAndCollectionIdAndStatusIn(userId, collectionId,
  519. Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
  520. }
  521. }
  522. Map<String, Object> map = new HashMap<>();
  523. map.put("limit", userMax.get() > 0 ? userMax.get() : collection.getMaxCount());
  524. map.put("count", count);
  525. return map;
  526. }
  527. public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException {
  528. Order order = orderRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("订单不存在"));
  529. if (order.getStatus() != OrderStatus.NOT_PAID) {
  530. throw new BusinessException("订单状态错误");
  531. }
  532. WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
  533. request.setBody(order.getName());
  534. request.setOutTradeNo(String.valueOf(new SnowflakeIdWorker(1, 1).nextId()));
  535. request.setTotalFee(order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue());
  536. if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
  537. // 测试环境设为1分
  538. // request.setTotalFee(1);
  539. }
  540. request.setSpbillCreateIp("180.102.110.170");
  541. request.setNotifyUrl(wxPayProperties.getNotifyUrl());
  542. request.setTradeType(tradeType);
  543. request.setOpenid(openId);
  544. request.setSignType("MD5");
  545. JSONObject body = new JSONObject();
  546. body.put("action", "payOrder");
  547. body.put("userId", order.getUserId());
  548. body.put("orderId", order.getId());
  549. request.setAttach(body.toJSONString());
  550. if (WxPayConstants.TradeType.MWEB.equals(tradeType)) {
  551. WxPayMwebOrderResult result = wxPayService.createOrder(request);
  552. return result.getMwebUrl() + "&redirect_url=" + new URLCodec().encode(wxPayProperties.getReturnUrl());
  553. } else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) {
  554. return wxPayService.<WxPayMpOrderResult>createOrder(request);
  555. }
  556. throw new BusinessException("不支持此付款方式");
  557. }
  558. @Cacheable(value = "adapay", key = "#id+'_'+#payChannel")
  559. public Object payAdapay(Long id, String payChannel, String openId) throws BaseAdaPayException {
  560. List<String> aliChannels = Arrays.asList("alipay", "alipay_qr", "alipay_wap");
  561. List<String> wxChannels = Arrays.asList("wx_pub", "wx_lite");
  562. if (!aliChannels.contains(payChannel) && !wxChannels.contains(payChannel)) {
  563. throw new BusinessException("不支持此渠道");
  564. }
  565. Order order = orderRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("订单不存在"));
  566. if (SecurityUtils.getAuthenticatedUser() != null
  567. && !SecurityUtils.getAuthenticatedUser().getId().equals(order.getUserId())) {
  568. log.error("payAdapay userId错误 requestUserId={} orderUserId={}",
  569. SecurityUtils.getAuthenticatedUser().getId(), order.getUserId());
  570. }
  571. Collection collection = collectionRepo.findById(order.getCollectionId())
  572. .orElseThrow(new BusinessException("藏品不存在"));
  573. User invitor = null;
  574. if (order.getInvitor() != null) {
  575. invitor = userRepo.findById(order.getInvitor()).orElse(null);
  576. }
  577. if (invitor != null && StringUtils.isBlank(invitor.getSettleAccountId())) {
  578. invitor = null;
  579. }
  580. if (order.getStatus() != OrderStatus.NOT_PAID) {
  581. throw new BusinessException("订单状态错误");
  582. }
  583. Map<String, Object> paymentParams = new HashMap<>();
  584. paymentParams.put("order_no", String.valueOf(snowflakeIdWorker.nextId()));
  585. paymentParams.put("pay_amt", order.getTotalPrice().setScale(2, RoundingMode.HALF_UP).toPlainString());
  586. paymentParams.put("app_id", adapayProperties.getAppId());
  587. paymentParams.put("pay_channel", payChannel);
  588. paymentParams.put("goods_title", collection.getName());
  589. paymentParams.put("goods_desc", collection.getName());
  590. paymentParams.put("time_expire", DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
  591. .format(LocalDateTime.now().plusMinutes(3)));
  592. paymentParams.put("notify_url", adapayProperties.getNotifyUrl() + "/order/" + adapayProperties
  593. .getMerchant() + "/" + order.getId());
  594. List<Map<String, Object>> divMembers = new ArrayList<>();
  595. BigDecimal totalAmount = order.getTotalPrice().subtract(order.getGasPrice());
  596. BigDecimal restAmount = order.getTotalPrice().multiply(BigDecimal.valueOf(1));
  597. if (collection.getSource().equals(CollectionSource.TRANSFER)) {
  598. Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("无记录"));
  599. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("拥有者用户不存在"));
  600. if (collection.getServiceCharge() + collection.getRoyalties() > 0) {
  601. // 扣除手续费、服务费、GAS费
  602. restAmount = divMoney(totalAmount, restAmount, divMembers, owner.getMemberId(),
  603. 100 - (collection.getServiceCharge() + collection.getRoyalties()), false);
  604. }
  605. restAmount = divMoney(restAmount, divMembers, "0", restAmount, true);
  606. } else {
  607. if (invitor != null && invitor.getShareRatio() != null
  608. && invitor.getShareRatio().compareTo(BigDecimal.ZERO) > 0) {
  609. restAmount = divMoney(totalAmount, restAmount, divMembers, invitor.getMemberId(),
  610. invitor.getShareRatio().intValue(), false);
  611. }
  612. restAmount = divMoney(restAmount, divMembers, "0", restAmount, true);
  613. }
  614. if (restAmount.compareTo(BigDecimal.ZERO) != 0) {
  615. log.error("分账出错 {}", JSON.toJSONString(divMembers, SerializerFeature.PrettyFormat));
  616. throw new BusinessException("分账出错");
  617. }
  618. if (divMembers.size() > 1) {
  619. paymentParams.put("div_members", divMembers);
  620. }
  621. Map<String, Object> expend = new HashMap<>();
  622. paymentParams.put("expend", expend);
  623. if ("wx_pub" .equals(payChannel)) {
  624. if (StringUtils.isBlank(openId)) {
  625. throw new BusinessException("缺少openId");
  626. }
  627. expend.put("open_id", openId);
  628. expend.put("limit_pay", "1");
  629. }
  630. Map<String, Object> response;
  631. if ("wx_lite" .equals(payChannel)) {
  632. paymentParams.put("adapay_func_code", "wxpay.createOrder");
  633. paymentParams.put("callback_url", generalProperties.getHost() + "/9th/orders");
  634. response = AdapayCommon.requestAdapayUits(paymentParams);
  635. log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
  636. } else {
  637. response = Payment.create(paymentParams);
  638. log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
  639. AdapayService.checkSuccess(response);
  640. // 保存adapay的订单id,用于后续取消订单时的查询
  641. BoundSetOperations<String, Object> ops = redisTemplate.boundSetOps(RedisKeys.PAY_RECORD + order.getId());
  642. ops.add(adapayProperties.getMerchant() + "#" + MapUtils.getString(response, "id"));
  643. ops.expire(7, TimeUnit.DAYS);
  644. }
  645. switch (payChannel) {
  646. case "alipay_wap":
  647. case "alipay":
  648. return MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info");
  649. case "alipay_qr":
  650. return MapUtils.getString(MapUtils.getMap(response, "expend"), "qrcode_url");
  651. case "wx_pub":
  652. JSONObject payParams = JSON
  653. .parseObject(MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info"));
  654. payParams.put("timestamp", payParams.get("timeStamp"));
  655. payParams.remove("timeStamp");
  656. return payParams;
  657. default:
  658. return MapUtils.getMap(response, "expend");
  659. }
  660. }
  661. public static BigDecimal divMoney(BigDecimal totalAmount, BigDecimal restAmount, List<Map<String, Object>> divMembers,
  662. String memberId, double ratio, boolean feeFlag) {
  663. if (ratio == -1 || (ratio > 0 && ratio < 100)) {
  664. BigDecimal divAmount = ratio == -1 ? restAmount :
  665. totalAmount.multiply(BigDecimal.valueOf(ratio))
  666. .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
  667. Map<String, Object> divMem = new HashMap<>();
  668. divMem.put("member_id", memberId);
  669. divMem.put("amount", divAmount.toPlainString());
  670. divMem.put("fee_flag", feeFlag ? "Y" : "N");
  671. divMembers.add(divMem);
  672. return restAmount.subtract(divAmount);
  673. } else {
  674. throw new BusinessException("分账比例错误");
  675. }
  676. }
  677. public Order fixMainOrder(Order mainOrder, BigDecimal total, BigDecimal gas, BigDecimal price, List<FileObject> count, String collectionIds, String subOrderIds) {
  678. mainOrder.setTotalPrice(total);
  679. mainOrder.setPic(count);
  680. mainOrder.setPrice(price);
  681. mainOrder.setGasPrice(gas);
  682. mainOrder.setPayTime(LocalDateTime.now());
  683. mainOrder.setCollectionIds(collectionIds);
  684. mainOrder.setSubOrderIds(subOrderIds);
  685. return orderRepo.save(mainOrder);
  686. }
  687. public static BigDecimal divMoney(BigDecimal restAmount, List<Map<String, Object>> divMembers,
  688. String memberId, BigDecimal divAmount, boolean feeFlag) {
  689. if (divAmount.compareTo(BigDecimal.ZERO) > 0) {
  690. Map<String, Object> divMem = new HashMap<>();
  691. divMem.put("member_id", memberId);
  692. divMem.put("amount", divAmount.toPlainString());
  693. divMem.put("fee_flag", feeFlag ? "Y" : "N");
  694. divMembers.add(divMem);
  695. }
  696. return restAmount.subtract(divAmount);
  697. }
  698. @Transactional
  699. public void notifyOrder(Long orderId, PayMethod payMethod, String transactionId) {
  700. log.info("订单回调 orderId: {}, payMethod: {}, transactionId: {}", orderId, payMethod, transactionId);
  701. // 取消订单与订单回调不能同时进行,需要抢锁
  702. if (!getOrderLock(orderId)) {
  703. log.info("订单回调失败 orderId: {} redis锁定, 重新发送到队列", orderId);
  704. rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
  705. new OrderNotifyEvent(orderId, payMethod, transactionId, System.currentTimeMillis()));
  706. return;
  707. }
  708. try {
  709. Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
  710. if (order.getOrderType() == null) {
  711. order.setOrderType(OrderType.NORMAL);
  712. }
  713. if (order.getOrderType().equals(OrderType.MIX)) {
  714. List<String> orderIds = Arrays.asList(order.getSubOrderIds().split(","));
  715. orderIds.forEach(subOrder -> {
  716. notifyOrder(Long.valueOf(subOrder), payMethod, transactionId);
  717. });
  718. order.setStatus(OrderStatus.FINISH);
  719. order.setPayMethod(payMethod);
  720. order.setTransactionId(transactionId);
  721. } else {
  722. Collection collection = collectionRepo.findDetailById(order.getCollectionId())
  723. .orElseThrow(new BusinessException("藏品不存在"));
  724. Cart cart = cartRepo
  725. .findFirstByCollectionIdAndUserIdAndDel(order.getCollectionId(), order.getUserId(), false);
  726. if (cart != null)
  727. cartRepo.delete(cart);
  728. User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  729. if (order.getStatus() == OrderStatus.NOT_PAID) {
  730. order.setStatus(OrderStatus.PROCESSING);
  731. order.setPayTime(LocalDateTime.now());
  732. order.setTransactionId(transactionId);
  733. order.setPayMethod(payMethod);
  734. if (order.getType() == CollectionType.BLIND_BOX) {
  735. log.info("开始盲盒抽卡 orderId: {}, collectionId: {}", orderId, collection.getId());
  736. BlindBoxItem winItem = collectionService.draw(order.getUserId(), collection.getId());
  737. log.info("抽卡成功 orderId: {}, collectionId: {}, winCollectionId: {}", orderId, collection
  738. .getId(), winItem.getCollectionId());
  739. order.setWinCollectionId(winItem.getCollectionId());
  740. orderRepo.save(order);
  741. //藏品其他信息/是否vip
  742. CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo
  743. .findByCollectionId(order.getCollectionId());
  744. if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
  745. if (collectionPrivilege.isVip()) {
  746. //更新vip信息
  747. userRepo.updateVipPurchase(order.getUserId(), 1);
  748. }
  749. }
  750. Collection winCollection = collectionRepo.findById(winItem.getCollectionId())
  751. .orElseThrow(new BusinessException("藏品不存在"));
  752. assetService.createAsset(winItem, user, order.getId(), order.getPrice(), "出售",
  753. winItem.getTotal() > 1 ? collectionService
  754. .getNextNumber(winItem.getCollectionId()) : null,
  755. winCollection.getHoldDays(), false);
  756. } else {
  757. if (collection.getSource() == CollectionSource.TRANSFER) {
  758. orderRepo.save(order);
  759. Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
  760. Set<Tag> tags = new HashSet<>(asset.getTags());
  761. boolean safeFlag = Objects
  762. .equals(true, redisTemplate.opsForValue().get("safeFlag::" + orderId));
  763. assetService.transfer(asset, order.getPrice(), user, TransferReason.TRANSFER,
  764. order.getId(), safeFlag, tags);
  765. order.setStatus(OrderStatus.FINISH);
  766. orderRepo.save(order);
  767. collectionRepo.delete(collection);
  768. cacheService.clearPrefixNameCount(collection.getPrefixName());
  769. // 如果展厅有此藏品
  770. showCollectionRepo.deleteAllByCollectionId(order.getCollectionId());
  771. // 发送短信提醒用户转让成功
  772. if (asset != null && asset.getUserId() != null) {
  773. smsService.sellOut(userRepo.findPhoneById(asset.getUserId()), order.getCompanyId());
  774. }
  775. riceService.addScoreInOrder(order.getUserId());
  776. userBalanceService.realtimeSettleOrder(order);
  777. if (collection.getType().equals(CollectionType.DOMAIN)) {
  778. NeteaseMessage neteaseMessage = new NeteaseMessage();
  779. neteaseMessage.setFromId("7209");
  780. neteaseMessage.setToId("9626109603");
  781. neteaseMessage.setType(0);
  782. neteaseMessage.setOpe(1);
  783. neteaseMessage
  784. .setFromAvatar("https://cdn.raex.vip/image/2022-12-22-11-08-11fdBrwzSL.png");
  785. neteaseMessage.setFromNickName("RID交易official");
  786. Map<String, String> object = new HashMap<>();
  787. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  788. String localDateTimeNowStr = order.getPayTime().format(formatter);
  789. // String info = "<div style=\"line-height: 40px;background: #06c04f;border-radius: 4px;padding: 0 20px;margin-bottom: 12px;\">RAEX绿洲:RID元域名称交易监控</div><div style=\"display: flex\"><img style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"src=\"https://cdn.raex.vip/image/2023-04-06-14-32-07vINcTaZP.png\"alt=\"\"/><span> RID:" + collection
  790. // .getName()
  791. // .substring(9) + "【已出售】</span></div><div style=\"display: flex\"><img style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"src=\"https://cdn.raex.vip/image/2023-04-06-14-28-32HcaKTDYb.png\"alt=\"\"/><span>分类:【RID元域名】</span></div><div style=\"display: flex\"><img style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"src=\"https://cdn.raex.vip/image/2023-04-06-14-31-49lldhGaqC.png\"alt=\"\"/><span>销售价格:【" + order
  792. // .getTotalPrice() + "】</span></div><div style=\"display: flex\"><img style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"src=\"https://cdn.raex.vip/image/2023-04-06-14-32-21ciilInpO.png\"alt=\"\"/><span>交易时间:<br/>【" + localDateTimeNowStr + "】</span></div>";
  793. String info = "<div style=\"padding: 4px 0\">" +
  794. " <div" +
  795. " style=\"" +
  796. " line-height: 40px;" +
  797. " background: #06c04f;" +
  798. " border-radius: 4px;" +
  799. " padding: 0 12px;" +
  800. " margin-bottom: 12px;" +
  801. " font-weight: bold;" +
  802. " \"" +
  803. " >" +
  804. " RAEX绿洲:RID元域名称交易监控" +
  805. " </div>" +
  806. " <div style=\"display: flex; margin-bottom: 8px\">" +
  807. " <img" +
  808. " style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"" +
  809. " src=\"https://cdn.raex.vip/image/2023-04-06-14-32-07vINcTaZP.png\"" +
  810. " alt=\"\"" +
  811. " />" +
  812. " <span> RID:"
  813. + collection
  814. .getName()
  815. .substring(9)
  816. + "【已出售】</span>" +
  817. " </div>" +
  818. " <div style=\"display: flex; margin-bottom: 8px\">" +
  819. " <img" +
  820. " style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"" +
  821. " src=\"https://cdn.raex.vip/image/2023-04-06-14-28-32HcaKTDYb.png\"" +
  822. " alt=\"\"" +
  823. " />" +
  824. " <span>分类: 【RID元域名】</span>" +
  825. " </div>" +
  826. " <div style=\"display: flex; margin-bottom: 8px\">" +
  827. " <img" +
  828. " style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"" +
  829. " src=\"https://cdn.raex.vip/image/2023-04-06-14-31-49lldhGaqC.png\"" +
  830. " alt=\"\"" +
  831. " />" +
  832. " <span>销售价格: 【" + order
  833. .getTotalPrice() + " 】</span>" +
  834. " </div>" +
  835. " <div style=\"display: flex\">" +
  836. " <img" +
  837. " style=\"width: 18px; height: 18px; margin: 5px 10px 0 0\"" +
  838. " src=\"https://cdn.raex.vip/image/2023-04-06-14-32-21ciilInpO.png\"" +
  839. " alt=\"\"" +
  840. " />" +
  841. " <span>交易时间:<br />【" + localDateTimeNowStr + "】</span>" +
  842. " </div>" +
  843. " </div>";
  844. // String info = "元域名:\"" + collection.getName().substring(9)
  845. // .replace(".nft", "") + "\"\n" +
  846. // "持有人:\"" + collection.getOwner() + "\"\n" +
  847. // "购买人:\"" + user.getNickname() + "\"\n" +
  848. // "时间:\"" + localDateTimeNowStr + "\"\n" +
  849. // "成交价:\"" + order.getTotalPrice() + "\"\n";
  850. object.put("msg", info);
  851. String news = JSONObject.toJSONString(object);
  852. neteaseMessage.setBody(news);
  853. neteaseMessageService.sendMessage(neteaseMessage);
  854. }
  855. } else {
  856. orderRepo.save(order);
  857. //藏品其他信息/是否vip
  858. CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo
  859. .findByCollectionId(order.getCollectionId());
  860. if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
  861. if (collectionPrivilege.isVip()) {
  862. //更新vip信息
  863. userRepo.updateVipPurchase(order.getUserId(), 1);
  864. }
  865. }
  866. Asset asset = assetService
  867. .createAsset(collection, user, order.getId(), order.getPrice(),
  868. "出售", collectionService.getNextNumber(collection), false);
  869. if (collection.getType() == CollectionType.SHOWROOM) {
  870. showroomService.save(asset);
  871. }
  872. }
  873. }
  874. commission(order);
  875. if (collection.getAssetId() == null) {
  876. collectionService.increaseSale(order.getCollectionId(), order.getQty());
  877. }
  878. //通过展厅购买加热力值
  879. List<HeatInfo> heatInfos = heatInfoRepo
  880. .findByUserIdAndOrderIdAndType(order.getUserId(), orderId, HeatType.BUY);
  881. if (CollUtil.isNotEmpty(heatInfos)) {
  882. HeatInfo heatInfo = heatInfos.get(0);
  883. int weight = sysConfigService.getInt("heat_buy_weight");
  884. heatInfo.setValue(weight);
  885. heatInfoRepo.save(heatInfo);
  886. showroomRepo.addHeat(heatInfo.getShowroomId(), weight);
  887. }
  888. } else {
  889. throw new BusinessException("状态错误 " + order.getStatus());
  890. }
  891. }
  892. } catch (Exception e) {
  893. ErrorOrder errorOrder = ErrorOrder.builder()
  894. .orderId(orderId)
  895. .transactionId(transactionId)
  896. .payMethod(payMethod)
  897. .build();
  898. if (e instanceof BusinessException) {
  899. log.error("订单回调出错 orderId: {} {}", orderId, e.getMessage());
  900. } else {
  901. log.error("订单回调出错 orderId: " + orderId, e);
  902. }
  903. errorOrder.setErrorMessage(e.getMessage());
  904. errorOrderRepo.save(errorOrder);
  905. }
  906. releaseOrderLock(orderId);
  907. }
  908. @EventListener
  909. public void onCreateAsset(CreateAssetEvent event) {
  910. Asset asset = event.getAsset();
  911. if (asset.getOrderId() != null) {
  912. Order order = orderRepo.findById(asset.getOrderId()).orElse(null);
  913. if (event.isSuccess() && order != null) {
  914. order.setTxHash(asset.getTxHash());
  915. order.setGasUsed(asset.getGasUsed());
  916. order.setBlockNumber(asset.getBlockNumber());
  917. order.setStatus(OrderStatus.FINISH);
  918. orderRepo.save(order);
  919. }
  920. }
  921. }
  922. @EventListener
  923. public void onHcMinted(MintedEvent event) {
  924. Asset asset = assetRepo.findById(event.getAssetId()).orElse(null);
  925. if (asset != null && asset.getOrderId() != null) {
  926. orderRepo.findById(asset.getOrderId()).ifPresent(order -> {
  927. order.setHcTxHash(asset.getTxHash());
  928. order.setHcGasUsed(asset.getGasUsed());
  929. order.setHcBlockNumber(asset.getBlockNumber());
  930. orderRepo.save(order);
  931. });
  932. }
  933. }
  934. @EventListener
  935. public void onTransferAsset(TransferAssetEvent event) {
  936. Asset asset = event.getAsset();
  937. Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException("订单不存在"));
  938. if (event.isSuccess()) {
  939. order.setTxHash(asset.getTxHash());
  940. order.setGasUsed(asset.getGasUsed());
  941. order.setBlockNumber(asset.getBlockNumber());
  942. order.setStatus(OrderStatus.FINISH);
  943. orderRepo.save(order);
  944. } else {
  945. log.error("创建asset失败");
  946. }
  947. }
  948. public void cancel(Long id) {
  949. Order order = orderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
  950. cancel(order);
  951. }
  952. public void cancel(Order order) {
  953. log.info("尝试取消订单 {}", order.getId());
  954. // 取消订单与订单回调不能同时进行,需要抢锁
  955. if (!getOrderLock(order.getId())) {
  956. log.error("订单取消失败 {}, redis锁了", order.getId());
  957. return;
  958. }
  959. try {
  960. if (order.getOrderType() == null) {
  961. order.setOrderType(OrderType.NORMAL);
  962. }
  963. if (order.getOrderType().equals(OrderType.MIX)) {
  964. order.setStatus(OrderStatus.CANCELLED);
  965. orderRepo.save(order);
  966. } else {
  967. if (order.getStatus() != OrderStatus.NOT_PAID) {
  968. throw new BusinessException("当前订单状态无法取消[" + order.getStatus().name() + "]");
  969. }
  970. CollectionSource source = Optional.ofNullable(order.getSource()).orElseGet(() ->
  971. collectionRepo.findById(order.getCollectionId()).map(Collection::getSource).orElse(null));
  972. if (source == CollectionSource.TRANSFER) {
  973. Asset asset = assetRepo.findById(order.getAssetId()).orElse(null);
  974. if (asset != null) {
  975. log.info("恢复藏品状态 assetId={}", asset.getId());
  976. asset.setStatus(AssetStatus.NORMAL);
  977. assetRepo.save(asset);
  978. }
  979. // collectionRepo.setOnShelf(order.getCollectionId(), true);
  980. log.info("取消支付中状态 collectionId={}", order.getId());
  981. collectionRepo.setInPaying(order.getCollectionId(), false);
  982. cacheService.clearPrefixNameCount(asset.getPrefixName());
  983. }
  984. collectionService.increaseStock(order.getCollectionId(), order.getQty());
  985. log.info("设置订单状态为取消 orderId={}", order.getId());
  986. order.setStatus(OrderStatus.CANCELLED);
  987. order.setCancelTime(LocalDateTime.now());
  988. orderRepo.save(order);
  989. if (order.getCouponId() != null) {
  990. userCouponRepo.findById(order.getCouponId()).ifPresent(coupon -> {
  991. log.info("恢复优惠券状态 couponId={}", coupon.getId());
  992. coupon.setUsed(false);
  993. coupon.setUseTime(null);
  994. userCouponRepo.save(coupon);
  995. });
  996. }
  997. //加上积分
  998. if (ObjectUtils.isNotEmpty(order.getVipPoint()) && order.getVipPoint() > 0) {
  999. userRepo.addVipPoint(order.getUserId(), order.getVipPoint());
  1000. cacheService.clearUserMy(order.getUserId());
  1001. log.info("取消加积分用户ID:{},订单ID:{},积分:{}", order.getUserId(), order.getId(), order.getVipPoint());
  1002. }
  1003. if (order.isVip()) {
  1004. collectionService.decreaseQuota(order.getCollectionId(), 1);
  1005. log.info("取消加藏品额度CollectionId:{}", order.getCollectionId());
  1006. }
  1007. rocketMQTemplate.syncSend(generalProperties.getUpdateQuotaTopic(), order.getCollectionId(), 10000);
  1008. log.info("取消订单完成 orderId={}", order.getId());
  1009. }
  1010. } catch (Exception e) {
  1011. if (e instanceof BusinessException) {
  1012. log.error(e.getMessage());
  1013. } else {
  1014. log.error("订单取消错误 orderId: " + order.getId(), e);
  1015. }
  1016. }
  1017. releaseOrderLock(order.getId());
  1018. }
  1019. public void refundCancelled(Order order) {
  1020. }
  1021. public void setNumber() {
  1022. for (Collection collection : collectionRepo.findAll()) {
  1023. if (collection.getSource() != CollectionSource.OFFICIAL) continue;
  1024. collection.setCurrentNumber(0);
  1025. collectionRepo.save(collection);
  1026. for (Asset asset : assetRepo.findByCollectionId(collection.getId())) {
  1027. if (asset.getStatus() == AssetStatus.GIFTED || asset.getStatus() == AssetStatus.TRANSFERRED) {
  1028. } else {
  1029. asset.setNumber(collectionService.getNextNumber(collection.getId()));
  1030. assetRepo.save(asset);
  1031. }
  1032. }
  1033. }
  1034. }
  1035. public void setNumberRecursive(Asset asset) {
  1036. }
  1037. @Scheduled(cron = "0 0 4 * * ?")
  1038. public void setSales() {
  1039. if (generalProperties.isNotifyServer()) {
  1040. return;
  1041. }
  1042. List<User> minters = userRepo.findByAuthoritiesContains(Authority.get(AuthorityName.ROLE_MINTER));
  1043. for (User minter : minters) {
  1044. userRepo.setSales(minter.getId(), (int) orderRepo.countSales(minter.getId()));
  1045. }
  1046. }
  1047. public void commission(Order order) {
  1048. if (order.getInvitor() != null) {
  1049. userRepo.findById(order.getInvitor()).ifPresent(user -> {
  1050. BigDecimal shareRatio = user.getShareRatio();
  1051. if (StringUtils.isNotBlank(user.getSettleAccountId()) &&
  1052. shareRatio != null && shareRatio.compareTo(BigDecimal.ZERO) > 0) {
  1053. BigDecimal totalPrice = order.getTotalPrice().subtract(order.getGasPrice());
  1054. commissionRecordRepo.save(CommissionRecord.builder()
  1055. .orderId(order.getId())
  1056. .collectionId(order.getCollectionId())
  1057. .name(order.getName())
  1058. .totalPrice(totalPrice)
  1059. .nickname(user.getNickname())
  1060. .userId(user.getId())
  1061. .shareRatio(user.getShareRatio())
  1062. .phone(user.getPhone())
  1063. .shareAmount(totalPrice.multiply(shareRatio)
  1064. .divide(BigDecimal
  1065. .valueOf(100), 2, RoundingMode.HALF_UP))
  1066. .type(InviteType.NORMAL)
  1067. .build());
  1068. }
  1069. });
  1070. }
  1071. }
  1072. public Object queryCreateOrder(String id) {
  1073. Object res = redisTemplate.opsForValue().get(RedisKeys.CREATE_ORDER + id);
  1074. if (res != null) {
  1075. if (res instanceof Map) {
  1076. if (MapUtils.getBooleanValue((Map) res, "success", false)) {
  1077. Order order = (Order) MapUtils.getObject((Map) res, "data");
  1078. if (!SecurityUtils.getAuthenticatedUser().getId().equals(order.getUserId())) {
  1079. log.error("queryCreateOrder userId错误 requestUserId={} orderUserId={}",
  1080. SecurityUtils.getAuthenticatedUser().getId(), order.getUserId());
  1081. return null;
  1082. }
  1083. }
  1084. }
  1085. }
  1086. return res;
  1087. }
  1088. // 获取订单锁,有效时间1小时
  1089. public boolean getOrderLock(Long orderId) {
  1090. BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.ORDER_LOCK + orderId);
  1091. Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.HOURS);
  1092. return Boolean.TRUE.equals(flag);
  1093. }
  1094. // 释放订单锁
  1095. public void releaseOrderLock(Long orderId) {
  1096. redisTemplate.delete(RedisKeys.ORDER_LOCK + orderId);
  1097. }
  1098. public void calcSettle(LocalDateTime start, LocalDateTime end, OutputStream outputStream) {
  1099. List<Order> orders = orderRepo
  1100. .findByCreatedAtBetweenAndSourceAndStatusIn(start, end, CollectionSource.TRANSFER, Arrays
  1101. .asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
  1102. List<Asset> assets = assetRepo.findAllById(orders.stream().map(Order::getAssetId).collect(Collectors.toSet()));
  1103. List<UserBankCard> bankCards = userBankCardRepo
  1104. .findByUserIdIn(assets.stream().map(Asset::getUserId).collect(Collectors.toSet()));
  1105. List<MarketSettlement> settlements = new ArrayList<>();
  1106. for (Order order : orders) {
  1107. BigDecimal amount = order.getTotalPrice()
  1108. .subtract(order.getGasPrice())
  1109. .multiply(new BigDecimal("100")
  1110. .subtract(BigDecimal.valueOf(order.getServiceCharge()))
  1111. .subtract(BigDecimal.valueOf(order.getRoyalties()))
  1112. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP))
  1113. .setScale(2, RoundingMode.HALF_UP);
  1114. Long userId = assets.stream().filter(a -> a.getId().equals(order.getAssetId())).map(Asset::getUserId)
  1115. .findAny().orElse(null);
  1116. if (userId != null) {
  1117. UserBankCard userBankCard = bankCards.stream().filter(b -> b.getUserId().equals(userId)).findAny()
  1118. .orElse(null);
  1119. MarketSettlement marketSettlement = settlements.stream().filter(s -> s.getUserId().equals(userId))
  1120. .findAny().orElse(null);
  1121. if (marketSettlement == null) {
  1122. marketSettlement = new MarketSettlement(userId,
  1123. Optional.ofNullable(userBankCard).map(UserBankCard::getRealName).orElse(null),
  1124. Optional.ofNullable(userBankCard).map(UserBankCard::getBankNo).orElse(null),
  1125. amount);
  1126. settlements.add(marketSettlement);
  1127. } else {
  1128. marketSettlement.setAmount(marketSettlement.getAmount()
  1129. .add(amount));
  1130. }
  1131. }
  1132. }
  1133. EasyExcel.write(outputStream, MarketSettlement.class).sheet("sheet").doWrite(settlements);
  1134. }
  1135. @Scheduled(cron = "0 0/3 * * * ?")
  1136. public void setBlackList() {
  1137. List<Long> userIds = orderRepo
  1138. .checkBlackList(LocalDateTime.now(), LocalDateTime.now().minusMinutes(30));
  1139. userIds.forEach(userId -> {
  1140. if (redisTemplate.opsForValue().get(RedisKeys.BLACK_LIST + userId) == null) {
  1141. redisTemplate.opsForValue().set(RedisKeys.BLACK_LIST + userId, 1, Duration.ofSeconds(60 * 180));
  1142. }
  1143. });
  1144. }
  1145. public List<Order> addOrder(Long collectionId, List<Long> userIds, LocalDateTime time, boolean notify) {
  1146. Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("藏品不存在"));
  1147. User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("用户不存在"));
  1148. int i = 0;
  1149. List<Order> list = new ArrayList<>();
  1150. for (Long userId : userIds) {
  1151. time = time.plusSeconds(i++);
  1152. BigDecimal gasFee = new BigDecimal("1");
  1153. Order order = Order.builder()
  1154. .id(snowflakeIdWorker.nextId())
  1155. .userId(userId)
  1156. .collectionId(collectionId)
  1157. .name(collection.getName())
  1158. .pic(collection.getPic())
  1159. .detail(collection.getDetail())
  1160. .properties(collection.getProperties())
  1161. .category(collection.getCategory())
  1162. .canResale(collection.isCanResale())
  1163. .royalties(collection.getRoyalties())
  1164. .serviceCharge(collection.getServiceCharge())
  1165. .type(collection.getType())
  1166. .source(collection.getSource())
  1167. .minterId(collection.getMinterId())
  1168. .minter(minter.getNickname())
  1169. .minterAvatar(minter.getAvatar())
  1170. .qty(1)
  1171. .price(collection.getPrice())
  1172. .gasPrice(gasFee)
  1173. .totalPrice(collection.getPrice().multiply(BigDecimal.valueOf(1)).add(gasFee))
  1174. .status(notify ? OrderStatus.NOT_PAID : OrderStatus.FINISH)
  1175. .assetId(collection.getAssetId())
  1176. .countId(collection.getCountId())
  1177. .build();
  1178. orderRepo.saveAndFlush(order);
  1179. String txHash = RandomStringUtils.randomAlphanumeric(64).toLowerCase(Locale.ROOT);
  1180. String transactionId = DateTimeUtils.format(LocalDateTime.now(), "yyyyMMdd") +
  1181. RandomStringUtils.randomNumeric(24);
  1182. BigInteger gas = new BigInteger("157377");
  1183. if (notify) {
  1184. rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
  1185. new OrderNotifyEvent(order.getId(), PayMethod.ALIPAY, order.getTransactionId()
  1186. , System.currentTimeMillis()));
  1187. } else {
  1188. order.setCreatedAt(ObjectUtils.clone(time));
  1189. order.setPayTime(time.plusSeconds(RandomUtils.nextInt(5, 50)));
  1190. order.setTransactionId(transactionId);
  1191. order.setTxHash(txHash);
  1192. order.setGasUsed(gas);
  1193. order.setPayMethod(PayMethod.ALIPAY);
  1194. order.setStatus(OrderStatus.FINISH);
  1195. order.setModifiedAt(order.getPayTime());
  1196. order.setModifiedBy("system");
  1197. order.setCreatedBy("system");
  1198. orderRepo.save(order);
  1199. }
  1200. list.add(order);
  1201. }
  1202. return list;
  1203. }
  1204. public Page<Order> byTag(Long tagId, List<Long> excludeUserId, Pageable pageable) {
  1205. if (excludeUserId.isEmpty()) {
  1206. excludeUserId.add(0L);
  1207. }
  1208. return orderRepo.findAll((root, query, criteriaBuilder) -> {
  1209. Join join = root.join("tags");
  1210. return criteriaBuilder.and(criteriaBuilder.equal(join.get("id"), tagId),
  1211. criteriaBuilder.equal(root.get("status"), OrderStatus.FINISH),
  1212. criteriaBuilder.equal(root.get("source"), CollectionSource.TRANSFER),
  1213. criteriaBuilder.not(root.get("ownerId").in(excludeUserId)));
  1214. }, pageable);
  1215. }
  1216. @Async
  1217. public void refundGas() throws ExecutionException, InterruptedException {
  1218. new ForkJoinPool(500).submit(() -> {
  1219. List<Order> list = orderRepo.findByCollectionId(8657801L).stream()
  1220. .filter(o -> o.getStatus() == OrderStatus.FINISH || o
  1221. .getStatus() == OrderStatus.PROCESSING)
  1222. .collect(Collectors.toList());
  1223. list.parallelStream().forEach(o -> {
  1224. log.info("refundGas {}", o.getId());
  1225. userBalanceService
  1226. .modifyBalance(o.getUserId(), new BigDecimal(1), BalanceType.REFUND, null, false, null);
  1227. });
  1228. }).get();
  1229. }
  1230. public Map<String, Object> domainTransferInfo(int type) {
  1231. Set<String> prefixNames = new HashSet<>();
  1232. switch (type) {
  1233. case 0:
  1234. prefixNames.add("RIDCX");
  1235. break;
  1236. case 1:
  1237. prefixNames.add("RID");
  1238. prefixNames.add("RID1");
  1239. prefixNames.add("RID2");
  1240. prefixNames.add("RID3");
  1241. prefixNames.add("RID4");
  1242. prefixNames.add("RID5");
  1243. break;
  1244. case 2:
  1245. prefixNames.add("RIDN1");
  1246. prefixNames.add("RIDN2");
  1247. prefixNames.add("RIDN3");
  1248. prefixNames.add("RIDN4");
  1249. prefixNames.add("RIDN5");
  1250. prefixNames.add("RIDN6");
  1251. break;
  1252. }
  1253. //流通量
  1254. int transferringCount = assetRepo.countAllByPrefixNameInAndStatusAndDel(prefixNames, AssetStatus.NORMAL, false);
  1255. //寄售中
  1256. int consignment = assetRepo
  1257. .countAllByPrefixNameInAndStatusAndConsignmentAndDel(prefixNames, AssetStatus.NORMAL, true, false);
  1258. //仅展示
  1259. int publicShow = assetRepo
  1260. .countAllByPrefixNameInAndStatusAndConsignmentAndPublicShowAndDel(prefixNames, AssetStatus.NORMAL, false, true, false);
  1261. Map<String, Object> result = new HashMap<>();
  1262. result.put("transferringCount", transferringCount);
  1263. result.put("consignment", consignment);
  1264. result.put("publicShow", publicShow);
  1265. return result;
  1266. }
  1267. public Page<DomainOrderDTO> newestDomainTransfer(int type, Pageable pageable) {
  1268. Set<String> prefixNames = new HashSet<>();
  1269. switch (type) {
  1270. case 0:
  1271. prefixNames.add("RIDCX");
  1272. break;
  1273. case 1:
  1274. prefixNames.add("RID");
  1275. prefixNames.add("RID1");
  1276. prefixNames.add("RID2");
  1277. prefixNames.add("RID3");
  1278. prefixNames.add("RID4");
  1279. prefixNames.add("RID5");
  1280. break;
  1281. case 2:
  1282. prefixNames.add("RIDN1");
  1283. prefixNames.add("RIDN2");
  1284. prefixNames.add("RIDN3");
  1285. prefixNames.add("RIDN4");
  1286. prefixNames.add("RIDN5");
  1287. prefixNames.add("RIDN6");
  1288. break;
  1289. }
  1290. Page<Order> orders = orderRepo.queryDomainOrder(prefixNames, pageable);
  1291. List<Order> orderContent = orders.getContent();
  1292. List<DomainOrderDTO> domainOrderDTOS = new ArrayList<>();
  1293. orderContent.forEach(order -> {
  1294. Asset asset = assetRepo.findById(order.getAssetId()).orElse(null);
  1295. if (asset != null) {
  1296. DomainOrderDTO domainOrderDTO = new DomainOrderDTO();
  1297. domainOrderDTO.setName(order.getName());
  1298. domainOrderDTO.setOwner(asset.getOwner());
  1299. domainOrderDTO.setOwnerAvatar(asset.getOwnerAvatar());
  1300. domainOrderDTO.setPrice(order.getPrice());
  1301. domainOrderDTO.setPayTime(order.getPayTime().toLocalDate());
  1302. domainOrderDTO.setPic(order.getPic());
  1303. domainOrderDTOS.add(domainOrderDTO);
  1304. }
  1305. });
  1306. return new PageImpl<>(domainOrderDTOS, orders.getPageable(), orders.getTotalElements());
  1307. }
  1308. }