Browse Source

Merge branch 'dev' of http://git.izouma.com/xiongzhu/9th into dev

panhui 4 years ago
parent
commit
ec35546675
30 changed files with 550 additions and 324 deletions
  1. 19 1
      pom.xml
  2. 18 5
      src/main/java/com/izouma/nineth/config/GeneralProperties.java
  3. 13 0
      src/main/java/com/izouma/nineth/config/SnowflakeIdWorkerConfig.java
  4. 20 0
      src/main/java/com/izouma/nineth/config/WebMvcConfig.java
  5. 21 0
      src/main/java/com/izouma/nineth/domain/AdaTrade.java
  6. 31 0
      src/main/java/com/izouma/nineth/event/AccountCreatedEvent.java
  7. 12 0
      src/main/java/com/izouma/nineth/event/MyMqEvent.java
  8. 30 0
      src/main/java/com/izouma/nineth/listener/MintListener.java
  9. 3 0
      src/main/java/com/izouma/nineth/repo/AssetRepo.java
  10. 13 0
      src/main/java/com/izouma/nineth/repo/CollectionRepo.java
  11. 2 0
      src/main/java/com/izouma/nineth/repo/GiftOrderRepo.java
  12. 2 0
      src/main/java/com/izouma/nineth/repo/OrderRepo.java
  13. 3 4
      src/main/java/com/izouma/nineth/security/JwtAuthorizationTokenFilter.java
  14. 1 0
      src/main/java/com/izouma/nineth/security/WebSecurityConfig.java
  15. 3 3
      src/main/java/com/izouma/nineth/service/AssetMintService.java
  16. 8 4
      src/main/java/com/izouma/nineth/service/AssetService.java
  17. 17 0
      src/main/java/com/izouma/nineth/service/CollectionService.java
  18. 2 2
      src/main/java/com/izouma/nineth/service/IdentityAuthService.java
  19. 37 7
      src/main/java/com/izouma/nineth/service/NFTService.java
  20. 25 13
      src/main/java/com/izouma/nineth/service/OrderService.java
  21. 16 1
      src/main/java/com/izouma/nineth/service/UserService.java
  22. 1 1
      src/main/java/com/izouma/nineth/utils/JpaUtils.java
  23. 23 0
      src/main/java/com/izouma/nineth/web/RocketMqController.java
  24. 12 0
      src/main/java/com/izouma/nineth/web/UserController.java
  25. 49 1
      src/main/resources/application.yaml
  26. 16 130
      src/main/resources/logback-spring.xml
  27. 1 1
      src/main/vue/src/views/OrderList.vue
  28. 94 3
      src/main/vue/src/views/UserList.vue
  29. 57 147
      src/test/java/com/izouma/nineth/service/AdapayServiceTest.java
  30. 1 1
      src/test/java/com/izouma/nineth/service/AdapayTest.java

+ 19 - 1
pom.xml

@@ -404,7 +404,25 @@
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>druid-spring-boot-starter</artifactId>
-            <version>1.1.21</version>
+            <version>1.2.8</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-spring-boot-starter</artifactId>
+            <version>2.2.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-validator</groupId>
+            <artifactId>commons-validator</artifactId>
+            <version>1.7</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-core</artifactId>
+            <version>5.7.21</version>
         </dependency>
     </dependencies>
 

+ 18 - 5
src/main/java/com/izouma/nineth/config/GeneralProperties.java

@@ -6,9 +6,22 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 @ConfigurationProperties(prefix = "general")
 @Data
 public class GeneralProperties {
-    private String host;
-    private String contractName;
-    private String name;
-    private String org;
-    private String shortName;
+    private String  host;
+    private String  contractName;
+    private String  name;
+    private String  org;
+    private String  shortName;
+    private String  createOrderGroup;
+    private String  createOrderTopic;
+    private String  updateStockGroup;
+    private String  updateStockTopic;
+    private String  updateSaleGroup;
+    private String  updateSaleTopic;
+    private String  orderNotifyGroup;
+    private String  orderNotifyTopic;
+    private String  mintGroup;
+    private String  mintTopic;
+    private boolean notifyServer;
+    private String  updateActivityStockGroup;
+    private String  updateActivityStockTopic;
 }

+ 13 - 0
src/main/java/com/izouma/nineth/config/SnowflakeIdWorkerConfig.java

@@ -0,0 +1,13 @@
+package com.izouma.nineth.config;
+
+import com.izouma.nineth.utils.SnowflakeIdWorker;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class SnowflakeIdWorkerConfig {
+    @Bean
+    public SnowflakeIdWorker snowflakeIdWorker() {
+        return new SnowflakeIdWorker(0, 0);
+    }
+}

+ 20 - 0
src/main/java/com/izouma/nineth/config/WebMvcConfig.java

@@ -1,9 +1,14 @@
 package com.izouma.nineth.config;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.web.servlet.config.annotation.CorsRegistry;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -13,6 +18,8 @@ import springfox.documentation.builders.RequestHandlerSelectors;
 import springfox.documentation.spi.DocumentationType;
 import springfox.documentation.spring.web.plugins.Docket;
 
+import java.util.List;
+
 @Configuration
 @EnableConfigurationProperties(GeneralProperties.class)
 public class WebMvcConfig implements WebMvcConfigurer {
@@ -44,6 +51,19 @@ public class WebMvcConfig implements WebMvcConfigurer {
                 .build();
     }
 
+    @Override
+    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+        converters.stream().filter(converter -> converter instanceof MappingJackson2HttpMessageConverter)
+                .forEach(converter -> {
+                    ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
+                    SimpleModule simpleModule = new SimpleModule();
+                    simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
+                    simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
+                    objectMapper.registerModule(simpleModule);
+                    ((MappingJackson2HttpMessageConverter) converter).setObjectMapper(objectMapper);
+                });
+        System.out.println(converters);
+    }
     // @Bean
     // public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
     //     MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();

+ 21 - 0
src/main/java/com/izouma/nineth/domain/AdaTrade.java

@@ -0,0 +1,21 @@
+package com.izouma.nineth.domain;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+@Data
+public class AdaTrade {
+    private String 交易时间;
+    private String 订单号;
+    private String 支付流水号;
+    private String 账户号;
+    private String 应用;
+    private String 支付渠道;
+    private String 交易状态;
+    private String 交易金额;
+    private String 手续费金额;
+    private String 第三方订单号;
+    @ExcelProperty("支付宝/微信订单号")
+    private String 支付宝_微信订单号;
+    private String 备注描述;
+}

+ 31 - 0
src/main/java/com/izouma/nineth/event/AccountCreatedEvent.java

