xiongzhu hace 4 años
padre
commit
1502062db3

+ 1 - 1
src/main/contract/9th.sol

@@ -1025,7 +1025,7 @@ contract ERC721PresetMinterPauserAutoId is Context, AccessControl, ERC721Burnabl
      *
      * - the caller must have the `MINTER_ROLE`.
      */
-    function mint(identity to) public virtual {
+    function mint(identity to, uint256 tokenId) public virtual {
         require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint");
 
         // We cannot just use balanceOf to create the new tokenId because tokens

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

@@ -19,7 +19,7 @@ public interface Constants {
 
     String kmsKey = "ydtg$@WZ9NH&EB2e";
 
-    String CONTRACT_NAME = "9th";
+    String CONTRACT_NAME = "nine_space";
 
     String SMS_TOKEN_SECRET = "rjbcsj39s9mg9r";
 }

+ 29 - 0
src/main/java/com/izouma/nineth/domain/Recommend.java

@@ -0,0 +1,29 @@
+package com.izouma.nineth.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class Recommend extends BaseEntity {
+
+    @ApiModelProperty("藏品ID")
+    private Long collectionId;
+
+    @ApiModelProperty("名称")
+    private String name;
+
+    @ApiModelProperty("排序")
+    private int sort;
+
+    @ApiModelProperty("类型")
+    private String type;
+}

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

@@ -1,6 +1,7 @@
 package com.izouma.nineth.repo;
 
 import com.izouma.nineth.domain.Collection;
+import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -18,6 +19,7 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
     @Query("update Collection t set t.del = true where t.id = ?1")
     @Modifying
     @Transactional
+    @CacheEvict(value = {"collection", "recommend"}, allEntries = true)
     void softDelete(Long id);
 
     @Cacheable("collection")
@@ -38,5 +40,11 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
 
     @Nonnull
     @CachePut(value = "collection", key = "#collection.id")
+    @CacheEvict(value = {"recommend"})
     Collection save(@Nonnull Collection collection);
+
+    @Query("select c from Collection c join Recommend r on c.id = r.collectionId " +
+            "where c.del = false and c.onShelf = true order by r.sort desc")
+    @Cacheable("recommend")
+    List<Collection> recommend(String type);
 }

+ 16 - 0
src/main/java/com/izouma/nineth/repo/RecommendRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.Recommend;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface RecommendRepo extends JpaRepository<Recommend, Long>, JpaSpecificationExecutor<Recommend> {
+    @Query("update Recommend t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

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

@@ -92,6 +92,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/sysConfig/getDecimal/*").permitAll()
                 .antMatchers("/user/code2openId").permitAll()
                 .antMatchers("/blindBoxItem/all").permitAll()
+                .antMatchers("/collection/recommend").permitAll()
                 // all other requests need to be authenticated
                 .anyRequest().authenticated().and()
                 // make sure we use stateless session; session won't be used to

+ 23 - 14
src/main/java/com/izouma/nineth/service/NFTService.java

@@ -3,13 +3,10 @@ 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.common.VMTypeEnum;
 import com.alipay.mychain.sdk.domain.transaction.LogEntry;
-import com.alipay.mychain.sdk.utils.ByteUtils;
 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;
@@ -187,16 +184,28 @@ public class NFTService {
         throw new BusinessException("创建nft失败");
     }
 
-    public void deployContract() throws Exception {
-        ClientParam clientParam = restClient.createDeployContractTransaction(
-                restClientProperties.getDefaultAccount(),
-                "rest_sol_test_2ghd2g2fr",
-                ByteUtils.hexStringToBytes("6080604052606460005534801561001557600080fd5b50610128806100256000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631ab06ee514605857806360fe47b114608c5780636d4ce63c1460b6575b600080fd5b348015606357600080fd5b50608a600480360381019080803590602001909291908035906020019092919050505060de565b005b348015609757600080fd5b5060b46004803603810190808035906020019092919050505060e9565b005b34801560c157600080fd5b5060c860f3565b6040518082815260200191505060405180910390f35b816000819055505050565b8060008190555050565b600080549050905600a165627a7a723058205bcd66c88d325808b2a6429cba1e79b49c8a6cfa4190c9158d4b63a9030ea1d70029"),
-                VMTypeEnum.EVM,
-                50000L);
-        BaseResp resp = restClient.chainCall(
-                clientParam.getHash(),
-                clientParam.getSignData(),
-                Method.DEPLOYCONTRACT);
+    public void deployContract(String name, String code) throws Exception {
+        String orderId = "order_" + System.currentTimeMillis();
+        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
+                .orderId(orderId)
+                .bizid(restClientProperties.getBizid())
+                .account(restClientProperties.getAccount())
+                .contractName(name)
+                .contractCode(code)
+                .mykmsKeyId(restClientProperties.getKmsId())
+                .method(Method.DEPLOYCONTRACTFORBIZ)
+                .tenantid(restClientProperties.getTenantid())
+                .gas(1000000L).build();
+        BaseResp resp = restClient.chainCallForBiz(callRestBizParam);
+        if (!resp.isSuccess()) {
+            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
+        }
+        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);
     }
+
 }

+ 20 - 0
src/main/java/com/izouma/nineth/service/RecommendService.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.domain.Recommend;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.repo.RecommendRepo;
+import com.izouma.nineth.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class RecommendService {
+
+    private RecommendRepo recommendRepo;
+
+    public Page<Recommend> all(PageQuery pageQuery) {
+        return recommendRepo.findAll(JpaUtils.toSpecification(pageQuery, Recommend.class), JpaUtils.toPageRequest(pageQuery));
+    }
+}

+ 5 - 0
src/main/java/com/izouma/nineth/web/CollectionController.java

@@ -94,5 +94,10 @@ public class CollectionController extends BaseController {
     public void appointment(@RequestParam Long id) {
         collectionService.appointment(id, SecurityUtils.getAuthenticatedUser().getId());
     }
+
+    @GetMapping("/recommend")
+    public List<CollectionDTO> recommend(@RequestParam String type) {
+        return collectionService.toDTO(collectionRepo.recommend(type));
+    }
 }
 

+ 60 - 0
src/main/java/com/izouma/nineth/web/RecommendController.java

@@ -0,0 +1,60 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.Recommend;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.RecommendRepo;
+import com.izouma.nineth.service.RecommendService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/recommend")
+@AllArgsConstructor
+public class RecommendController extends BaseController {
+    private RecommendService recommendService;
+    private RecommendRepo recommendRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public Recommend save(@RequestBody Recommend record) {
+        if (record.getId() != null) {
+            Recommend orig = recommendRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return recommendRepo.save(orig);
+        }
+        return recommendRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<Recommend> all(@RequestBody PageQuery pageQuery) {
+        return recommendService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public Recommend get(@PathVariable Long id) {
+        return recommendRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        recommendRepo.deleteById(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<Recommend> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 0 - 0
src/main/resources/access (2).key → src/main/resources/access1.key


+ 15 - 15
src/main/resources/application.yaml

@@ -85,28 +85,28 @@ aliyun:
   oss-domain: https://9space-2021.oss-cn-shenzhen.aliyuncs.com
 general:
   host: https://nfttest.9space.vip
+mychain:
+  rest:
+    bizid: a00e36c5
+    cipher-suit: ec
+    rest-url: https://rest.baas.alipay.com
+    access-id: VB4osv3QHEHBGVZM
+    access-secret: access1.key
+    tenantid: HEHBGVZM
+    read-file-from-ext: false
+    account: nine-space-official
+    kmsId: m9U4I0R2HEHBGVZM1635922538317
 #mychain:
 #  rest:
 #    bizid: a00e36c5
 #    cipher-suit: ec
 #    rest-url: https://rest.baas.alipay.com
-#    access-id: VB4osv3QHEHBGVZM
+#    access-id: 11nv01O0PJCIVRRI
 #    access-secret: access.key
-#    tenantid: HEHBGVZM
+#    tenantid: PJCIVRRI
 #    read-file-from-ext: false
-#    account: nine-space-official
-#    kmsId: m9U4I0R2HEHBGVZM1635922538317
-mychain:
-  rest:
-    bizid: a00e36c5
-    cipher-suit: ec
-    rest-url: https://rest.baas.alipay.com
-    access-id: 11nv01O0PJCIVRRI
-    access-secret: access.key
-    tenantid: PJCIVRRI
-    read-file-from-ext: false
-    account: 9th_test
-    kmsId: pf08UBzfPJCIVRRI1632647899655
+#    account: 9th_test
+#    kmsId: pf08UBzfPJCIVRRI1632647899655
 #alipay:
 #  app-id: 2021002187613003
 #  gateway: https://openapi.alipay.com/gateway.do

+ 1 - 0
src/main/resources/genjson/Recommend.json

@@ -0,0 +1 @@
+{"tableName":"Recommend","className":"Recommend","remark":"首页推荐","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/drew/Projects/Java/9th/src/main/java/com/izouma/nineth","viewPath":"/Users/drew/Projects/Java/9th/src/main/vue/src/views","routerPath":"/Users/drew/Projects/Java/9th/src/main/vue/src","resourcesPath":"/Users/drew/Projects/Java/9th/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"collectionId","modelName":"collectionId","remark":"藏品ID","showInList":true,"showInForm":true,"formType":"number","required":true},{"name":"name","modelName":"name","remark":"名称","showInList":true,"showInForm":true,"formType":"singleLineText","required":true},{"name":"sort","modelName":"sort","remark":"排序","showInList":true,"showInForm":true,"formType":"singleLineText","required":true},{"name":"type","modelName":"type","remark":"类型","showInList":true,"showInForm":true,"formType":"select","required":true,"apiFlag":"1","optionsValue":"[{\"label\":\"列表\",\"value\":\"LIST\"},{\"label\":\"轮播\",\"value\":\"BANNER\"}]"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.Recommend"}

+ 1 - 0
src/main/vue/src/components/CollectionSearch.vue

@@ -43,6 +43,7 @@ export default {
             this.$emit('input', val);
             if (val) {
                 this.selected = this.options.find(i => i.id === val);
+                this.$emit('select', this.selected);
             }
             setTimeout(() => {
                 this.filterOptions = [...this.options];

+ 16 - 0
src/main/vue/src/router.js

@@ -364,6 +364,22 @@ const router = new Router({
                     meta: {
                        title: '分销记录',
                     },
+               },
+                {
+                    path: '/recommendEdit',
+                    name: 'RecommendEdit',
+                    component: () => import(/* webpackChunkName: "recommendEdit" */ '@/views/RecommendEdit.vue'),
+                    meta: {
+                       title: '首页推荐编辑',
+                    },
+                },
+                {
+                    path: '/recommendList',
+                    name: 'RecommendList',
+                    component: () => import(/* webpackChunkName: "recommendList" */ '@/views/RecommendList.vue'),
+                    meta: {
+                       title: '首页推荐',
+                    },
                }
                 /**INSERT_LOCATION**/
             ]

