xiongzhu 4 жил өмнө
parent
commit
aa5b797def

+ 0 - 6
pom.xml

@@ -303,12 +303,6 @@
             <version>0.10.2.11</version>
         </dependency>
 
-        <dependency>
-            <groupId>com.alipay.mychainx</groupId>
-            <artifactId>mychainx-sdk</artifactId>
-            <version>0.10.2.9</version>
-        </dependency>
-
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>

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

@@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
@@ -12,6 +13,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
 @EnableSwagger2
 @EnableCaching
 @EnableScheduling
+@EnableAsync
 public class Application {
 
     public static void main(String[] args) {

+ 6 - 0
src/main/java/com/izouma/nineth/config/Constants.java

@@ -14,4 +14,10 @@ public interface Constants {
     String SMS_SIGN_NAME = "走马信息";
 
     String SMS_TEMPLATE_CODE_GENERIC = "SMS_175485688";
+
+    String bizId = "a00e36c5";
+
+    String kmsKey = "ydtg$@WZ9NH&EB2e";
+
+    String CONTRACT_NAME = "9th";
 }

+ 9 - 0
src/main/java/com/izouma/nineth/domain/User.java

@@ -85,4 +85,13 @@ public class User extends BaseEntity implements Serializable {
     @Enumerated(EnumType.STRING)
     private AuthStatus authStatus;
 
+    @JsonIgnore
+    private String nftAccount;
+
+    @JsonIgnore
+    private String kmsId;
+
+    @JsonIgnore
+    private String publicKey;
+
 }

+ 21 - 0
src/main/java/com/izouma/nineth/dto/NFT.java

@@ -0,0 +1,21 @@
+package com.izouma.nineth.dto;
+
+import lombok.*;
+
+import java.math.BigInteger;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@ToString
+public class NFT {
+
+    private String txHash;
+
+    private String tokenId;
+
+    private BigInteger blockNumber;
+
+    private BigInteger gasUsed;
+}

+ 16 - 0
src/main/java/com/izouma/nineth/dto/NFTAccount.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.dto;
+
+import lombok.*;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@ToString
+public class NFTAccount {
+    private String accountId;
+
+    private String accountKmsId;
+
+    private String publicKey;
+}

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

@@ -77,6 +77,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/404").permitAll()
                 .antMatchers("/500").permitAll()
                 .antMatchers("/MP_verify*").permitAll()
+                .antMatchers("/payOrder/**").permitAll()
                 .antMatchers("/notify/**").permitAll()
                 // all other requests need to be authenticated
                 .anyRequest().authenticated().and()

+ 23 - 1
src/main/java/com/izouma/nineth/service/AssetService.java

@@ -1,20 +1,42 @@
 package com.izouma.nineth.service;
 
 import com.izouma.nineth.domain.Asset;
+import com.izouma.nineth.domain.Order;
+import com.izouma.nineth.domain.User;
+import com.izouma.nineth.dto.NFTAccount;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.AssetRepo;
+import com.izouma.nineth.repo.UserRepo;
 import com.izouma.nineth.utils.JpaUtils;
 import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.data.domain.Page;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 @Service
 @AllArgsConstructor
 public class AssetService {
 
-    private AssetRepo assetRepo;
+    private AssetRepo  assetRepo;
+    private UserRepo   userRepo;
+    private NFTService nftService;
 
     public Page<Asset> all(PageQuery pageQuery) {
         return assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
     }
+
+    @Async
+    public void createAsset(Order order) {
+        User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
+        if (StringUtils.isEmpty(user.getPublicKey())) {
+            NFTAccount account = nftService.createAccount(user.getUsername());
+            user.setNftAccount(account.getAccountId());
+            user.setKmsId(account.getAccountKmsId());
+            user.setPublicKey(account.getPublicKey());
+            userRepo.save(user);
+        }
+
+    }
 }

+ 101 - 0
src/main/java/com/izouma/nineth/service/NFTService.java

@@ -0,0 +1,101 @@
+package com.izouma.nineth.service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alipay.mychain.sdk.api.utils.Utils;
+import com.alipay.mychain.sdk.domain.transaction.LogEntry;
+import com.antfinancial.mychain.baas.tool.restclient.RestClient;
+import com.antfinancial.mychain.baas.tool.restclient.RestClientProperties;
+import com.antfinancial.mychain.baas.tool.restclient.model.CallRestBizParam;
+import com.antfinancial.mychain.baas.tool.restclient.model.ClientParam;
+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.Constants;
+import com.izouma.nineth.dto.NFT;
+import com.izouma.nineth.dto.NFTAccount;
+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.stereotype.Service;
+
+import java.math.BigInteger;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+public class NFTService {
+    private final RestClient           restClient;
+    private final RestClientProperties restClientProperties;
+
+    public NFTAccount createAccount(String username) {
+        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
+                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
+                .bizid(Constants.bizId)
+                .account(restClientProperties.getAccount())
+                .mykmsKeyId(restClientProperties.getKmsId())
+                .newAccountId(username)
+                .newAccountKmsId(Constants.kmsKey)
+                .method(Method.TENANTCREATEACCUNT)
+                .gas(100000L).build();
+        try {
+            BaseResp baseResp = restClient.chainCallForBiz(callRestBizParam);
+            NFTAccount account = new NFTAccount(username, Constants.kmsKey, baseResp.getData());
+            if (baseResp.isSuccess()) {
+                log.info("创建账户成功 {}", account);
+                return account;
+            } else {
+                throw new RuntimeException(baseResp.getCode());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("创建账户失败", e);
+            throw new BusinessException("创建账户失败");
+        }
+    }
+
+    public NFT createToken(String toAccount) throws Exception {
+        JSONArray jsonArray = new JSONArray();
+        jsonArray.add(Utils.getIdentityByName(toAccount));
+        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
+                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
+                .bizid(restClientProperties.getBizid())
+                .account(restClientProperties.getAccount())
+                .contractName(Constants.CONTRACT_NAME)
+                .methodSignature("mint(identity)")
+                .inputParamListStr(jsonArray.toJSONString())
+                .outTypes("[]")//合约返回值类型
+                .mykmsKeyId(restClientProperties.getKmsId())
+                .method(Method.CALLCONTRACTBIZASYNC)
+                .tenantid(restClientProperties.getTenantid())
+                .gas(500000L)
+                .build();
+        BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
+        if (!resp.isSuccess()) {
+            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
+        }
+        if ("200".equals(resp.getCode())) {
+            log.info("EVM合约执行成功");
+            // 合约调用交易回执内容
+            ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
+            BigInteger gasUsed = txReceipt.getGasUsed();
+            long result = txReceipt.getResult();
+            log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
+            for (LogEntry logEntry : txReceipt.getLogs()) {
+                if (logEntry.getTopics().get(0).equals(HashUtils.Keccak256("Transfer(identity,identity,uint256)"))) {
+                    String tokenId = logEntry.getTopics().get(3);
+                    txReceipt.getBlockNumber();
+                    NFT nft = new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
+                    log.info("NFT生成成功 {}", nft);
+                    return nft;
+                }
+            }
+        } else {
+            // 异步交易未成功需要根据状态码判断交易状态
+            log.error("EVM合约执行未成功: " + resp.getCode());
+        }
+        return null;
+    }
+}

+ 16 - 0
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -10,6 +10,7 @@ import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.Order;
 import com.izouma.nineth.domain.User;
 import com.izouma.nineth.domain.UserAddress;
+import com.izouma.nineth.dto.NFTAccount;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.enums.OrderStatus;
 import com.izouma.nineth.enums.PayMethod;
@@ -21,6 +22,7 @@ import com.izouma.nineth.repo.UserRepo;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.SnowflakeIdWorker;
 import lombok.AllArgsConstructor;
+import org.apache.commons.collections.MapUtils;
 import org.springframework.core.env.Environment;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
@@ -28,7 +30,9 @@ import org.springframework.ui.Model;
 
 import javax.transaction.Transactional;
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.util.Arrays;
+import java.util.Map;
 import java.util.Optional;
 import java.util.UUID;
 
@@ -43,6 +47,7 @@ public class OrderService {
     private Environment      env;
     private AlipayClient     alipayClient;
     private AlipayProperties alipayProperties;
+    private AssetService     assetService;
 
     public Page<Order> all(PageQuery pageQuery) {
         return orderRepo.findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
@@ -140,4 +145,15 @@ public class OrderService {
         return null;
     }
 
+    public void notifyAlipay(Long orderId, Map<String, String> params) {
+        Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() == OrderStatus.NOT_PAID) {
+            order.setStatus(OrderStatus.PROCESSING);
+            order.setPayTime(LocalDateTime.now());
+            order.setTransactionId(MapUtils.getString(params, "trade_no"));
+            order.setPayMethod(PayMethod.ALIPAY);
+            orderRepo.save(order);
+            assetService.createAsset(order);
+        }
+    }
 }

+ 7 - 5
src/main/java/com/izouma/nineth/web/OrderNotifyController.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.internal.util.AlipaySignature;
 import com.izouma.nineth.config.AlipayProperties;
+import com.izouma.nineth.service.OrderService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections.MapUtils;
@@ -27,6 +28,7 @@ import static com.alibaba.fastjson.serializer.SerializerFeature.PrettyFormat;
 public class OrderNotifyController {
 
     private final AlipayProperties alipayProperties;
+    private final OrderService     orderService;
 
     @PostMapping("/order/alipay")
     @ResponseBody
@@ -54,11 +56,11 @@ public class OrderNotifyController {
         AlipaySignature.rsaCheckV1(params, alipayProperties.getAliPublicKey(), "UTF-8", "RSA2");
         if (MapUtils.getString(params, "trade_status").equals("TRADE_SUCCESS")) {
             JSONObject body = JSON.parseObject(params.get("body"));
-            String type = body.getString("type");
-            switch (type) {
-                case "deposit":
-                    break;
-                case "recharge":
+            String action = body.getString("action");
+            switch (action) {
+                case "payOrder":
+                    Long orderId = body.getLong("orderId");
+                    orderService.notifyAlipay(orderId, params);
                     break;
             }
             return "success";

+ 23 - 0
src/test/java/com/izouma/nineth/service/NFTServiceTest.java

@@ -0,0 +1,23 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.ApplicationTests;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static org.junit.Assert.*;
+
+public class NFTServiceTest extends ApplicationTests {
+    @Autowired
+    private NFTService nftService;
+
+    @Test
+    public void createAccount() {
+        nftService.createAccount(RandomStringUtils.randomAlphabetic(8));
+    }
+
+    @Test
+    public void createToken() throws Exception {
+        nftService.createToken("9th_test");
+    }
+}