Browse Source

Merge branch 'dev'

# Conflicts:
#	src/test/java/com/izouma/nineth/service/AssetServiceTest.java
xiongzhu 4 years ago
parent
commit
5625b1b37c

+ 286 - 0
0001-3d.patch

@@ -0,0 +1,286 @@
+From 30c5e5f1f7b70d18d7006266640f6e24b1a000b9 Mon Sep 17 00:00:00 2001
+From: xiongzhu <692949348@qq.com>
+Date: Wed, 29 Dec 2021 18:15:08 +0800
+Subject: [PATCH] 3d
+
+---
+ .../com/izouma/nineth/domain/Collection.java  |  4 ++
+ .../izouma/nineth/service/AssetService.java   |  2 +-
+ .../com/izouma/nineth/utils/FileUtils.java    | 66 +++++++++++++++++++
+ .../nineth/web/FileUploadController.java      | 33 ++++++++--
+ src/main/vue/src/components/FileUpload.vue    |  7 +-
+ src/main/vue/src/views/BlindBoxEdit.vue       |  4 +-
+ src/main/vue/src/views/CollectionEdit.vue     | 13 +++-
+ 7 files changed, 119 insertions(+), 10 deletions(-)
+
+diff --git a/src/main/java/com/izouma/nineth/domain/Collection.java b/src/main/java/com/izouma/nineth/domain/Collection.java
+index f3ee2ed..75c7949 100644
+--- a/src/main/java/com/izouma/nineth/domain/Collection.java
++++ b/src/main/java/com/izouma/nineth/domain/Collection.java
+@@ -37,6 +37,10 @@ public class Collection extends BaseEntity {
+     @Convert(converter = FileObjectListConverter.class)
+     private List<FileObject> pic;
+ 
++    @Column(columnDefinition = "TEXT")
++    @Convert(converter = FileObjectListConverter.class)
++    private FileObject model3d;
++
+     @ApiModelProperty("铸造者")
+     @Searchable
+     private String minter;
+diff --git a/src/main/java/com/izouma/nineth/service/AssetService.java b/src/main/java/com/izouma/nineth/service/AssetService.java
+index 134a056..01e1b01 100644
+--- a/src/main/java/com/izouma/nineth/service/AssetService.java
++++ b/src/main/java/com/izouma/nineth/service/AssetService.java
+@@ -289,7 +289,7 @@ public class AssetService {
+                 .toUserId(toUser.getId())
+                 .toAvatar(toUser.getAvatar())
+                 .operation(reason)
+-                .price("转赠".equals(reason) ? price : null)
++                .price("转赠".equals(reason) ? null : price)
+                 .build());
+ 
+         asset.setPublicShow(false);
+diff --git a/src/main/java/com/izouma/nineth/utils/FileUtils.java b/src/main/java/com/izouma/nineth/utils/FileUtils.java
+index 3545e2c..d4454d9 100644
+--- a/src/main/java/com/izouma/nineth/utils/FileUtils.java
++++ b/src/main/java/com/izouma/nineth/utils/FileUtils.java
+@@ -3,13 +3,18 @@ package com.izouma.nineth.utils;
+ import org.apache.commons.lang3.StringUtils;
+ 
+ import java.io.*;
++import java.nio.charset.Charset;
++import java.nio.charset.StandardCharsets;
+ import java.nio.file.Files;
+ import java.nio.file.Path;
+ import java.nio.file.attribute.PosixFileAttributeView;
+ import java.nio.file.attribute.PosixFileAttributes;
+ import java.nio.file.attribute.PosixFilePermission;
+ import java.nio.file.attribute.PosixFilePermissions;
++import java.util.Optional;
+ import java.util.Set;
++import java.util.zip.ZipEntry;
++import java.util.zip.ZipInputStream;
+ 
+ public class FileUtils {
+ 
+@@ -200,5 +205,66 @@ public class FileUtils {
+ 
+     }
+ 
++    public static void unzip(InputStream in, File destDir) throws IOException {
++        try {
++            unzip(in, destDir, StandardCharsets.UTF_8);
++        } catch (Exception e) {
++            unzip(in, destDir, Charset.forName("GB2312"));
++        }
++    }
++
++    public static void unzip(InputStream in, File destDir, Charset charset) throws IOException {
++        byte[] buffer = new byte[1024];
++        ZipInputStream zis = new ZipInputStream(in);
++        ZipEntry zipEntry = zis.getNextEntry();
++        while (zipEntry != null) {
++            File newFile = newFile(destDir, zipEntry);
++            if (zipEntry.isDirectory()) {
++                if (!newFile.isDirectory() && !newFile.mkdirs()) {
++                    throw new IOException("Failed to create directory " + newFile);
++                }
++            } else {
++                // fix for Windows-created archives
++                File parent = newFile.getParentFile();
++                if (!parent.isDirectory() && !parent.mkdirs()) {
++                    throw new IOException("Failed to create directory " + parent);
++                }
+ 
++                // write file content
++                FileOutputStream fos = new FileOutputStream(newFile);
++                int len;
++                while ((len = zis.read(buffer)) > 0) {
++                    fos.write(buffer, 0, len);
++                }
++                fos.close();
++            }
++            zipEntry = zis.getNextEntry();
++        }
++        zis.closeEntry();
++        zis.close();
++    }
++
++    public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
++        File destFile = new File(destinationDir, zipEntry.getName());
++
++        String destDirPath = destinationDir.getCanonicalPath();
++        String destFilePath = destFile.getCanonicalPath();
++
++        if (!destFilePath.startsWith(destDirPath + File.separator)) {
++            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
++        }
++
++        return destFile;
++    }
++
++    public static File findInDir(File dir, String ext) {
++        if (!(dir.exists() && dir.isDirectory())) return null;
++        for (File file : Optional.ofNullable(dir.listFiles()).orElse(new File[0])) {
++            String name = file.getName().toLowerCase();
++            if (name.endsWith(ext.toLowerCase()) && !file.isHidden()) {
++                return file;
++            }
++        }
++        return null;
++    }
+ }
+diff --git a/src/main/java/com/izouma/nineth/web/FileUploadController.java b/src/main/java/com/izouma/nineth/web/FileUploadController.java
+index 47580f3..114788e 100644
+--- a/src/main/java/com/izouma/nineth/web/FileUploadController.java
++++ b/src/main/java/com/izouma/nineth/web/FileUploadController.java
+@@ -10,10 +10,10 @@ import org.apache.commons.io.FilenameUtils;
+ import org.apache.commons.lang3.ArrayUtils;
+ import org.apache.commons.lang3.RandomStringUtils;
+ import org.apache.commons.lang3.StringUtils;
++import org.apache.poi.util.TempFile;
+ import org.bytedeco.javacv.FFmpegFrameGrabber;
+ import org.bytedeco.javacv.Frame;
+ import org.bytedeco.javacv.Java2DFrameConverter;
+-import org.pngquant.PngQuant;
+ import org.springframework.beans.factory.annotation.Autowired;
+ import org.springframework.web.bind.annotation.PostMapping;
+ import org.springframework.web.bind.annotation.RequestMapping;
+@@ -26,10 +26,7 @@ import java.awt.image.BufferedImage;
+ import java.io.*;
+ import java.net.URLConnection;
+ import java.text.SimpleDateFormat;
+-import java.util.Base64;
+-import java.util.Date;
+-import java.util.Objects;
+-import java.util.Optional;
++import java.util.*;
+ import java.util.regex.Pattern;
+ 
+ 
+@@ -182,4 +179,30 @@ public class FileUploadController {
+ 
+         return new FileObject(file.getOriginalFilename(), url, thumbUrl, file.getContentType());
+     }
++
++    @PostMapping("/3dModel")
++    public FileObject upload3dModel(@RequestParam("file") MultipartFile file) throws IOException {
++        if (!"zip".equalsIgnoreCase(FilenameUtils.getExtension(file.getOriginalFilename()))) {
++            throw new BusinessException("只能上传zip");
++        }
++        File destDir = TempFile.createTempDirectory(RandomStringUtils.randomAlphabetic(20));
++        com.izouma.nineth.utils.FileUtils.unzip(file.getInputStream(), destDir);
++        File fbxFile = com.izouma.nineth.utils.FileUtils.findInDir(destDir, ".fbx");
++        if (fbxFile == null) {
++            throw new BusinessException("找不到fbx文件");
++        }
++        File fbxDir = fbxFile.getParentFile();
++        String basePath = "fbx/"
++                + new SimpleDateFormat("yyyy-MM_dd-HH").format(new Date()) + "/"
++                + RandomStringUtils.randomAlphabetic(16);
++        List<String> urls = new ArrayList<>();
++        for (File listFile : fbxDir.listFiles()) {
++            if (!listFile.isHidden() && !listFile.isDirectory()) {
++                urls.add(storageService.uploadFromInputStream(new FileInputStream(listFile), basePath + "/" + listFile.getName()));
++            }
++        }
++        String fbxUrl = urls.stream().filter(s -> s.toLowerCase().endsWith(".fbx")).findAny()
++                .orElseThrow(new BusinessException("找不到fbx文件"));
++        return new FileObject(fbxFile.getName(), fbxUrl, null, "fbx");
++    }
+ }
+diff --git a/src/main/vue/src/components/FileUpload.vue b/src/main/vue/src/components/FileUpload.vue
+index beed236..e5257e1 100644
+--- a/src/main/vue/src/components/FileUpload.vue
++++ b/src/main/vue/src/components/FileUpload.vue
+@@ -1,13 +1,14 @@
+ <template>
+     <el-upload
+         class="file-upload"
+-        :action="uploadUrl"
++        :action="customUrl || uploadUrl"
+         :on-success="onSuccess"
+         :headers="headers"
+         :file-list="fileList"
+         :limit="filesLimit"
+         :on-exceed="onExceed"
+         :on-preview="onPreview"
++        :accept="accept || '*/*'"
+         ref="upload"
+     >
+         <el-button type="primary" size="mini" slot="trigger"> 点击上传 </el-button>
+@@ -62,7 +63,9 @@ export default {
+         format: {
+             type: String,
+             default: 'string'
+-        }
++        },
++        customUrl: {},
++        accept: {}
+     },
+     data() {
+         return {
+diff --git a/src/main/vue/src/views/BlindBoxEdit.vue b/src/main/vue/src/views/BlindBoxEdit.vue
+index 773a3aa..2459522 100644
+--- a/src/main/vue/src/views/BlindBoxEdit.vue
++++ b/src/main/vue/src/views/BlindBoxEdit.vue
+@@ -189,6 +189,7 @@
+     </div>
+ </template>
+ <script>
++import resolveUrl from 'resolve-url';
+ export default {
+     name: 'BlindBoxEdit',
+     created() {
+@@ -428,7 +429,8 @@ export default {
+                 id: [{ required: true, message: '请选择作品' }],
+                 total: [{ required: true, message: '请输入数量' }]
+             },
+-            cateogories: ['勋章', '收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他']
++            cateogories: ['勋章', '收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他'],
++            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel')
+         };
+     },
+     methods: {
+diff --git a/src/main/vue/src/views/CollectionEdit.vue b/src/main/vue/src/views/CollectionEdit.vue
+index d794f0d..5574fc7 100644
+--- a/src/main/vue/src/views/CollectionEdit.vue
++++ b/src/main/vue/src/views/CollectionEdit.vue
+@@ -29,6 +29,15 @@
+                         ></object-upload>
+                         <div class="tip">支持JPG、PNG、GIF、MP4,推荐长宽比1:1</div>
+                     </el-form-item>
++                    <el-form-item prop="model3d" label="3D模型">
++                        <file-upload
++                            :limit="1"
++                            v-model="formData.model3d"
++                            :customUrl="customUrl"
++                            accept="application/zip"
++                        ></file-upload>
++                        <div class="tip">请将FBX文件与贴图打包成zip压缩包上传</div>
++                    </el-form-item>
+                     <el-form-item prop="minterId" label="铸造者">
+                         <minter-select
+                             v-model="formData.minterId"
+@@ -221,6 +230,7 @@
+     </div>
+ </template>
+ <script>
++import resolveUrl from 'resolve-url';
+ export default {
+     name: 'CollectionEdit',
+     created() {
+@@ -418,7 +428,8 @@ export default {
+             privelegeRules: {
+                 detail: [{ required: true, message: '请填写内容' }],
+                 remark: [{ required: true, message: '请填写说明' }]
+-            }
++            },
++            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel')
+         };
+     },
+     methods: {
+-- 
+2.30.1 (Apple Git-130)
+

+ 4 - 0
src/main/java/com/izouma/nineth/domain/Collection.java

@@ -37,6 +37,10 @@ public class Collection extends BaseEntity {
     @Convert(converter = FileObjectListConverter.class)
     private List<FileObject> pic;
 
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = FileObjectListConverter.class)
+    private FileObject model3d;
+
     @ApiModelProperty("铸造者")
     @Searchable
     private String minter;

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

@@ -46,4 +46,7 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
 
     @Query("select a from Asset a left join TokenHistory t on a.tokenId = t.tokenId where t.id is null")
     List<Asset> findByNoHistory();
+
+
+    List<Asset> findByTokenIdAndCreatedAtBetween(String tokenId, LocalDateTime start, LocalDateTime end);
 }

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

@@ -23,4 +23,6 @@ public interface TokenHistoryRepo extends JpaRepository<TokenHistory, Long>, Jpa
 
     @Query("select t from TokenHistory t where t.fromUserId = t.toUserId")
     List<TokenHistory> findError();
+
+    List<TokenHistory> findByOperationAndPriceNull(String oper);
 }

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

@@ -289,7 +289,7 @@ public class AssetService {
                 .toUserId(toUser.getId())
                 .toAvatar(toUser.getAvatar())
                 .operation(reason)
-                .price("转赠".equals(reason) ? price : null)
+                .price("转赠".equals(reason) ? null : price)
                 .build());
 
         asset.setPublicShow(false);

+ 66 - 0
src/main/java/com/izouma/nineth/utils/FileUtils.java

@@ -3,13 +3,18 @@ package com.izouma.nineth.utils;
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.PosixFileAttributeView;
 import java.nio.file.attribute.PosixFileAttributes;
 import java.nio.file.attribute.PosixFilePermission;
 import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Optional;
 import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
 
 public class FileUtils {
 
@@ -200,5 +205,66 @@ public class FileUtils {
 
     }
 
+    public static void unzip(InputStream in, File destDir) throws IOException {
+        try {
+            unzip(in, destDir, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            unzip(in, destDir, Charset.forName("GB2312"));
+        }
+    }
+
+    public static void unzip(InputStream in, File destDir, Charset charset) throws IOException {
+        byte[] buffer = new byte[1024];
+        ZipInputStream zis = new ZipInputStream(in);
+        ZipEntry zipEntry = zis.getNextEntry();
+        while (zipEntry != null) {
+            File newFile = newFile(destDir, zipEntry);
+            if (zipEntry.isDirectory()) {
+                if (!newFile.isDirectory() && !newFile.mkdirs()) {
+                    throw new IOException("Failed to create directory " + newFile);
+                }
+            } else {
+                // fix for Windows-created archives
+                File parent = newFile.getParentFile();
+                if (!parent.isDirectory() && !parent.mkdirs()) {
+                    throw new IOException("Failed to create directory " + parent);
+                }
 
+                // write file content
+                FileOutputStream fos = new FileOutputStream(newFile);
+                int len;
+                while ((len = zis.read(buffer)) > 0) {
+                    fos.write(buffer, 0, len);
+                }
+                fos.close();
+            }
+            zipEntry = zis.getNextEntry();
+        }
+        zis.closeEntry();
+        zis.close();
+    }
+
+    public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
+        File destFile = new File(destinationDir, zipEntry.getName());
+
+        String destDirPath = destinationDir.getCanonicalPath();
+        String destFilePath = destFile.getCanonicalPath();
+
+        if (!destFilePath.startsWith(destDirPath + File.separator)) {
+            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
+        }
+
+        return destFile;
+    }
+
+    public static File findInDir(File dir, String ext) {
+        if (!(dir.exists() && dir.isDirectory())) return null;
+        for (File file : Optional.ofNullable(dir.listFiles()).orElse(new File[0])) {
+            String name = file.getName().toLowerCase();
+            if (name.endsWith(ext.toLowerCase()) && !file.isHidden()) {
+                return file;
+            }
+        }
+        return null;
+    }
 }

+ 28 - 5
src/main/java/com/izouma/nineth/web/FileUploadController.java

@@ -10,10 +10,10 @@ import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.util.TempFile;
 import org.bytedeco.javacv.FFmpegFrameGrabber;
 import org.bytedeco.javacv.Frame;
 import org.bytedeco.javacv.Java2DFrameConverter;
-import org.pngquant.PngQuant;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -26,10 +26,7 @@ import java.awt.image.BufferedImage;
 import java.io.*;
 import java.net.URLConnection;
 import java.text.SimpleDateFormat;
-import java.util.Base64;
-import java.util.Date;
-import java.util.Objects;
-import java.util.Optional;
+import java.util.*;
 import java.util.regex.Pattern;
 
 
@@ -182,4 +179,30 @@ public class FileUploadController {
 
         return new FileObject(file.getOriginalFilename(), url, thumbUrl, file.getContentType());
     }
+
+    @PostMapping("/3dModel")
+    public FileObject upload3dModel(@RequestParam("file") MultipartFile file) throws IOException {
+        if (!"zip".equalsIgnoreCase(FilenameUtils.getExtension(file.getOriginalFilename()))) {
+            throw new BusinessException("只能上传zip");
+        }
+        File destDir = TempFile.createTempDirectory(RandomStringUtils.randomAlphabetic(20));
+        com.izouma.nineth.utils.FileUtils.unzip(file.getInputStream(), destDir);
+        File fbxFile = com.izouma.nineth.utils.FileUtils.findInDir(destDir, ".fbx");
+        if (fbxFile == null) {
+            throw new BusinessException("找不到fbx文件");
+        }
+        File fbxDir = fbxFile.getParentFile();
+        String basePath = "fbx/"
+                + new SimpleDateFormat("yyyy-MM_dd-HH").format(new Date()) + "/"
+                + RandomStringUtils.randomAlphabetic(16);
+        List<String> urls = new ArrayList<>();
+        for (File listFile : fbxDir.listFiles()) {
+            if (!listFile.isHidden() && !listFile.isDirectory()) {
+                urls.add(storageService.uploadFromInputStream(new FileInputStream(listFile), basePath + "/" + listFile.getName()));
+            }
+        }
+        String fbxUrl = urls.stream().filter(s -> s.toLowerCase().endsWith(".fbx")).findAny()
+                .orElseThrow(new BusinessException("找不到fbx文件"));
+        return new FileObject(fbxFile.getName(), fbxUrl, null, "fbx");
+    }
 }

+ 5 - 2
src/main/vue/src/components/FileUpload.vue

@@ -1,13 +1,14 @@
 <template>
     <el-upload
         class="file-upload"
-        :action="uploadUrl"
+        :action="customUrl || uploadUrl"
         :on-success="onSuccess"
         :headers="headers"
         :file-list="fileList"
         :limit="filesLimit"
         :on-exceed="onExceed"
         :on-preview="onPreview"
+        :accept="accept || '*/*'"
         ref="upload"
     >
         <el-button type="primary" size="mini" slot="trigger"> 点击上传 </el-button>
@@ -62,7 +63,9 @@ export default {
         format: {
             type: String,
             default: 'string'
-        }
+        },
+        customUrl: {},
+        accept: {}
     },
     data() {
         return {

+ 3 - 1
src/main/vue/src/views/BlindBoxEdit.vue

@@ -189,6 +189,7 @@
     </div>
 </template>
 <script>
+import resolveUrl from 'resolve-url';
 export default {
     name: 'BlindBoxEdit',
     created() {
@@ -428,7 +429,8 @@ export default {
                 id: [{ required: true, message: '请选择作品' }],
                 total: [{ required: true, message: '请输入数量' }]
             },
-            cateogories: ['勋章', '收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他']
+            cateogories: ['勋章', '收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他'],
+            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel')
         };
     },
     methods: {

+ 12 - 1
src/main/vue/src/views/CollectionEdit.vue

@@ -29,6 +29,15 @@
                         ></object-upload>
                         <div class="tip">支持JPG、PNG、GIF、MP4,推荐长宽比1:1</div>
                     </el-form-item>
+                    <el-form-item prop="model3d" label="3D模型">
+                        <file-upload
+                            :limit="1"
+                            v-model="formData.model3d"
+                            :customUrl="customUrl"
+                            accept="application/zip"
+                        ></file-upload>
+                        <div class="tip">请将FBX文件与贴图打包成zip压缩包上传</div>
+                    </el-form-item>
                     <el-form-item prop="minterId" label="铸造者">
                         <minter-select
                             v-model="formData.minterId"
@@ -221,6 +230,7 @@
     </div>
 </template>
 <script>
+import resolveUrl from 'resolve-url';
 export default {
     name: 'CollectionEdit',
     created() {
@@ -418,7 +428,8 @@ export default {
             privelegeRules: {
                 detail: [{ required: true, message: '请填写内容' }],
                 remark: [{ required: true, message: '请填写说明' }]
-            }
+            },
+            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel')
         };
     },
     methods: {

+ 17 - 19
src/test/java/com/izouma/nineth/service/AssetServiceTest.java

@@ -3,13 +3,13 @@ package com.izouma.nineth.service;
 import com.izouma.nineth.ApplicationTests;
 import com.izouma.nineth.TokenHistory;
 import com.izouma.nineth.domain.*;
+import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.*;
 import org.apache.commons.lang3.StringUtils;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import java.util.List;
-import java.util.concurrent.Future;
 
 class AssetServiceTest extends ApplicationTests {
     @Autowired
@@ -111,24 +111,22 @@ class AssetServiceTest extends ApplicationTests {
         System.out.println(assets);
     }
 
+
     @Test
-    public void fixMint() {
-        assetRepo.findAll().stream()
-                .filter(a -> StringUtils.isEmpty(a.getTxHash()) && StringUtils.isNotEmpty(a.getTokenId()))
-                .forEach(a -> {
-                    Future<Asset> f = assetMintService.mint(a);
-                    while (!f.isDone()) {
-                        try {
-                            Thread.sleep(300);
-                        } catch (InterruptedException e) {
-                            e.printStackTrace();
-                        }
-                    }
-                    try {
-                        Thread.sleep(300);
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                });
+    public void fixHistoryPrice() {
+        List<TokenHistory> list = tokenHistoryRepo.findByOperationAndPriceNull("转让");
+        System.out.println(list.size());
+        StringBuilder builder = new StringBuilder();
+        list.parallelStream().forEach(tokenHistory -> {
+            List<Asset> assets = assetRepo.findByTokenIdAndCreatedAtBetween(tokenHistory.getTokenId(), tokenHistory.getCreatedAt()
+                    .minusSeconds(10), tokenHistory.getCreatedAt().plusSeconds(1));
+            if (assets.size() != 1) {
+                throw new BusinessException("");
+            }
+            Order order = orderRepo.findById(assets.get(0).getOrderId())
+                    .orElseThrow(new BusinessException(assets.get(0).getId() + "订单没找到"));
+            builder.append(order.getId()).append(",").append(order.getPrice()).append("\n");
+        });
+        System.out.println(builder);
     }
 }