+ 153 - 0
src/main/vue/src/views/RecommendEdit.vue

@@ -0,0 +1,153 @@
+<template>
+    <div class="edit-view">
+        <page-title>
+            <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+            <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id"> 删除 </el-button>
+            <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+        </page-title>
+        <div class="edit-view__content-wrapper">
+            <div class="edit-view__content-section">
+                <el-form
+                    :model="formData"
+                    :rules="rules"
+                    ref="form"
+                    label-width="100px"
+                    label-position="right"
+                    size="small"
+                    style="max-width: 500px"
+                >
+                    <el-form-item prop="collectionId" label="藏品/盲盒ID">
+                        <collection-search v-model="formData.collectionId" @select="onSelect"></collection-search>
+                    </el-form-item>
+                    <el-form-item prop="sort" label="排序">
+                        <el-input-number v-model="formData.sort" :min="0"></el-input-number>
+                    </el-form-item>
+                    <el-form-item prop="type" label="类型">
+                        <el-select v-model="formData.type" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in typeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item class="form-submit">
+                        <el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+                        <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                            删除
+                        </el-button>
+                        <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name: 'RecommendEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('recommend/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {
+                collectionId: [
+                    {
+                        required: true,
+                        message: '请输入藏品ID',
+                        trigger: 'blur'
+                    }
+                ],
+                name: [
+                    {
+                        required: true,
+                        message: '请输入名称',
+                        trigger: 'blur'
+                    }
+                ],
+                sort: [
+                    {
+                        required: true,
+                        message: '请输入排序',
+                        trigger: 'blur'
+                    }
+                ],
+                type: [
+                    {
+                        required: true,
+                        message: '请输入类型',
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            typeOptions: [
+                { label: '列表', value: 'LIST' },
+                { label: '轮播', value: 'BANNER' }
+            ]
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/recommend/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$confirm('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/recommend/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error((e || {}).error || '删除失败');
+                    }
+                });
+        },
+        onSelect(e) {
+            this.$set(this.formData, 'name', e.name);
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 185 - 0
src/main/vue/src/views/RecommendList.vue

@@ -0,0 +1,185 @@
+<template>
+    <div class="list-view">
+        <page-title>
+            <el-button
+                @click="addRow"
+                type="primary"
+                icon="el-icon-plus"
+                :disabled="fetchingData || downloading"
+                class="filter-item"
+            >
+                新增
+            </el-button>
+        </page-title>
+        <div class="filters-container">
+            <el-select v-model="type" clearable @change="getData">
+                <el-option v-for="item in typeOptions" :label="item.label" :value="item.value"></el-option>
+            </el-select>
+            <el-input
+                placeholder="搜索..."
+                v-model="search"
+                clearable
+                class="filter-item search"
+                @keyup.enter.native="getData"
+            >
+                <el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
+            </el-input>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+            v-loading="fetchingData"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="collectionId" label="藏品/盲盒ID"> </el-table-column>
+            <el-table-column prop="name" label="名称"> </el-table-column>
+            <el-table-column prop="sort" label="排序"> </el-table-column>
+            <el-table-column prop="type" label="类型" :formatter="typeFormatter"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'RecommendList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/recommend/all',
+            downloading: false,
+            typeOptions: [
+                { label: '列表', value: 'LIST' },
+                { label: '轮播', value: 'BANNER' }
+            ],
+            type: null,
+            sortStr: 'sort,desc'
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        typeFormatter(row, column, cellValue, index) {
+            let selectedOption = this.typeOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return { search: this.search, query: { del: false, type: this.type } };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/recommendEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/recommendEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/recommend/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/recommend/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+</style>

+ 8 - 1
src/test/java/com/izouma/nineth/CommonTest.java

@@ -1,10 +1,12 @@
 package com.izouma.nineth;
 
+import com.alipay.mychain.sdk.utils.ByteUtils;
 import com.github.kevinsawicki.http.HttpRequest;
 import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.domain.BaseEntity;
 import com.izouma.nineth.domain.BlindBoxItem;
 import com.izouma.nineth.domain.User;
+import com.izouma.nineth.utils.HashUtils;
 import com.izouma.nineth.web.BaseController;
 import io.ipfs.api.IPFS;
 import io.ipfs.api.MerkleNode;
@@ -361,7 +363,12 @@ public class CommonTest {
     }
 
     @Test
-    public void testReplace(){
+    public void testReplace() {
         System.out.println("15077886171".replaceAll("(?<=.{3}).*(?=.{4})", "**"));
     }
+
+    @Test
+    public void sfsdf() {
+        System.out.println(ByteUtils.hexStringToBytes(HashUtils.Keccak256(UUID.randomUUID().toString())));
+    }
 }

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

@@ -5,6 +5,9 @@ import org.apache.commons.lang3.RandomStringUtils;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.io.File;
+import java.util.Scanner;
+
 public class NFTServiceTest extends ApplicationTests {
     @Autowired
     private NFTService nftService;
@@ -16,7 +19,9 @@ public class NFTServiceTest extends ApplicationTests {
 
     @Test
     public void createToken() throws Exception {
-        nftService.createToken("9th_BHlKkGWw");
+        for (int i = 0; i < 100; i++) {
+            nftService.createToken("nine-space-official");
+        }
     }
 
     @Test
@@ -33,4 +38,36 @@ public class NFTServiceTest extends ApplicationTests {
     public void setApprovalForAll() throws Exception {
         nftService.setApprovalForAll("9th_test");
     }
+
+    @Test
+    public void deployContract() throws Exception {
+        ProcessBuilder builder = new ProcessBuilder("/Users/drew/Projects/Java/9th/src/main/contract/mychain_solc", "--bin", "9th.sol");
+        builder.directory(new File("/Users/drew/Projects/Java/9th/src/main/contract").getAbsoluteFile());
+
+        Process process = builder.start();
+
+        Scanner s = new Scanner(process.getInputStream());
+        StringBuilder text = new StringBuilder();
+        while (s.hasNextLine()) {
+            text.append(s.nextLine());
+            text.append("\n");
+        }
+        s.close();
+
+        int result = process.waitFor();
+
+        String[] lines = text.toString().split("\n");
+        int idx = 0;
+        for (int i = 0; i < lines.length; i++) {
+            if ("======= 9th.sol:ERC721PresetMinterPauserAutoId =======".equals(lines[i])) {
+                idx = i;
+                break;
+            }
+        }
+        System.out.println(lines[idx]);
+        System.out.println(lines[idx + 2]);
+
+
+        nftService.deployContract("nine_space_test" + System.currentTimeMillis(), lines[idx + 2]);
+    }
 }