فهرست منبع

Merge branch 'dev'

# Conflicts:
#	src/main/java/com/izouma/nineth/service/AssetMintService.java
#	src/main/java/com/izouma/nineth/service/NFTService.java
#	src/test/java/com/izouma/nineth/service/AdapayServiceTest.java
xiongzhu 4 سال پیش
والد
کامیت
8981aea74f

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

@@ -137,7 +137,11 @@ public class Collection extends BaseEntity {
     @Formula(value = "if(stock = 0, 1, 0)")
     private int soldOut;
 
+    @ApiModelProperty("限购数量")
     private int maxCount;
 
+    @ApiModelProperty("限购识别码")
+    private String countId;
+
     private boolean scanCode;
 }

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

@@ -192,4 +192,6 @@ public class Order {
     public LocalDateTime getCreatedAt() {
         return createdAt;
     }
+
+    private String countId;
 }

+ 6 - 9
src/main/java/com/izouma/nineth/repo/CollectionRepo.java

@@ -1,9 +1,6 @@
 package com.izouma.nineth.repo;
 
 import com.izouma.nineth.domain.Collection;
-import com.izouma.nineth.domain.CollectionProperty;
-import com.izouma.nineth.domain.FileObject;
-import com.izouma.nineth.domain.Privilege;
 import com.izouma.nineth.dto.RecommendCollection;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
@@ -28,13 +25,13 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
 
     @Transactional
     @Modifying
-    @Query("update Collection c set c.onShelf = ?2, c.salable = ?3, c.startTime = ?4, " +
-            "c.scheduleSale = ?5, c.sort = ?6, c.detail = ?7, c.privileges = ?8, " +
-            "c.properties = ?9, c.model3d = ?10 where c.id = ?1")
+    @Query(value = "update collection_info c set c.on_shelf = ?2, c.salable = ?3, c.start_time = ?4, " +
+            "c.schedule_sale = ?5, c.sort = ?6, c.detail = ?7, c.privileges = ?8, " +
+            "c.properties = ?9, c.model3d = ?10, c.max_count = ?11, c.count_id = ?12 where c.id = ?1",nativeQuery = true)
     @CacheEvict(value = {"collection", "recommend"}, allEntries = true)
     void update(@Nonnull Long id, boolean onShelf, boolean salable, LocalDateTime startTime,
-                boolean schedule, int sort, String detail, List<Privilege> privileges,
-                List<CollectionProperty> properties, FileObject model3d);
+                boolean schedule, int sort, String detail, String privileges,
+                String properties, String model3d, int maxCount, String countId);
 
     @Cacheable("collection")
     Optional<Collection> findById(@Nonnull Long id);
@@ -93,7 +90,7 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
 
     @Transactional
     @Modifying
-    @Query("update Collection c set c.onShelf = true, c.salable = true where c.id = ?1")
+    @Query("update Collection c set c.scheduleSale = false, c.startTime = null, c.onShelf = true, c.salable = true where c.id = ?1")
     @CacheEvict(value = "collection", key = "#id")
     void scheduleOnShelf(Long id);
 

+ 3 - 1
src/main/java/com/izouma/nineth/repo/OrderRepo.java

@@ -26,7 +26,9 @@ public interface OrderRepo extends JpaRepository<Order, Long>, JpaSpecificationE
 
     List<Order> findByCollectionIdIn(Iterable<Long> collectionId);
 
-    int countByCollectionIdAndStatusIn(Long collectionId, Iterable<OrderStatus> orderStatuses);
+    int countByUserIdAndCollectionIdAndStatusIn(Long userId, Long collectionId, Iterable<OrderStatus> orderStatuses);
+
+    int countByUserIdAndCountIdAndStatusIn(Long userId, String countId, Iterable<OrderStatus> orderStatuses);
 
     List<Order> findByStatus(OrderStatus orderStatus);
 

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

@@ -18,11 +18,9 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.context.ApplicationContext;
 import org.springframework.scheduling.annotation.Async;
-import org.springframework.scheduling.annotation.AsyncResult;
 import org.springframework.stereotype.Service;
 
 import java.io.File;
-import java.util.concurrent.Future;
 
 @Service
 @Slf4j