@@ -0,0 +1,31 @@
+package com.izouma.nineth.event;
+
+import com.izouma.nineth.dto.NFTAccount;
+import org.springframework.context.ApplicationEvent;
+
+public class AccountCreatedEvent extends ApplicationEvent {
+    private final Long       userId;
+    private final NFTAccount account;
+
+    /**
+     * Create a new {@code ApplicationEvent}.
+     *
+     * @param source  the object on which the event initially occurred or with
+     *                which the event is associated (never {@code null})
+     * @param userId
+     * @param account
+     */
+    public AccountCreatedEvent(Object source, Long userId, NFTAccount account) {
+        super(source);
+        this.userId = userId;
+        this.account = account;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public NFTAccount getAccount() {
+        return account;
+    }
+}

+ 12 - 0
src/main/java/com/izouma/nineth/event/MyMqEvent.java

@@ -0,0 +1,12 @@
+package com.izouma.nineth.event;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class MyMqEvent {
+    private String data;
+}

+ 30 - 0
src/main/java/com/izouma/nineth/listener/MintListener.java

@@ -0,0 +1,30 @@
+package com.izouma.nineth.listener;
+
+import com.izouma.nineth.service.AssetMintService;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.ConsumeMode;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+@RocketMQMessageListener(
+        consumerGroup = "${general.mint-group}",
+        topic = "${general.mint-topic}",
+        consumeMode = ConsumeMode.ORDERLY)
+//@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
+public class MintListener implements RocketMQListener<Long> {
+    private AssetMintService assetMintService;
+
+    @Override
+    public void onMessage(Long assetId) {
+        try {
+            assetMintService.mint(assetId);
+        } catch (Exception e) {
+            log.error("铸造失败 " + assetId, e);
+        }
+    }
+}

+ 3 - 0
src/main/java/com/izouma/nineth/repo/AssetRepo.java

@@ -45,4 +45,7 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
     Asset findFirstByTokenIdAndCreatedAtAfterOrderByCreatedAt(String tokenId, LocalDateTime time);
 
     List<Asset> findByTxHashIsNullAndTokenIdNotNullAndCreatedAtBefore(LocalDateTime time);
+
+    @Query("select id from Asset where status = ?1")
+    List<Long> findAllByStatus(AssetStatus status);
 }

+ 13 - 0
src/main/java/com/izouma/nineth/repo/CollectionRepo.java

@@ -95,4 +95,17 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
 
     @Query("select c.currentNumber from Collection c where c.id = ?1")
     Optional<Integer> getCurrentNumber(Long id);
+
+    @Query("select c.id from Collection c where c.assetId in ?1")
+    List<Long> findAllByDelFalseAndAssetIdIn(java.util.Collection<Long> assetId);
+
+    @Query("select c.id from Collection c where c.assetId = ?1 and c.source = 'TRANSFER'")
+    List<Long> findAllByAssetId(Long assetId);
+
+    @Query("update Collection t set t.del = true where t.id in ?1")
+    @Modifying
+    @Transactional
+    void softDeleteByIdIn(java.util.Collection<Long> id);
+
+    void deleteAllByIdIn(java.util.Collection<Long> id);
 }

+ 2 - 0
src/main/java/com/izouma/nineth/repo/GiftOrderRepo.java

@@ -18,4 +18,6 @@ public interface GiftOrderRepo extends JpaRepository<GiftOrder, Long>, JpaSpecif
     @Modifying
     @Transactional
     void softDelete(Long id);
+
+    GiftOrder findByTransactionId(String txId);
 }

+ 2 - 0
src/main/java/com/izouma/nineth/repo/OrderRepo.java

@@ -33,4 +33,6 @@ public interface OrderRepo extends JpaRepository<Order, Long>, JpaSpecificationE
     @Query("select count(o) from Order o join Collection c on o.collectionId = c.id " +
             "where c.minterId = ?1 and c.source = 'OFFICIAL' and o.status <> 'NOT_PAID' and o.status <> 'CANCELLED'")
     long countSales(Long userId);
+
+    Order findByTransactionId(String txId);
 }

+ 3 - 4
src/main/java/com/izouma/nineth/security/JwtAuthorizationTokenFilter.java

@@ -1,6 +1,5 @@
 package com.izouma.nineth.security;
 
-import com.izouma.nineth.repo.UserTokenRepo;
 import io.jsonwebtoken.ExpiredJwtException;
 import io.jsonwebtoken.SignatureException;
 import lombok.extern.slf4j.Slf4j;
@@ -49,12 +48,12 @@ public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
             } catch (IllegalArgumentException e) {
                 log.error("an error occurred during getting username from token", e);
             } catch (ExpiredJwtException e) {
-                log.warn("the token is expired and not valid anymore", e);
+                log.debug("the token is expired and not valid anymore");
             } catch (SignatureException e) {
                 log.error(e.getMessage());
             }
         } else {
-            log.warn("couldn't find bearer string, will ignore the header");
+            log.debug("couldn't find bearer string, will ignore the header");
         }
 
         log.debug("checking authentication for user '{}'", username);
@@ -78,7 +77,7 @@ public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
             if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                 authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
-                log.info("authorized user '{}', setting security context", username);
+                log.debug("authorized user '{}', setting security context", username);
                 SecurityContextHolder.getContext().setAuthentication(authentication);
             }
         }

+ 1 - 0
src/main/java/com/izouma/nineth/security/WebSecurityConfig.java

@@ -99,6 +99,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/activity/all").permitAll()
                 .antMatchers("/activity/get/*").permitAll()
                 .antMatchers("/druid/**").permitAll()
+                .antMatchers("/testmq/**").permitAll()
                 // all other requests need to be authenticated
                 .anyRequest().authenticated().and()
                 // make sure we use stateless session; session won't be used to

+ 3 - 3
src/main/java/com/izouma/nineth/service/AssetMintService.java

@@ -32,8 +32,9 @@ public class AssetMintService {
     private ApplicationContext applicationContext;
 
     @Async
-    public void mint(Long assetId, Long userId) {
-        User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
+    public void mint(Long assetId) {
+        Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("asset不存在"));
+        User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
         if (StringUtils.isEmpty(user.getPublicKey())) {
             NFTAccount account = nftService.createAccount(user.getUsername() + "_");
             user.setNftAccount(account.getAccountId());
@@ -42,7 +43,6 @@ public class AssetMintService {
             userRepo.save(user);
         }
         try {
-            Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("asset不存在"));
             NFT nft = nftService.createToken(user.getNftAccount(), asset.getTokenId());
             if (nft != null) {
                 asset.setTokenId(nft.getTokenId());

+ 8 - 4
src/main/java/com/izouma/nineth/service/AssetService.java

@@ -1,6 +1,7 @@
 package com.izouma.nineth.service;
 
 import com.izouma.nineth.TokenHistory;
+import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PageQuery;
@@ -19,6 +20,7 @@ import com.izouma.nineth.utils.TokenUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.beans.BeanUtils;
 import org.springframework.context.ApplicationContext;
 import org.springframework.data.domain.Page;
@@ -43,8 +45,9 @@ public class AssetService {
     private ApplicationContext applicationContext;
     private OrderRepo          orderRepo;
     private TokenHistoryRepo   tokenHistoryRepo;
-    private AssetMintService   assetMintService;
     private SysConfigService   sysConfigService;
+    private RocketMQTemplate   rocketMQTemplate;
+    private GeneralProperties  generalProperties;
 
     public Page<Asset> all(PageQuery pageQuery) {
         return assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
@@ -56,7 +59,7 @@ public class AssetService {
         asset.setNumber(number);
         asset.setOrderId(orderId);
         asset.setPrice(price);
-        assetRepo.save(asset);
+        assetRepo.saveAndFlush(asset);
 
         tokenHistoryRepo.save(TokenHistory.builder()
                 .tokenId(asset.getTokenId())
@@ -70,7 +73,7 @@ public class AssetService {
                 .price(price)
                 .projectId(asset.getProjectId())
                 .build());
-        assetMintService.mint(asset);
+        rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
         return asset;
     }
 
@@ -94,7 +97,7 @@ public class AssetService {
                 .price(price)
                 .projectId(asset.getProjectId())
                 .build());
-        assetMintService.mint(asset.getId(), user.getId());
+        rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
         return asset;
     }
 
@@ -265,6 +268,7 @@ public class AssetService {
 
     @Async
     public void transfer(Asset asset, BigDecimal price, User toUser, String reason, Long orderId) {
+        log.info("转让藏品 fromAssetId={} toUser={}", asset.getId(), toUser.getId());
         Asset newAsset = new Asset();
         BeanUtils.copyProperties(asset, newAsset);
         newAsset.setId(null);

+ 17 - 0
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -6,6 +6,7 @@ import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.CollectionDTO;
 import com.izouma.nineth.dto.CreateBlindBox;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.AssetStatus;
 import com.izouma.nineth.enums.CollectionSource;
 import com.izouma.nineth.enums.CollectionType;
 import com.izouma.nineth.exception.BusinessException;
@@ -13,6 +14,8 @@ import com.izouma.nineth.repo.*;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.RandomUtils;
 import org.apache.commons.lang3.Range;
@@ -24,6 +27,7 @@ import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
@@ -35,6 +39,7 @@ import java.util.*;
 import java.util.concurrent.ScheduledFuture;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service
 @AllArgsConstructor
 public class CollectionService {
@@ -46,6 +51,7 @@ public class CollectionService {
     private UserRepo         userRepo;
     private TaskScheduler    taskScheduler;
     private CacheService     cacheService;
+    private AssetRepo        assetRepo;
 
     private final Map<Long, ScheduledFuture<?>> tasks = new HashMap<>();
 
@@ -335,4 +341,15 @@ public class CollectionService {
         collection.setStock(collection.getStock() + number);
         collection.setTotal(collection.getTotal() + number);
     }
+
+//    @Scheduled(cron = "0 0 1 ?")
+    public void delCollection() {
+        List<Long> assetIds = assetRepo.findAllByStatus(AssetStatus.TRANSFERRED);
+        List<Long> collections = collectionRepo.findAllByDelFalseAndAssetIdIn(assetIds);
+        if (CollectionUtils.isEmpty(collections)) {
+            return;
+        }
+        log.info("定时任务删除未删除藏品:{}", collections);
+        collectionRepo.softDeleteByIdIn(collections);
+    }
 }

+ 2 - 2
src/main/java/com/izouma/nineth/service/IdentityAuthService.java

@@ -53,9 +53,9 @@ public class IdentityAuthService {
         if (status == AuthStatus.SUCCESS) {
             user.setAuthId(auth.getId());
         }
-        auth.setStatus(status);
-        identityAuthRepo.save(auth);
         user.setAuthStatus(status);
         userRepo.save(user);
+        auth.setStatus(status);
+        identityAuthRepo.save(auth);
     }
 }

+ 37 - 7
src/main/java/com/izouma/nineth/service/NFTService.java

@@ -10,21 +10,26 @@ import com.antfinancial.mychain.baas.tool.restclient.model.CallRestBizParam;
 import com.antfinancial.mychain.baas.tool.restclient.model.Method;
 import com.antfinancial.mychain.baas.tool.restclient.model.ReceiptDecoration;
 import com.antfinancial.mychain.baas.tool.restclient.response.BaseResp;
-import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.config.Constants;
+import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.dto.NFT;
 import com.izouma.nineth.dto.NFTAccount;
+import com.izouma.nineth.event.AccountCreatedEvent;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.utils.HashUtils;
 import com.izouma.nineth.utils.SnowflakeIdWorker;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.Environment;
 import org.springframework.retry.annotation.Backoff;
 import org.springframework.retry.annotation.Retryable;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 @Service
@@ -34,11 +39,30 @@ public class NFTService {
     private final RestClient           restClient;
     private final RestClientProperties restClientProperties;
     private final GeneralProperties    generalProperties;
+    private final SnowflakeIdWorker    snowflakeIdWorker;
+    private final ApplicationContext   context;
+    private final Environment          env;
+
+    @Async
+    public void createAccount(Long userId) {
+        NFTAccount account = null;
+        for (int i = 0; i < 10; i++) {
+            try {
+                Thread.sleep(5000);
+                account = createAccount("account_" + userId);
+                break;
+            } catch (Exception e) {
+            }
+        }
+        if (account != null) {
+            context.publishEvent(new AccountCreatedEvent(this, userId, account));
+        }
+    }
 
     @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
     public NFTAccount createAccount(String username) {
         CallRestBizParam callRestBizParam = CallRestBizParam.builder()
-                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
+                .orderId(String.valueOf(snowflakeIdWorker.nextId()))
                 .bizid(Constants.bizId)
                 .account(restClientProperties.getAccount())
                 .mykmsKeyId(restClientProperties.getKmsId())
@@ -64,11 +88,17 @@ public class NFTService {
 
     @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
     public synchronized NFT createToken(String toAccount, String tokenId) throws Exception {
+        log.info("开始执行EVM合约mint, toAccount: {}, tokenId: {}", toAccount, tokenId);
+        if (Arrays.asList(env.getActiveProfiles()).contains("test")) {
+            NFT nft = new NFT("1", tokenId, new BigInteger("1"), new BigInteger("1"));
+            log.info("测试服生成假token {}", tokenId);
+            return nft;
+        }
         JSONArray jsonArray = new JSONArray();
         jsonArray.add(Utils.getIdentityByName(toAccount));
         jsonArray.add(new BigInteger(tokenId, 16));
         CallRestBizParam callRestBizParam = CallRestBizParam.builder()
-                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
+                .orderId(String.valueOf(snowflakeIdWorker.nextId()))
                 .bizid(restClientProperties.getBizid())
                 .account(restClientProperties.getAccount())
                 .contractName(generalProperties.getContractName())
@@ -82,7 +112,7 @@ public class NFTService {
                 .build();
         BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
         if (!resp.isSuccess()) {
-            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
+            log.info("EVM合约执行失败: code: {}, data: {}, toAccount: {}, tokenId: {}", resp.getCode(), resp.getData(), toAccount, tokenId);
         }
         if ("200".equals(resp.getCode())) {
             log.info("EVM合约执行成功");
@@ -95,18 +125,18 @@ public class NFTService {
             log.info("NFT生成成功 {}", nft);
             return nft;
         } else if ("211".equals(resp.getCode())) {
-
+            log.error("EVM合约执行限流");
         } else if ("10201".equals(resp.getCode())) {
             // 异步交易未成功需要根据状态码判断交易状态
             ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
             String out = new String(matchAndReplaceNonEnglishChar(txReceipt.getOutput()));
             // 异步交易未成功需要根据状态码判断交易状态
-            log.error("EVM合约执行未成功: {}, {}", resp.getCode(), out);
+            log.error("EVM合约执行未成功: code: {}, out: {}, tokenId: {}", resp.getCode(), out, tokenId);
             if (out.endsWith("ERC721: token already minted")) {
                 return new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
             }
         }
-        throw new BusinessException("创建nft失败");
+        throw new BusinessException("执行EVM合约mint失败");
     }
 
     public static byte[] matchAndReplaceNonEnglishChar(byte[] data) {

+ 25 - 13
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -34,6 +34,7 @@ import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.EncoderException;
 import org.apache.commons.codec.net.URLCodec;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.context.event.EventListener;
@@ -289,10 +290,11 @@ public class OrderService {
             Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("无记录"));
             User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("拥有者用户不存在"));
             if (collection.getServiceCharge() + collection.getRoyalties() > 0) {
-                restAmount = divMoney(totalAmount, restAmount, divMembers, "0",
-                        collection.getServiceCharge() + collection.getRoyalties(), true);
+                // 扣除手续费、服务费、GAS费
+                restAmount = divMoney(totalAmount, restAmount, divMembers, owner.getMemberId(),
+                        100 - (collection.getServiceCharge() + collection.getRoyalties()), false);
             }
-            restAmount = divMoney(restAmount, divMembers, owner.getMemberId(), restAmount, false);
+            restAmount = divMoney(restAmount, divMembers, "0", restAmount, true);
         } else {
             if (invitor != null && invitor.getShareRatio() != null
                     && invitor.getShareRatio().compareTo(BigDecimal.ZERO) > 0) {
@@ -398,7 +400,14 @@ public class OrderService {
                 if (collection.getSource() == CollectionSource.TRANSFER) {
                     Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
                     assetService.transfer(asset, order.getPrice(), user, "转让", order.getId());
-                    collectionRepo.delete(collection);
+                    List<Long> collectionIds = collectionRepo.findAllByAssetId(collection.getAssetId());
+                    log.info("删除collection {}", collectionIds);
+                    if (CollectionUtils.isNotEmpty(collectionIds)) {
+                        collectionRepo.deleteAllByIdIn(collectionIds);
+                    } else {
+                        collectionRepo.delete(collection);
+                    }
+
                 } else {
                     orderRepo.save(order);
                     assetService.createAsset(collection, user, order.getId(), order.getPrice(), "出售",
@@ -414,20 +423,23 @@ public class OrderService {
     @EventListener
     public void onCreateAsset(CreateAssetEvent event) {
         Asset asset = event.getAsset();
-        Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException("订单不存在"));
-        if (event.isSuccess()) {
-            order.setTxHash(asset.getTxHash());
-            order.setGasUsed(asset.getGasUsed());
-            order.setBlockNumber(asset.getBlockNumber());
-            order.setStatus(OrderStatus.FINISH);
-            orderRepo.save(order);
-        } else {
-            log.error("创建asset失败");
+        if (asset.getOrderId() != null) {
+            Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException("订单不存在"));
+            if (event.isSuccess()) {
+                order.setTxHash(asset.getTxHash());
+                order.setGasUsed(asset.getGasUsed());
+                order.setBlockNumber(asset.getBlockNumber());
+                order.setStatus(OrderStatus.FINISH);
+                orderRepo.save(order);
+            } else {
+                log.error("创建asset失败");
+            }
         }
     }
 
     @EventListener
     public void onTransferAsset(TransferAssetEvent event) {
+        log.info("藏品转让成功 assetId={}", event.getAsset().getId());
         Asset asset = event.getAsset();
         Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException("订单不存在"));
         if (event.isSuccess()) {

+ 16 - 1
src/main/java/com/izouma/nineth/service/UserService.java

@@ -12,6 +12,7 @@ import com.izouma.nineth.domain.User;
 import com.izouma.nineth.dto.*;
 import com.izouma.nineth.enums.AuthStatus;
 import com.izouma.nineth.enums.AuthorityName;
+import com.izouma.nineth.event.AccountCreatedEvent;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.*;
 import com.izouma.nineth.security.Authority;
@@ -33,6 +34,7 @@ import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.context.event.EventListener;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.jpa.domain.Specification;
@@ -65,6 +67,7 @@ public class UserService {
     private UserBankCardRepo  userBankCardRepo;
     private CacheService      cacheService;
     private InviteRepo        inviteRepo;
+    private NFTService        nftService;
 
     @CacheEvict(value = "user", key = "#user.username")
     public User update(User user) {
@@ -117,7 +120,19 @@ public class UserService {
         if (StringUtils.isNotBlank(userRegister.getPassword())) {
             user.setPassword(new BCryptPasswordEncoder().encode(userRegister.getPassword()));
         }
-        return userRepo.save(user);
+        user = userRepo.saveAndFlush(user);
+        nftService.createAccount(user.getId());
+        return user;
+    }
+
+    @EventListener
+    public void accountCreated(AccountCreatedEvent event) {
+        userRepo.findById(event.getUserId()).ifPresent(user -> {
+            user.setNftAccount(event.getAccount().getAccountId());
+            user.setKmsId(event.getAccount().getAccountKmsId());
+            user.setPublicKey(event.getAccount().getPublicKey());
+            userRepo.save(user);
+        });
     }
 
     public User phoneRegister(String phone, String code, String password) {

+ 1 - 1
src/main/java/com/izouma/nineth/utils/JpaUtils.java

@@ -188,7 +188,7 @@ public class JpaUtils {
             }
             clazz = clazz.getSuperclass();
         }
-        log.error("no such field [{}] in class [{}]", property, className);
+        log.debug("no such field [{}] in class [{}]", property, className);
         return null;
     }
 }

+ 23 - 0
src/main/java/com/izouma/nineth/web/RocketMqController.java

@@ -0,0 +1,23 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.event.MyMqEvent;
+import lombok.AllArgsConstructor;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/testmq")
+@AllArgsConstructor
+public class RocketMqController {
+
+    private RocketMQTemplate rocketMQTemplate;
+
+    @GetMapping("/send")
+    public String send(String data) {
+        rocketMQTemplate.syncSend("test-mq-topic", new MyMqEvent(data));
+        return "ok";
+    }
+
+}

+ 12 - 0
src/main/java/com/izouma/nineth/web/UserController.java

@@ -223,6 +223,18 @@ public class UserController extends BaseController {
         userService.removeBankCard(SecurityUtils.getAuthenticatedUser().getId());
     }
 
+    @PostMapping("/removeBankCardAdmin")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public void removeBankCardAdmin(@RequestParam Long userId) throws BaseAdaPayException {
+        userService.removeBankCard(userId);
+    }
+
+    @PostMapping("/removeAuthAdmin")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public void removeAuthAdmin(@RequestParam Long userId) {
+        userService.removeAuth(userId);
+    }
+
     @PreAuthorize("hasAnyRole('ADMIN')")
     @PostMapping("/batchRegister")
     public Map<String, Object> batchRegister(@RequestParam String phones, @RequestParam String defaultPassword) {

+ 49 - 1
src/main/resources/application.yaml

@@ -135,6 +135,18 @@ general:
   name: 第九空间
   org: 广州麦塔沃司信息技术有限公司
   short-name: 麦塔沃司
+  create-order-group: create-order-group-dev
+  create-order-topic: create-order-topic-dev
+  update-stock-group: update-stock-group-dev
+  update-stock-topic: update-stock-topic-dev
+  update-sale-group: update-sale-group-dev
+  update-sale-topic: update-sale-topic-dev
+  order-notify-group: order-notify-group-dev
+  order-notify-topic: order-notify-topic-dev
+  mint-group: mint-group-dev
+  mint-topic: mint-topic-dev
+  update-activity-stock-group: update-activity-stock-group-dev
+  update-activity-stock-topic: update-activity-stock-topic-dev
 mychain:
   rest:
     bizid: a00e36c5
@@ -192,11 +204,30 @@ adapay:
   app-public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwN6xgd6Ad8v2hIIsQVnbt8a3JituR8o4Tc3B5WlcFR55bz4OMqrG/356Ur3cPbc2Fe8ArNd/0gZbC9q56Eb16JTkVNA/fye4SXznWxdyBPR7+guuJZHc/VW2fKH2lfZ2P3Tt0QkKZZoawYOGSMdIvO+WqK44updyax0ikK6JlNQIDAQAB
   wx-app-id:
   notify-url: https://nfttest.9space.vip/notify/adapay
+rocketmq:
+  name-server: 112.74.34.84:9876
+  producer:
+    group: my-producer-dev
+    send-message-timeout: 30000
 ---
 
 spring:
   profiles: test
-
+general:
+  host: https://nfttest.9space.vip
+  create-order-group: create-order-group-test
+  create-order-topic: create-order-topic-test
+  update-stock-group: update-stock-group-test
+  update-stock-topic: update-stock-topic-test
+  update-sale-group: update-sale-group-test
+  update-sale-topic: update-sale-topic-test
+  order-notify-group: order-notify-group-test
+  order-notify-topic: order-notify-topic-test
+  mint-group: mint-group-test
+  mint-topic: mint-topic-test
+  update-activity-stock-group: update-activity-stock-group-test
+  update-activity-stock-topic: update-activity-stock-topic-test
+  notify-server: true
 ---
 
 spring:
@@ -208,6 +239,19 @@ spring:
     database: 1
 general:
   host: https://nft.9space.vip
+  create-order-group: create-order-group
+  create-order-topic: create-order-topic
+  update-stock-group: update-stock-group
+  update-stock-topic: update-stock-topic
+  update-sale-group: update-sale-group
+  update-sale-topic: update-sale-topic
+  order-notify-group: order-notify-group
+  order-notify-topic: order-notify-topic
+  mint-group: mint-group
+  mint-topic: mint-topic
+  update-activity-stock-group: update-activity-stock-group
+  update-activity-stock-topic: update-activity-stock-topic
+  notify-server: true
 wx:
   pay:
     notify-url: https://nft.9space.vip/notify/order/weixin
@@ -218,3 +262,7 @@ alipay:
   return-url: https://nft.9space.vip/9th/home
 adapay:
   notify-url: https://nft.9space.vip/notify/adapay
+rocketmq:
+  name-server: 172.31.167.5:9876
+  producer:
+    group: my-producer

+ 16 - 130
src/main/resources/logback-spring.xml

@@ -11,10 +11,6 @@
         <root level="INFO">
             <appender-ref ref="CONSOLE"/>
         </root>
-        <logger name="org.hibernate.SQL" level="DEBUG"/>
-        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
-        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>
-        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>
         <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
         <!--swagger 类型转换异常日志去除-->
         <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
@@ -30,24 +26,12 @@
             </encoder>
         </appender>
 
-        <!-- ding open sdk log -->
-        <appender name="DING_OPEN_CLIENT_SDK_LOGGER_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
-            <param name="append" value="true"/>
-            <param name="encoding" value="UTF-8"/>
-            <param name="threshold" value="INFO"/>
-            <encoder>
-                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
-            </encoder>
-        </appender>
-        <logger name="HTTP_INVOKE_LOGGER" additivity="false">
-            <level value="WARN"/>
-            <appender-ref ref="HTTP_INVOKE_LOGGER_APPENDER"/>
-        </logger>
-        <logger name="DING_OPEN_CLIENT_SDK_LOGGER" additivity="false">
-            <level value="WARN"/>
-            <appender-ref ref="DING_OPEN_CLIENT_SDK_LOGGER_APPENDER"/>
-        </logger>
-        <!-- end of dingding log -->
+        <!--        <logger name="org.hibernate.SQL" level="DEBUG"/>-->
+        <!--        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>-->
+        <!--        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>-->
+        <!--        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>-->
     </springProfile>
 
     <springProfile name="test">
@@ -55,22 +39,22 @@
             <encoder>
                 <pattern>${FILE_LOG_PATTERN}</pattern>
             </encoder>
-            <file>/var/www/9th_test/app.log</file>
+            <file>app.log</file>
             <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                 <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
-                <maxFileSize>10MB</maxFileSize>
+                <maxFileSize>100MB</maxFileSize>
                 <maxHistory>60</maxHistory>
             </rollingPolicy>
         </appender>
         <root level="INFO">
             <appender-ref ref="FILE"/>
         </root>
-        <logger name="org.hibernate.SQL" level="DEBUG"/>
-        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
-        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>
-        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>
-        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
-        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
+        <!--        <logger name="org.hibernate.SQL" level="DEBUG"/>-->
+        <!--        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>-->
+        <!--        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>-->
+        <!--        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>-->
     </springProfile>
 
     <springProfile name="prod">
@@ -78,114 +62,16 @@
             <encoder>
                 <pattern>${FILE_LOG_PATTERN}</pattern>
             </encoder>
-            <file>/var/www/9th/app.log</file>
-            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
-                <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
-                <maxFileSize>10MB</maxFileSize>
-                <maxHistory>60</maxHistory>
-            </rollingPolicy>
-        </appender>
-        <root level="INFO">
-            <appender-ref ref="FILE"/>
-        </root>
-        <logger name="org.hibernate.SQL" level="DEBUG"/>
-        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
-        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>
-        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>
-        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
-        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
-    </springProfile>
-
-    <springProfile name="dev_raex">
-        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
-            <encoder>
-                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
-            </encoder>
-        </appender>
-        <root level="INFO">
-            <appender-ref ref="CONSOLE"/>
-        </root>
-        <logger name="org.hibernate.SQL" level="DEBUG"/>
-        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
-        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>
-        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>
-        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
-        <!--swagger 类型转换异常日志去除-->
-        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
-        <logger name="org.freemarker" level="DEBUG"/>
-
-        <!-- dingding log -->
-        <appender name="HTTP_INVOKE_LOGGER_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
-            <param name="append" value="true"/>
-            <param name="encoding" value="UTF-8"/>
-            <param name="threshold" value="INFO"/>
-            <encoder>
-                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
-            </encoder>
-        </appender>
-
-        <!-- ding open sdk log -->
-        <appender name="DING_OPEN_CLIENT_SDK_LOGGER_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
-            <param name="append" value="true"/>
-            <param name="encoding" value="UTF-8"/>
-            <param name="threshold" value="INFO"/>
-            <encoder>
-                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
-            </encoder>
-        </appender>
-        <logger name="HTTP_INVOKE_LOGGER" additivity="false">
-            <level value="WARN"/>
-            <appender-ref ref="HTTP_INVOKE_LOGGER_APPENDER"/>
-        </logger>
-        <logger name="DING_OPEN_CLIENT_SDK_LOGGER" additivity="false">
-            <level value="WARN"/>
-            <appender-ref ref="DING_OPEN_CLIENT_SDK_LOGGER_APPENDER"/>
-        </logger>
-        <!-- end of dingding log -->
-    </springProfile>
-
-    <springProfile name="test_raex">
-        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-            <encoder>
-                <pattern>${FILE_LOG_PATTERN}</pattern>
-            </encoder>
-            <file>/var/www/9th_test/app.log</file>
-            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
-                <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
-                <maxFileSize>10MB</maxFileSize>
-                <maxHistory>60</maxHistory>
-            </rollingPolicy>
-        </appender>
-        <root level="INFO">
-            <appender-ref ref="FILE"/>
-        </root>
-        <logger name="org.hibernate.SQL" level="DEBUG"/>
-        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
-        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>
-        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>
-        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
-        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
-    </springProfile>
-
-    <springProfile name="prod_raex">
-        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-            <encoder>
-                <pattern>${FILE_LOG_PATTERN}</pattern>
-            </encoder>
-            <file>/var/www/9th/app.log</file>
+            <file>app.log</file>
             <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                 <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
-                <maxFileSize>10MB</maxFileSize>
+                <maxFileSize>100MB</maxFileSize>
                 <maxHistory>60</maxHistory>
             </rollingPolicy>
         </appender>
         <root level="INFO">
             <appender-ref ref="FILE"/>
         </root>
-        <logger name="org.hibernate.SQL" level="DEBUG"/>
-        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
-        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>
-        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>
         <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
         <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
     </springProfile>

+ 1 - 1
src/main/vue/src/views/OrderList.vue

@@ -182,7 +182,7 @@ export default {
         download() {
             this.downloading = true;
             let params = this.beforeGetData();
-            params.size = 10000;
+            params.size = 100000;
             params.query.projectId = this.$store.state.projectId;
             this.$axios
                 .post('/order/excel', params, {

+ 94 - 3
src/main/vue/src/views/UserList.vue

@@ -44,9 +44,20 @@
             </el-table-column>
             <el-table-column label="手机" prop="phone"></el-table-column>
             <el-table-column label="注册时间" prop="createdAt" width="150"></el-table-column>
-            <el-table-column label="操作" align="center" fixed="right">
+            <el-table-column label="操作" align="center" fixed="right" width="200">
                 <template slot-scope="{ row }">
                     <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="removeCard(row)" size="mini" type="text" :disabled="!row.settleAccountId">
+                        解绑卡
+                    </el-button>
+                    <el-button
+                        @click="removeAuth(row)"
+                        size="mini"
+                        type="text"
+                        :disabled="row.authStatus !== 'SUCCESS'"
+                    >
+                        取消认证
+                    </el-button>
                 </template>
             </el-table-column>
         </el-table>
@@ -89,7 +100,7 @@ export default {
     },
     methods: {
         beforeGetData() {
-            return { search: this.search, query: {createdAt: this.createdAt} };
+            return { search: this.search, query: { createdAt: this.createdAt } };
         },
         afterGetData(res) {
             // let i = this.tableData.findIndex(i => i.username === 'root');
@@ -167,7 +178,7 @@ export default {
                     .then(res => {
                         let el = document.createElement('div');
                         new ClipboardJS(el, {
-                            text: function (trigger) {
+                            text: function(trigger) {
                                 return res;
                             }
                         });
@@ -179,6 +190,86 @@ export default {
                         this.$message.error(e.error);
                     });
             }
+        },
+        removeCard(row) {
+            this.$msgbox({
+                title: '确认解绑吗?',
+                message: '该操作无法撤回,请谨慎操作',
+                showCancelButton: true,
+                confirmButtonText: '确认',
+                cancelButtonText: '取消',
+                type: 'warning',
+                beforeClose: (action, instance, done) => {
+                    if (action === 'confirm') {
+                        instance.confirmButtonLoading = true;
+                        instance.showCancelButton = false;
+                        instance.closeOnClickModal = false;
+                        instance.showClose = false;
+                        this.$http
+                            .post('/user/removeBankCardAdmin', { userId: row.id })
+                            .then(res => {
+                                done();
+                                this.getData();
+                                this.$message.success('解绑成功');
+                                instance.confirmButtonLoading = false;
+                                instance.showCancelButton = true;
+                                instance.closeOnClickModal = true;
+                                instance.showClose = true;
+                            })
+                            .catch(e => {
+                                done();
+                                this.getData();
+                                this.$message.error(e.error || '解绑失败');
+                                instance.confirmButtonLoading = false;
+                                instance.showCancelButton = true;
+                                instance.closeOnClickModal = true;
+                                instance.showClose = true;
+                            });
+                    } else {
+                        done();
+                    }
+                }
+            }).then(_ => {});
+        },
+        removeAuth(row) {
+            this.$msgbox({
+                title: '确认取消认证吗?',
+                message: '该操作无法撤回,请谨慎操作',
+                showCancelButton: true,
+                confirmButtonText: '确认',
+                cancelButtonText: '取消',
+                type: 'warning',
+                beforeClose: (action, instance, done) => {
+                    if (action === 'confirm') {
+                        instance.confirmButtonLoading = true;
+                        instance.showCancelButton = false;
+                        instance.closeOnClickModal = false;
+                        instance.showClose = false;
+                        this.$http
+                            .post('/user/removeAuthAdmin', { userId: row.id })
+                            .then(res => {
+                                done();
+                                this.getData();
+                                this.$message.success('取消认证成功');
+                                instance.confirmButtonLoading = false;
+                                instance.showCancelButton = true;
+                                instance.closeOnClickModal = true;
+                                instance.showClose = true;
+                            })
+                            .catch(e => {
+                                done();
+                                this.getData();
+                                this.$message.error(e.error || '取消认证失败');
+                                instance.confirmButtonLoading = false;
+                                instance.showCancelButton = true;
+                                instance.closeOnClickModal = true;
+                                instance.showClose = true;
+                            });
+                    } else {
+                        done();
+                    }
+                }
+            }).then(_ => {});
         }
     }
 };

+ 57 - 147
src/test/java/com/izouma/nineth/service/AdapayServiceTest.java

@@ -1,183 +1,93 @@
 package com.izouma.nineth.service;
 
 import com.alibaba.excel.EasyExcel;
-import com.alibaba.excel.annotation.ExcelProperty;
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.serializer.SerializerFeature;
-import com.huifu.adapay.Adapay;
+import com.alibaba.fastjson.JSONObject;
 import com.huifu.adapay.core.exception.BaseAdaPayException;
-import com.huifu.adapay.model.AdapayCommon;
-import com.huifu.adapay.model.MerConfig;
-import com.huifu.adapay.model.Payment;
 import com.huifu.adapay.model.Refund;
+import com.izouma.nineth.ApplicationTests;
+import com.izouma.nineth.domain.AdaTrade;
+import com.izouma.nineth.repo.GiftOrderRepo;
+import com.izouma.nineth.repo.OrderRepo;
 import com.izouma.nineth.utils.SnowflakeIdWorker;
-import com.izouma.nineth.utils.excel.BigIntegerConverter;
-import com.izouma.nineth.utils.excel.LocalDateConverter;
-import com.izouma.nineth.utils.excel.LocalDateTimeConverter;
-import lombok.Data;
-import org.apache.commons.lang3.StringUtils;
 import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
 
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.stream.Collectors;
 
-public class AdapayServiceTest {
-    private final String appId = "app_f8760acc-f4d8-46f6-8f70-d80e36517075";
+public class AdapayServiceTest extends ApplicationTests {
+    @Autowired
+    private OrderRepo         orderRepo;
+    @Autowired
+    private GiftOrderRepo     giftOrderRepo;
+    @Autowired
+    private SnowflakeIdWorker snowflakeIdWorker;
 
-    public AdapayServiceTest() {
-        Adapay.debug = true;
-        Adapay.prodMode = true;
-
-        MerConfig merConfig = new MerConfig();
-        merConfig.setApiKey("api_live_8818cac1-894b-40bc-ac77-803e02e9c260");
-        merConfig.setApiMockKey("api_test_0b1b0eb9-30e1-4acd-8e03-10b529de1856");
-        merConfig.setRSAPrivateKey("MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCnIaIrFP598qcf/s3FqXxMTjrVAevnf1w5CAMdJqcRFv0WvdLEmNRxxqAYSLl/iQ8AyU4yqVwkwYqvn/aENBg0iQ5h5qboxb8T6xmrZLctk97TiIuCUwbd7Q5NBHJXzHlaKc1p1KBvTCJDMw8+t0Jh3sBkBvu7l0KPliEQHtPLBDkKvy3NBQD6BMy47kPersjSUH53HBvNpcjcPZGpUX/TmL3SNkS98RkoebFxuorK5UdazYN5xvarjfcYziShIjU3WIZJb52DGUxYWTk6vJKZrJhw7AhWIEQpH7qJxvml/B8RMQ4guHdtp3ojxwkHDtZdPU9rY8i6EHwba4qOYgobAgMBAAECggEAT36L5/oAYl+8ZleIAHBxEspS6WYUkvPdJbNN59uus04/60U2rxQSWFulYmeU87h5TmJxs18i2MjF8msfkhpFORfHo4FV+nm0PQEiIIezKRagcfUMhlx/c6eBmdh3mpNDVUN01NWxyb5ovZXXtnjsNikBUZKQwdVcb3d1GnnPO0xtt6/0xwiduCkA2ihS1tgnsYYDhMHgukIdZ3eczn3stRPQ+QyCt1JWS6DDd1nS3S2RyPZw8P9Z1zzJFVKH8z3bGqk3/98Lw7Hw+rKFnKhIA6/H9ZVORKw5OuGC3Ozy6cVbmUn8tuw3sC0NdR7w56dedB+fjJB8od0nahX1Cc6eQQKBgQDckcenslWqjs2PbncwW1wqlw7FdJX9rzJAg7kp9ItpHCoNi/kSgXeLphHXWJmyj7a1BkWynmTGxO48X3dPXUrDPFKJc42fSbxMgAQdtc/A2z+v7Ga/oUpH8jajKfKmcgeRX026R7gd9W0yi0EW+C0WdFhrzNKKY4shvnYy9lc+QwKBgQDB+mHSllqLqYru0bLrtKOKJXaR3N3INxDBZKnRqba4tUKN35IVIexiEMkHmC51jtjoRyA5Y+fc/8P11i9FbuShtRVGHWeyDibKlwff5zrETveSLTpSULBKZ6MsFSm0Fo1krSUC1QTUGG5VX/wwWm9AB2UKJqG5cMDd3i3RiPeDSQKBgBs1ED+rS83iF5Eduy4H1vKZ94R7wRSty7ERjoGSXK/2fWl2Xp7dwXVEYucBUtQnzg2+XFKQHzY1jH19+SWdCF/UzQmPa2S+n6+ACwHvL1VGtjBpJLN2nccKJZsyzW+imTRhYSEdP6TSZUnay4idzFH8v/tsJHxVkw/ygnn+0PwpAn8uOHsWsrzgioWQYmc/wss1H7ghCX/PNU/IxTOxwb7IRGiXZa5pWqv4sgc0yA5J9L+6mTgUdLnK7ybCbUbWRJY18fAfxOHwi26y10oJEA/wtuBG9H/xHUjkcc1vs5s8TiNi2d73zcpYv3mK3lQ5MVNQ7nIk+Q+QIE3UkBxa0UgpAoGBAMDwg0ebzBEZsV2cr/Er2b25LsXteDJ+V67plBNrv+A1/omA9a52sWek4bY0D+Uu6zPTDaLj9BhHC2wJmThYl0eLRKyDKYQslBR3h253Gsn3If6RH9/tSyDsQ88iAEI1f6QH27bGHL9VDrsLGEFg5E7ZEzFQuJPqoUvBOoURNwa6");
-        merConfig.setRSAPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApyGiKxT+ffKnH/7Nxal8TE461QHr539cOQgDHSanERb9Fr3SxJjUccagGEi5f4kPAMlOMqlcJMGKr5/2hDQYNIkOYeam6MW/E+sZq2S3LZPe04iLglMG3e0OTQRyV8x5WinNadSgb0wiQzMPPrdCYd7AZAb7u5dCj5YhEB7TywQ5Cr8tzQUA+gTMuO5D3q7I0lB+dxwbzaXI3D2RqVF/05i90jZEvfEZKHmxcbqKyuVHWs2Decb2q433GM4koSI1N1iGSW+dgxlMWFk5OrySmayYcOwIViBEKR+6icb5pfwfETEOILh3bad6I8cJBw7WXT1Pa2PIuhB8G2uKjmIKGwIDAQAB");
-        Adapay.publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwN6xgd6Ad8v2hIIsQVnbt8a3JituR8o4Tc3B5WlcFR55bz4OMqrG/356Ur3cPbc2Fe8ArNd/0gZbC9q56Eb16JTkVNA/fye4SXznWxdyBPR7+guuJZHc/VW2fKH2lfZ2P3Tt0QkKZZoawYOGSMdIvO+WqK44updyax0ikK6JlNQIDAQAB";
-        try {
-            Adapay.initWithMerConfig(merConfig);
-        } catch (Exception e) {
-        }
-    }
-
-    @Test
-    public void testPay() throws BaseAdaPayException {
-        Map<String, Object> paymentParams = new HashMap<String, Object>(10);
-        List<Map<String, Object>> divMembers = new ArrayList<>();
-        divMembers.add(new HashMap<>() {{
-            put("member_id", "0");
-            put("amount", BigDecimal.valueOf(0.05));
-            put("fee_flag", "Y");
-        }});
-        divMembers.add(new HashMap<>() {{
-            put("member_id", "1110");
-            put("amount", BigDecimal.valueOf(0.05));
-        }});
-
-        paymentParams.put("app_id", appId);
-        paymentParams.put("order_no", "jsdk_payment" + System.currentTimeMillis());
-        paymentParams.put("pay_channel", "wx_pub");
-        paymentParams.put("pay_amt", "0.10");
-        paymentParams.put("goods_title", "your goods title");
-        paymentParams.put("goods_desc", "your goods desc");
-        paymentParams.put("notify_url", "http://9th.frp.izouma.com/notify/adapay/order/1");
-        paymentParams.put("description", "orderId=1");
-
-        Map<String, Object> expend = new HashMap<>();
-        expend.put("open_id", "oWJG55wLnwdVzXoKka1-DzQKOd_Y");
-        paymentParams.put("expend", expend);
-
-        Map<String, Object> payment = Payment.create(paymentParams);
-        System.out.println("payment result=" + JSON.toJSONString(payment, SerializerFeature.PrettyFormat));
-    }
+    private final String appId = "app_0e8d3acb-3d95-4ebb-8445-e470c378a787";
 
     @Test
-    public void testWxPay() throws BaseAdaPayException {
-        Map<String, Object> params = new HashMap<String, Object>();
-        params.put("order_no", "host_wx_lite_params_sdk_" + System.currentTimeMillis());
-        params.put("adapay_func_code", "wxpay.createOrder");
-        params.put("pay_amt", "0.02");
-        params.put("app_id", appId);
-        params.put("currency", "cny");
-        params.put("goods_title", "Your goods_title");
-        params.put("goods_desc", "Your goods_desc");
-        params.put("description", "payment Discription");
-        params.put("callback_url", "/9th/orders");
-        Map<String, Object> response = AdapayCommon.requestAdapayUits(params);
-        System.out.println("payment result=" + JSON.toJSONString(response, SerializerFeature.PrettyFormat));
-    }
-
-    @Data
-    public static class RefundOrder {
-        @ExcelProperty("交易时间")
-        private LocalDateTime time;
+    public void queryRefund() throws ExecutionException, InterruptedException {
 
-        @ExcelProperty("订单号")
-        private String id;
+        ForkJoinPool customThreadPool = new ForkJoinPool(100);
+        customThreadPool.submit(() -> {
 
-        @ExcelProperty("支付流水号")
-        private String serial;
+            List<AdaTrade> list = EasyExcel.read("/Users/drew/Downloads/merTransDetail_0284905900625472_20220212_20220212_1644980532.xlsx")
+                    .head(AdaTrade.class).sheet().doReadSync();
+            list = list.parallelStream().filter(adaTrade -> {
+                return adaTrade.get交易金额().equals("19.80")
+                        && orderRepo.findByTransactionId(adaTrade.get支付流水号()) == null
+                        && giftOrderRepo.findByTransactionId(adaTrade.get支付流水号()) == null;
+            }).collect(Collectors.toList());
 
-        @ExcelProperty("第三方订单号")
-        private String thirdId;
+            EasyExcel.write("/Users/drew/Downloads/1.xlsx", AdaTrade.class).sheet("模板").doWrite(list);
+            System.out.println(list.size());
 
-        @ExcelProperty("支付宝/微信订单号")
-        private String txId;
 
-        @ExcelProperty("交易金额")
-        private String amount;
+        }).get();
 
-        private String refundId;
     }
 
     @Test
-    public void refund() throws BaseAdaPayException {
-        List<RefundOrder> orders = EasyExcel.read("/Users/drew/Downloads/merTransDetail_0284905900625472_20211201_20211215_1639557451.xlsx")
-                .head(RefundOrder.class)
-                .registerConverter(new LocalDateConverter())
-                .registerConverter(new LocalDateTimeConverter())
-                .registerConverter(new BigIntegerConverter())
-                .sheet().doReadSync();
-        System.out.println(orders.size());
-        for (RefundOrder order : orders) {
-            String refundId = new SnowflakeIdWorker(0, 0).nextId() + "";
+    public void refund() {
+        List<AdaTrade> list = EasyExcel.read("/Users/drew/Downloads/1.xlsx")
+                .head(AdaTrade.class).sheet().doReadSync();
+        list.parallelStream().forEach(adaTrade -> {
             Map<String, Object> refundParams = new HashMap<>();
-            refundParams.put("refund_amt", order.getAmount());
-            refundParams.put("refund_order_no", new SnowflakeIdWorker(0, 0).nextId() + "");
-            Map<String, Object> response = Refund.create(order.getId(), refundParams);
-            order.setRefundId(refundId);
-        }
-        EasyExcel.write("/Users/drew/Desktop/refund.xlsx", RefundOrder.class).sheet("sheet")
-                .registerConverter(new LocalDateConverter())
-                .registerConverter(new LocalDateTimeConverter())
-                .registerConverter(new BigIntegerConverter())
-                .doWrite(orders);
+            refundParams.put("refund_amt", adaTrade.get交易金额());
+            refundParams.put("refund_order_no", snowflakeIdWorker.nextId() + "");
+            try {
+                Map<String, Object> response = Refund.create(adaTrade.get支付流水号(), refundParams);
+            } catch (BaseAdaPayException e) {
+                e.printStackTrace();
+            }
+        });
     }
 
-
     @Test
-    public void queryrefund() throws BaseAdaPayException {
-        List<RefundOrder> orders = EasyExcel.read("/Users/drew/Desktop/refund.xlsx")
-                .head(RefundOrder.class)
-                .registerConverter(new LocalDateConverter())
-                .registerConverter(new LocalDateTimeConverter())
-                .registerConverter(new BigIntegerConverter())
-                .sheet().doReadSync();
-        System.out.println(orders.size());
-        List<String> success = new ArrayList<>();
-        List<String> fail = new ArrayList<>();
-        for (RefundOrder order : orders) {
-            Map<String, Object> refundParams = new HashMap<>();
-            refundParams.put("refund_order_no", order.getRefundId());
-            Map<String, Object> refund = Refund.query(refundParams);
-            System.out.println(refund.get("refunds"));
+    public void verifyRefund() throws BaseAdaPayException {
+        List<AdaTrade> list = EasyExcel.read("/Users/drew/Downloads/1.xlsx")
+                .head(AdaTrade.class).sheet().doReadSync();
+        list = list.parallelStream().filter(adaTrade -> {
+            boolean success = false;
             try {
-                if (((JSONArray) refund.get("refunds")).getJSONObject(0).getString("trans_status").equals("S")) {
-                    success.add(order.getId());
-                } else {
-                    fail.add(order.getId());
+                Map<String, Object> refundParams = new HashMap<>(2);
+                refundParams.put("payment_id", adaTrade.get支付流水号());
+                Map<String, Object> refund = Refund.query(refundParams);
+                JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(refund));
+                if ("S".equals(jsonObject.getJSONArray("refunds")
+                        .getJSONObject(jsonObject.getJSONArray("refunds").size() - 1).getString("trans_status"))) {
+                    success = true;
                 }
             } catch (Exception e) {
-                fail.add(order.getId());
             }
-        }
-        System.out.println("success:" + success.size());
-        System.out.println("fail:" + fail.size());
-        System.out.println(StringUtils.join(fail, ","));
+            return !success;
+        }).collect(Collectors.toList());
+        EasyExcel.write("/Users/drew/Downloads/2.xlsx", AdaTrade.class).sheet("模板").doWrite(list);
     }
 
-    @Test
-    public void queryList() throws BaseAdaPayException {
-        Map<String, Object> paymentParams = new HashMap<>();
-        paymentParams.put("app_id", appId);
-        paymentParams.put("page_index", "1");
-        paymentParams.put("page_size", "10");
-        paymentParams.put("order_no", "928303191131422720");
-        Map<String, Object> paymentList = Payment.queryList(paymentParams);
-    }
 }

+ 1 - 1
src/test/java/com/izouma/nineth/service/AdapayTest.java

@@ -210,7 +210,7 @@ public class AdapayTest {
     @Test
     public void singleRefundVerify() throws BaseAdaPayException {
         Map<String, Object> queryParams = new HashMap<>();
-        queryParams.put("payment_id", "002112022021218280610338519802865594368");
+        queryParams.put("payment_id", "002112022022011030310341306906502942720");
         Map<String, Object> refund = Refund.query(queryParams);
         System.out.println(JSON.toJSONString(refund, SerializerFeature.PrettyFormat));
     }