@@ -34,7 +32,38 @@ public class AssetMintService {
     private ApplicationContext applicationContext;
 
     @Async
-    public Future<Asset> mint(Asset asset) {
+    public void mint(Long assetId, Long userId) {
+        User user = userRepo.findById(userId).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);
+        }
+        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());
+                asset.setBlockNumber(nft.getBlockNumber());
+                asset.setTxHash(nft.getTxHash());
+                asset.setGasUsed(nft.getGasUsed());
+                if (asset.getIpfsUrl() == null) {
+                    asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl()));
+                }
+                assetRepo.save(asset);
+                applicationContext.publishEvent(new CreateAssetEvent(this, true, asset));
+            } else {
+                log.error("铸造失败");
+            }
+        } catch (Exception e) {
+            log.error("铸造失败", e);
+        }
+    }
+
+    @Async
+    public void mint(Asset asset) {
         User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
         if (StringUtils.isEmpty(user.getPublicKey())) {
             NFTAccount account = nftService.createAccount(user.getUsername() + "_");
@@ -59,7 +88,6 @@ public class AssetMintService {
             log.error("铸造失败", e);
         }
         applicationContext.publishEvent(new CreateAssetEvent(this, true, asset));
-        return new AsyncResult<>(asset);
     }
 
     public String ipfsUpload(String url) {

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

@@ -81,7 +81,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())
@@ -94,7 +94,7 @@ public class AssetService {
                 .operation(type)
                 .price(price)
                 .build());
-        assetMintService.mint(asset);
+        assetMintService.mint(asset.getId(), user.getId());
         return asset;
     }
 

+ 35 - 34
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -1,5 +1,6 @@
 package com.izouma.nineth.service;
 
+import com.alibaba.fastjson.JSON;
 import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.CollectionDTO;
@@ -23,7 +24,6 @@ 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;
@@ -105,36 +105,47 @@ public class CollectionService {
             if (record.getStartTime() == null) {
                 throw new BusinessException("请填写定时发布时间");
             }
-            record.setOnShelf(record.getStartTime().isBefore(LocalDateTime.now()));
-        }
-        collectionRepo.save(record);
-        if (record.isScheduleSale()) {
-            onShelfTask(record);
+            if (record.getStartTime().isBefore(LocalDateTime.now())) {
+                record.setOnShelf(true);
+                record.setSalable(true);
+                record.setStartTime(null);
+            }
         }
+        record = collectionRepo.save(record);
+        onShelfTask(record);
         return record;
     }
 
-    private void onShelfTask(Collection record) {
-        if (record.getStartTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
-            Date date = Date.from(record.getStartTime().atZone(ZoneId.systemDefault()).toInstant());
-            ScheduledFuture<?> future = taskScheduler.schedule(() -> {
-                collectionRepo.scheduleOnShelf(record.getId());
-                tasks.remove(record.getId());
-            }, date);
-            tasks.put(record.getId(), future);
-        } else {
-            collectionRepo.scheduleOnShelf(record.getId());
-        }
-    }
-
     public Collection update(Collection record) {
-        Collection orig = collectionRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
         collectionRepo.update(record.getId(), record.isOnShelf(), record.isSalable(),
                 record.getStartTime(), record.isScheduleSale(), record.getSort(),
-                record.getDetail(), record.getPrivileges(), record.getProperties(),
-                record.getModel3d());
+                record.getDetail(), JSON.toJSONString(record.getPrivileges()),
+                JSON.toJSONString(record.getProperties()), JSON.toJSONString(record.getModel3d()),
+                record.getMaxCount(), record.getCountId());
+        record = collectionRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+        onShelfTask(record);
+        return record;
+    }
 
-        return collectionRepo.save(orig);
+    private void onShelfTask(Collection record) {
+        ScheduledFuture<?> task = tasks.get(record.getId());
+        if (task != null) {
+            if (!task.cancel(true)) {
+                return;
+            }
+        }
+        if (record.isScheduleSale()) {
+            if (record.getStartTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
+                Date date = Date.from(record.getStartTime().atZone(ZoneId.systemDefault()).toInstant());
+                ScheduledFuture<?> future = taskScheduler.schedule(() -> {
+                    collectionRepo.scheduleOnShelf(record.getId());
+                    tasks.remove(record.getId());
+                }, date);
+                tasks.put(record.getId(), future);
+            } else {
+                collectionRepo.scheduleOnShelf(record.getId());
+            }
+        }
     }
 
     public CollectionDTO toDTO(Collection collection) {
@@ -185,8 +196,7 @@ public class CollectionService {
     public Collection createBlindBox(CreateBlindBox createBlindBox) {
         Collection blindBox = createBlindBox.getBlindBox();
         if (blindBox.getId() != null) {
-            collectionRepo.save(blindBox);
-            return blindBox;
+            throw new BusinessException("无法完成此操作");
         }
 
         List<Collection> list =
@@ -245,15 +255,6 @@ public class CollectionService {
                 .build());
     }
 
-    @Scheduled(fixedRate = 10000)
-    public void scheduleOnShelf() {
-        List<Collection> collections = collectionRepo.findByScheduleSaleTrueAndOnShelfFalseAndStartTimeBeforeAndDelFalse(LocalDateTime.now());
-        for (Collection collection : collections) {
-            collectionRepo.scheduleOnShelf(collection.getId());
-        }
-    }
-
-
     public BlindBoxItem draw(Long collectionId) {
         List<BlindBoxItem> items = blindBoxItemRepo.findByBlindBoxId(collectionId);
 

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

@@ -34,6 +34,7 @@ public class NFTService {
     private final RestClientProperties restClientProperties;
     private final GeneralProperties    generalProperties;
 
+    @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()))
@@ -47,11 +48,9 @@ public class NFTService {
         try {
             BaseResp baseResp = restClient.chainCallForBiz(callRestBizParam);
             NFTAccount account = new NFTAccount(username, Constants.kmsKey, baseResp.getData());
-            if (baseResp.isSuccess()) {
+            if (baseResp.isSuccess() || "400123".equals(baseResp.getCode())) {
                 log.info("创建账户成功 {}", account);
                 return account;
-            } else if ("400123".equals(baseResp.getCode())) {
-                return account;
             } else {
                 throw new RuntimeException(baseResp.getCode());
             }
@@ -62,7 +61,7 @@ public class NFTService {
         }
     }
 
-    @Retryable(maxAttempts = 100, backoff = @Backoff(delay = 5000), value = BusinessException.class)
+    @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
     public synchronized NFT createToken(String toAccount, String tokenId) throws Exception {
         JSONArray jsonArray = new JSONArray();
         jsonArray.add(Utils.getIdentityByName(toAccount));
@@ -94,7 +93,10 @@ public class NFTService {
             NFT nft = new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
             log.info("NFT生成成功 {}", nft);
             return nft;
-        } else {
+        } else if ("211".equals(resp.getCode())) {
+
+        } else if ("10201".equals(resp.getCode())) {
+            // 异步交易未成功需要根据状态码判断交易状态
             ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
             String out = new String(matchAndReplaceNonEnglishChar(txReceipt.getOutput()));
             // 异步交易未成功需要根据状态码判断交易状态

+ 24 - 15
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -112,11 +112,19 @@ public class OrderService {
         if (!collection.isSalable()) {
             throw new BusinessException("该藏品当前不可购买");
         }
-        if (collection.getType() == CollectionType.BLIND_BOX) {
-            if (collection.getStartTime().isAfter(LocalDateTime.now())) {
-                throw new BusinessException("盲盒未开售");
+
+        if (collection.getMaxCount() > 0) {
+            int count;
+            if (StringUtils.isNotBlank(collection.getCountId())) {
+                count = orderRepo.countByUserIdAndCountIdAndStatusIn(userId, collection.getCountId(), Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
+            } else {
+                count = orderRepo.countByUserIdAndCollectionIdAndStatusIn(userId, collectionId, Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
+            }
+            if (count >= collection.getMaxCount()) {
+                throw new BusinessException("限购" + collection.getMaxCount() + "件");
             }
         }
+
         UserAddress userAddress = null;
         if (addressId != null) {
             userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
@@ -154,6 +162,7 @@ public class OrderService {
                 .assetId(collection.getAssetId())
                 .couponId(userCouponId)
                 .invitor(invitor)
+                .countId(collection.getCountId())
                 .build();
         if (coupon != null) {
             coupon.setUsed(true);
@@ -280,7 +289,7 @@ public class OrderService {
         paymentParams.put("goods_title", collection.getName());
         paymentParams.put("goods_desc", collection.getName());
         paymentParams.put("time_expire", DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
-                .format(LocalDateTime.now().plusMinutes(5)));
+                .format(LocalDateTime.now().plusMinutes(3)));
         paymentParams.put("notify_url", adapayProperties.getNotifyUrl() + "/order/" + order.getId());
 
         List<Map<String, Object>> divMembers = new ArrayList<>();
@@ -416,15 +425,15 @@ 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()).orElse(null);
+            if (event.isSuccess() && order != null) {
+                order.setTxHash(asset.getTxHash());
+                order.setGasUsed(asset.getGasUsed());
+                order.setBlockNumber(asset.getBlockNumber());
+                order.setStatus(OrderStatus.FINISH);
+                orderRepo.save(order);
+            }
         }
     }
 
@@ -480,10 +489,10 @@ public class OrderService {
         }
     }
 
-    @Scheduled(fixedRate = 60000)
+    @Scheduled(fixedRate = 30000)
     public void batchCancel() {
         List<Order> orders = orderRepo.findByStatusAndCreatedAtBeforeAndDelFalse(OrderStatus.NOT_PAID,
-                LocalDateTime.now().minusMinutes(5));
+                LocalDateTime.now().minusSeconds(210));
         orders.forEach(o -> {
             try {
                 cancel(o);

+ 1 - 10
src/main/java/com/izouma/nineth/web/CollectionController.java

@@ -9,7 +9,6 @@ import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.CollectionRepo;
 import com.izouma.nineth.service.CollectionService;
 import com.izouma.nineth.service.LikeService;
-import com.izouma.nineth.utils.ObjUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 import com.izouma.nineth.utils.excel.ExcelUtils;
 import io.swagger.annotations.ApiOperation;
@@ -36,15 +35,7 @@ public class CollectionController extends BaseController {
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public Collection save(@RequestBody Collection record) {
-        Collection orig = collectionRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
-        orig.setOnShelf(record.isOnShelf());
-        orig.setSalable(record.isSalable());
-        orig.setStartTime(record.getStartTime());
-        orig.setScheduleSale(record.isScheduleSale());
-        orig.setSort(record.getSort());
-        orig.setDetail(record.getDetail());
-        orig.setPrivileges(record.getPrivileges());
-        return collectionRepo.save(orig);
+        return collectionService.update(record);
     }
 
     @PostMapping("/create")

+ 17 - 8
src/main/resources/application.yaml

@@ -12,9 +12,9 @@ spring:
   profiles:
     active: dev
   datasource:
-    url: jdbc:mysql://rm-wz9sc79f5255780opqo.mysql.rds.aliyuncs.com/raex_test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
-    username: raex
-    password: K@SdS7e6NTw4CK
+    url: jdbc:mysql://192.168.50.10/raex_test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
+    username: root
+    password: 123456
     hikari:
       minimum-idle: 20
       maximum-pool-size: 300
@@ -77,13 +77,12 @@ spring:
         enable_lazy_load_no_trans: true
     open-in-view: true
   redis:
-    host: 120.78.171.194
-    password: jV%93RtjUx82Tp
+    host: 192.168.50.16
     port: 6379
     lettuce:
       pool:
-        max_active: 8
-        max_idle: 8
+        max_active: 50
+        max_idle: 50
         min_idle: 0
   servlet:
     multipart:
@@ -173,16 +172,26 @@ adapay:
 
 spring:
   profiles: test
-
+  datasource:
+    url: jdbc:mysql://rm-wz9sc79f5255780opqo.mysql.rds.aliyuncs.com/raex_test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
+    username: raex
+    password: K@SdS7e6NTw4CK
+  redis:
+    host: 127.0.0.1
+    database: 0
+    password: jV%93RtjUx82Tp
 ---
 
 spring:
   profiles: prod
   datasource:
     url: jdbc:mysql://rm-wz9sc79f5255780op.mysql.rds.aliyuncs.com/raex?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
+    username: raex
+    password: K@SdS7e6NTw4CK
   redis:
     host: 127.0.0.1
     database: 1
+    password: jV%93RtjUx82Tp
 general:
   host: https://www.raex.vip
 wx:

+ 54 - 16
src/main/vue/src/views/BlindBoxEdit.vue

@@ -146,6 +146,14 @@
                     <el-form-item prop="salable" label="可售">
                         <el-switch v-model="formData.salable" active-text="可销售" inactive-text="仅展示"></el-switch>
                     </el-form-item>
+                    <el-form-item prop="maxCount" label="限购">
+                        <el-input-number v-model="formData.maxCount"></el-input-number>
+                        <div class="tip">0表示不限购</div>
+                    </el-form-item>
+                    <el-form-item prop="countId" label="限购识别码" v-if="formData.maxCount > 0">
+                        <el-input v-model="formData.countId"></el-input>
+                        <div class="tip">相同识别码的藏品共享限购数量</div>
+                    </el-form-item>
                     <el-form-item class="form-submit">
                         <el-button @click="onSave" :loading="saving" type="primary" v-if="!formData.id">
                             保存
@@ -276,7 +284,11 @@ export default {
                 properties: [],
                 type: 'BLIND_BOX',
                 source: 'OFFICIAL',
-                pic: [{}]
+                pic: [{}],
+                scheduleSale: true,
+                privileges: [],
+                maxCount: 0,
+                countId: null
             },
             rules: {
                 name: [
@@ -397,7 +409,17 @@ export default {
                         trigger: 'blur'
                     }
                 ],
-                startTime: [{ required: true, message: '请填写开售时间' }],
+                startTime: [
+                    {
+                        validator: (rule, value, callback) => {
+                            if (this.formData.scheduleSale === true && !value) {
+                                callback(new Error('请填写开售时间'));
+                                return;
+                            }
+                            callback();
+                        }
+                    }
+                ],
                 items: [
                     {
                         validator: (rule, value, callback) => {
@@ -444,21 +466,37 @@ export default {
             });
         },
         submit() {
-            let data = { blindBox: { ...this.formData }, items: this.blindBoxItems };
+            if (this.formData.id) {
+                this.saving = true;
+                this.$http
+                    .post('/collection/save', this.formData, { 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);
+                    });
+            } else {
+                let data = { blindBox: { ...this.formData }, items: this.blindBoxItems };
 
-            this.saving = true;
-            this.$http
-                .post('/collection/createBlindBox', 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);
-                });
+                this.saving = true;
+                this.$http
+                    .post('/collection/createBlindBox', 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' })

+ 17 - 22
src/main/vue/src/views/CollectionEdit.vue

@@ -59,33 +59,17 @@
                         <el-table :data="formData.properties">
                             <el-table-column prop="name" label="名称">
                                 <template v-slot="{ row }">
-                                    <el-input
-                                        v-model="row.name"
-                                        placeholder="20字以内"
-                                        maxlength="20"
-                                        :disabled="!canEdit"
-                                    ></el-input>
+                                    <el-input v-model="row.name" placeholder="20字以内" maxlength="20"></el-input>
                                 </template>
                             </el-table-column>
                             <el-table-column prop="value" label="内容">
                                 <template v-slot="{ row }">
-                                    <el-input
-                                        v-model="row.value"
-                                        placeholder="20字以内"
-                                        maxlength="20"
-                                        :disabled="!canEdit"
-                                    ></el-input>
+                                    <el-input v-model="row.value" placeholder="20字以内" maxlength="20"></el-input>
                                 </template>
                             </el-table-column>
                             <el-table-column width="80" align="center">
                                 <template v-slot="{ row, $index }">
-                                    <el-button
-                                        type="danger"
-                                        plain
-                                        size="mini"
-                                        @click="delProperty($index)"
-                                        :disabled="!canEdit"
-                                    >
+                                    <el-button type="danger" plain size="mini" @click="delProperty($index)">
                                         删除
                                     </el-button>
                                 </template>
@@ -93,7 +77,7 @@
                         </el-table>
                     </el-form-item>
                     <el-form-item>
-                        <el-button size="mini" @click="addProperty" :disabled="!canEdit"> 添加特性 </el-button>
+                        <el-button size="mini" @click="addProperty"> 添加特性 </el-button>
                     </el-form-item>
                     <el-form-item label="特权" prop="privileges" style="width: calc(100vw - 450px)">
                         <el-table :data="privilegeOptions">
@@ -187,6 +171,14 @@
                         <el-input-number v-model="formData.sort" :min="0"></el-input-number>
                         <div class="tip">数字越大排序越靠前,相同数值按创建时间倒序排列</div>
                     </el-form-item>
+                    <el-form-item prop="maxCount" label="限购">
+                        <el-input-number v-model="formData.maxCount"></el-input-number>
+                        <div class="tip">0表示不限购</div>
+                    </el-form-item>
+                    <el-form-item prop="countId" label="限购识别码" v-if="formData.maxCount > 0">
+                        <el-input v-model="formData.countId"></el-input>
+                        <div class="tip">相同识别码的藏品共享限购数量</div>
+                    </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">
@@ -290,7 +282,10 @@ export default {
                 pic: [],
                 scheduleSale: true,
                 sort: 0,
-                privileges: []
+                privileges: [],
+                maxCount: 0,
+                countId: null,
+                canResale: true
             },
             rules: {
                 name: [
@@ -512,7 +507,7 @@ export default {
             }
         },
         delPrivilege(row, i) {
-            let idx = this.formData.privileges.find(e => e.name === row.name);
+            let idx = this.formData.privileges.findIndex(e => e.name === row.name);
             if (idx > -1) {
                 this.formData.privileges.splice(idx, 1);
             }

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

@@ -1,9 +1,11 @@
 package com.izouma.nineth;
 
+import com.alibaba.fastjson.JSON;
 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.Privilege;
 import com.izouma.nineth.domain.User;
 import com.izouma.nineth.utils.TokenUtils;
 import com.izouma.nineth.web.BaseController;
@@ -394,4 +396,10 @@ public class CommonTest {
             }
         }
     }
+
+    @Test
+    public void jj(){
+        System.out.println(JSON.toJSONString(null) == null);
+        System.out.println(JSON.parseObject("null", Privilege.class));
+    }
 }

+ 28 - 128
src/test/java/com/izouma/nineth/service/AdapayServiceTest.java

@@ -5,13 +5,12 @@ 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.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.config.GeneralProperties;
-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;
@@ -19,7 +18,6 @@ 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;
@@ -27,15 +25,25 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 
-public class AdapayServiceTest extends ApplicationTests {
-    @Autowired
-    private AdapayService     adapayService;
-    @Autowired
-    private GeneralProperties generalProperties;
-    @Autowired
-    private OrderRepo         orderRepo;
+public class AdapayServiceTest {
+    private final String appId = "app_0e8d3acb-3d95-4ebb-8445-e470c378a787";
+
+    public AdapayServiceTest() {
+        Adapay.debug = true;
+        Adapay.prodMode = true;
+
+        MerConfig merConfig = new MerConfig();
+        merConfig.setApiKey("api_live_dc298e47-c0be-4acf-a962-a2c2988e4cae");
+        merConfig.setApiMockKey("api_test_26e9eee7-6695-4169-90a1-203c6d2cf196");
+        merConfig.setRSAPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLeSEu8CXg4iwpRtAapIGpbuoJdyAh3E2FgIqOJSME/0+DscuIUVzfQzMWgXT98Zd8aKQLmjTWHx6Tg60DCFJzJBkISZYaqF9Oc+YdPe15TINpp5syKDk72WeUEW/jlXiLwKyeFxzCvedbpbdAUi0cP64US/LslGohHGWUrgj/WBR33vgwA2NAHR+ziGp8IhFUXHoWtXdBjwGsWuZ6Wt9mWOVeGIHP6OyUYzhkVuDyFMZ6wlA60PqxPAQrvZG3VP2mSlpWTs7xQHM9nk8Rn2pQDUmexHmKS/wn4XZvl3S9KRs6/l8wDqlBW5QhmEZNRwiOG8n/FpqxUMIhFFj4SSq1AgMBAAECggEAVwmWXbZbzQUXzgJ058t1ZwjiYFnI4ZibgA6BaMdgHUQ+mM6hV0Z/EIzdGtRa8AaOJIMgrGTlSCJPcHshwty0p0oFnRhe5e/g2hKVrfXxdlr7PznrLdfQL7syWkKvcnTar0vj7Rw7RIRCFv1JaIhfwlszBVOIG39a46LW+XvJ/Z5Lk5wB8tkt0xWDUghAU1xP10P8OHcr/x3aFhiyAvmWWr7syYKD1rDAtC+n+6Imb8MvTxwk4Gz1wpM+a7gHeYk2n91yR94G6A2wMSeM80T4hCbYKutD5rkdi5i3hoeIiPiq5qbR0rmI/dudVbGkVT+xkh1z1IsKeYiD2ef4Ddy2QQKBgQDIqOuaml9bnJpKie9Z0ysiPWxc771byxhgx7bwsoJey4x5bcLx+G9IAvkGwWFUl+25jgpeVn/LZ6147e9ozeip7WSKvStniND+CB7SvA5ZDRm7CfqFQub//9Q1DWNfl+ThGAXHMlKgS7DanLqvpUsRdhUOceZwwrdkV93v7by8nQKBgQCx8EdYbxNIEnVI3Y98YCAWtvgBCCa8yurL+9IXe9VKCFGdGRZ4b61GWDNyjUAfiboXY4ByzvX4cdlZ1euVmqMYsEv28xXEFdGxSDpa+oeETbv1U2GEGL4Ups3z28Sx4TGGSyvReyQOhZftIF6vCZtZ8dZMEf8IdxyudJkcJp+u+QKBgBFtemkHF1khlNT8felTSd/DbfH0cIHUdd2R+vWUy3XoP98cBV52sVOTzoUjroxmVaNUDtp6sMa9znc+UxjLKXX4xE64d1iarWwi0GqFIsnhNWblSvjgAqghVBD3hLX8v0g9ieLvH/YEHOwfyKcQuCBgHRwQEG+iucLhTslT4JyRAoGAHa25CKwGKEhD3bJuw0z5LTC5btqgM28Y+Ir5AMe7zIxUqIJNuCrQWOJPOnUK0/fR3SLQgtW4OwcqPIysrZhMScrl8Luczsbg4dPtP813mv6oMgQFSNYjpigoQ9tNFGo+K2sQVPFYEz//FiMHB+TvT3JBzxBVXGEZnJOAEizzB2kCgYEAxUFRiJMcmp5IyGcFlmGP219OcEfzt8BV8s3yoPHPpgYX/zPsH9764UMSZb+FRXFLd8HC+UqqppQ4cq2RmMu5X7H/DWoj9FqXgusjOmZUyWiJgTPC4ktFesuhJhCHuk/50/nXmc6O8rWLwXqXf1XNaoNfzDmZNQ20jRZGr8eVc4g=");
+        merConfig.setRSAPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi3khLvAl4OIsKUbQGqSBqW7qCXcgIdxNhYCKjiUjBP9Pg7HLiFFc30MzFoF0/fGXfGikC5o01h8ek4OtAwhScyQZCEmWGqhfTnPmHT3teUyDaaebMig5O9lnlBFv45V4i8Csnhccwr3nW6W3QFItHD+uFEvy7JRqIRxllK4I/1gUd974MANjQB0fs4hqfCIRVFx6FrV3QY8BrFrmelrfZljlXhiBz+jslGM4ZFbg8hTGesJQOtD6sTwEK72Rt1T9pkpaVk7O8UBzPZ5PEZ9qUA1JnsR5ikv8J+F2b5d0vSkbOv5fMA6pQVuUIZhGTUcIjhvJ/xaasVDCIRRY+EkqtQIDAQAB");
+        Adapay.publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwN6xgd6Ad8v2hIIsQVnbt8a3JituR8o4Tc3B5WlcFR55bz4OMqrG/356Ur3cPbc2Fe8ArNd/0gZbC9q56Eb16JTkVNA/fye4SXznWxdyBPR7+guuJZHc/VW2fKH2lfZ2P3Tt0QkKZZoawYOGSMdIvO+WqK44updyax0ikK6JlNQIDAQAB";
+        try {
+            Adapay.initWithMerConfig(merConfig);
+        } catch (Exception e) {
+        }
+    }
 
     @Test
     public void testPay() throws BaseAdaPayException {
@@ -51,7 +59,7 @@ public class AdapayServiceTest extends ApplicationTests {
             put("amount", BigDecimal.valueOf(0.05));
         }});
 
-        paymentParams.put("app_id", "app_f8760acc-f4d8-46f6-8f70-d80e36517075");
+        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");
@@ -74,37 +82,16 @@ public class AdapayServiceTest extends ApplicationTests {
         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", "app_f8760acc-f4d8-46f6-8f70-d80e36517075");
+        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", generalProperties.getHost() + "/9th/orders");
+        params.put("callback_url", "/9th/orders");
         Map<String, Object> response = AdapayCommon.requestAdapayUits(params);
         System.out.println("payment result=" + JSON.toJSONString(response, SerializerFeature.PrettyFormat));
     }
 
-    @Test
-    public void createMember() throws BaseAdaPayException {
-        adapayService.createMember(99999999999999L, "15077886171", "熊竹", "321002199408304614");
-    }
-
-    @Test
-    public void createSettleAccount() throws BaseAdaPayException {
-        adapayService.createSettleAccount("99999999999999", "熊竹", "321002199408304614",
-                "15077886171", "6222024301070380163");
-    }
-
-    @Test
-    public void delSettleAccount() throws BaseAdaPayException {
-        adapayService.delSettleAccount("1110", "0288514678171392");
-    }
-
-    @Test
-    public void queryBalance() throws BaseAdaPayException {
-        adapayService.queryBalance("1110", "0288514678171392");
-    }
-
     @Data
     public static class RefundOrder {
         @ExcelProperty("交易时间")
@@ -185,100 +172,13 @@ public class AdapayServiceTest extends ApplicationTests {
     }
 
     @Test
-    public void rrr() throws BaseAdaPayException {
-        Map<String, Object> response1 = Payment.query("002112021120816060510314566462621900800");
-        String refundId = new SnowflakeIdWorker(0, 0).nextId() + "";
-        Map<String, Object> refundParams = new HashMap<>();
-        refundParams.put("refund_amt", "10.80");
-        refundParams.put("refund_order_no", new SnowflakeIdWorker(0, 0).nextId() + "");
-        Map<String, Object> response = Refund.create("002112021120816060510314566462621900800", refundParams);
-    }
-
-    @Test
-    public void refund1() throws BaseAdaPayException {
-        List<RefundOrder> orders = EasyExcel.read("/Users/drew/Desktop/refund2.xlsx")
-                .head(RefundOrder.class)
-                .registerConverter(new LocalDateConverter())
-                .registerConverter(new LocalDateTimeConverter())
-                .registerConverter(new BigIntegerConverter())
-                .sheet().doReadSync();
-        System.out.println(orders.size());
-
-        orders = orders.stream().parallel().filter(o ->
-                        o.amount.equals("60.90") && !orderRepo.findByTransactionId(o.getId()).isPresent())
-                .collect(Collectors.toList());
-        System.out.println(orders);
-//        orders.stream().parallel().forEach(o -> {
-//            orderRepo.findByTransactionId(o.getId()).ifPresent(order -> {
-//
-//            });
-//        });
-        for (RefundOrder order : orders) {
-            String refundId = new SnowflakeIdWorker(0, 0).nextId() + "";
-            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/refund3.xlsx", RefundOrder.class).sheet("sheet")
-                .registerConverter(new LocalDateConverter())
-                .registerConverter(new LocalDateTimeConverter())
-                .registerConverter(new BigIntegerConverter())
-                .doWrite(orders);
-    }
-
-    @Test
-    public void queryrefund1() 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"));
-            try {
-                if (((JSONArray) refund.get("refunds")).getJSONObject(0).getString("trans_status").equals("S")) {
-                    success.add(order.getId());
-                } else {
-                    fail.add(order.getId());
-                }
-            } 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, ","));
-    }
-
-    @Test
-    public void queryOrder() throws BaseAdaPayException {
+    public void queryList() throws BaseAdaPayException {
         Map<String, Object> paymentParams = new HashMap<>();
-        paymentParams.put("app_id", "app_0e8d3acb-3d95-4ebb-8445-e470c378a787");
+        paymentParams.put("app_id", appId);
         paymentParams.put("page_index", "1");
-        paymentParams.put("page_size", "20");
-        paymentParams.put("payment_id", "002112021122415003310320348180126400512");
+        paymentParams.put("page_size", "10");
+        paymentParams.put("order_no", "928303191131422720");
         Map<String, Object> paymentList = Payment.queryList(paymentParams);
-        System.out.println(JSON.toJSONString(paymentList, SerializerFeature.PrettyFormat));
-    }
-
-    @Test
-    public void refunds() throws BaseAdaPayException {
-        Map<String, Object> refundParams = new HashMap<>();
-        refundParams.put("refund_order_no", "923969330914263040");
-        Map<String, Object> refund = Refund.query(refundParams);
-
-        Map<String, Object> refundParams1 = new HashMap<>();
-        refundParams1.put("refund_amt", "0.01");
-        refundParams1.put("refund_order_no", "923969330914263040");
-        Map<String, Object> response = Refund.create("002112021122415001010320348080327135232", refundParams1);
+        System.out.println(paymentList);
     }
 }