xiongzhu před 4 roky
rodič
revize
1818247f56

+ 6 - 0
pics2pic.sql

@@ -0,0 +1,6 @@
+alter table collection_info drop column pic;
+alter table collection_info change pics pic text null;
+
+
+alter table blind_box_item drop column pic;
+alter table blind_box_item change pics pic text null;

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

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

+ 35 - 1
src/main/java/com/izouma/nineth/domain/Privilege.java

@@ -1,18 +1,52 @@
 package com.izouma.nineth.domain;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import java.time.LocalDateTime;
+import java.util.List;
 
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
-public class Privilege extends PrivilegeOption {
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}, ignoreUnknown = true)
+public class Privilege {
 
+    @ApiModelProperty("特权名称")
+    private String name;
+
+    @ApiModelProperty("特权描述")
+    private String description;
+
+    @ApiModelProperty("简介")
+    private String intro;
+
+    @ApiModelProperty("详情")
+    private String detail;
+
+    @ApiModelProperty("类型")
+    private String type;
+
+    @ApiModelProperty("图标")
+    private List<String> icon;
+
+    @ApiModelProperty("已开启")
     private boolean opened;
 
+    @ApiModelProperty("开启时间")
     private LocalDateTime openTime;
 
+    @ApiModelProperty("只能打开一次")
+    private boolean once;
+
+    @ApiModelProperty("每次交易刷新")
+    private boolean refreshOnTransfer;
+
+    @ApiModelProperty("结束时间")
+    private LocalDateTime endTime;
 }

+ 20 - 1
src/main/java/com/izouma/nineth/domain/PrivilegeOption.java

@@ -1,10 +1,14 @@
 package com.izouma.nineth.domain;
 
+import com.izouma.nineth.converter.StringArrayConverter;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import javax.persistence.Convert;
 import javax.persistence.Entity;
+import java.time.LocalDateTime;
+import java.util.List;
 
 @Data
 @Entity
@@ -16,5 +20,20 @@ public class PrivilegeOption extends BaseEntity {
 
     private String description;
 
-    private String icon;
+    private String intro;
+
+    private String detail;
+
+    private String type;
+
+    @Convert(converter = StringArrayConverter.class)
+    private List<String> icon;
+
+    private boolean opened;
+
+    private LocalDateTime openTime;
+
+    private boolean once;
+
+    private boolean refreshOnTransfer;
 }

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

@@ -22,6 +22,7 @@ import com.izouma.nineth.service.storage.StorageService;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.ObjUtils;
 import com.izouma.nineth.utils.SecurityUtils;
+import io.jsonwebtoken.Jwts;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -341,9 +342,12 @@ public class UserService {
         return new PageImpl<>(userDTOS, users.getPageable(), users.getTotalElements());
     }
 
-    public void setTradeCode(Long userId, String code, String tradeCode) {
+    public void setTradeCode(Long userId, String token, String tradeCode) {
+        String phone = smsService.verifyToken(token);
         User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
-        smsService.verify(user.getPhone(), code);
+        if (!StringUtils.equals(phone, user.getPhone())) {
+            throw new BusinessException("验证码无效");
+        }
         user.setTradeCode(new BCryptPasswordEncoder().encode(tradeCode));
         userRepo.save(user);
     }

+ 1 - 2
src/main/java/com/izouma/nineth/service/sms/AliSmsService.java

@@ -99,9 +99,8 @@ public class AliSmsService implements SmsService {
                 .setSubject(phone)
                 .setIssuedAt(new Date())
                 .setExpiration(new Date(new Date().getTime() + 10 * 60 * 1000)) //10min
-                .signWith(SignatureAlgorithm.HS512, "jwtConfig.getSecret()")
+                .signWith(SignatureAlgorithm.HS512, Constants.SMS_TOKEN_SECRET)
                 .compact();
     }
 
-
 }

+ 21 - 0
src/main/java/com/izouma/nineth/service/sms/SmsService.java

@@ -1,5 +1,11 @@
 package com.izouma.nineth.service.sms;
 
+import com.izouma.nineth.config.Constants;
+import com.izouma.nineth.exception.BusinessException;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+
+import java.util.Date;
 import java.util.function.Supplier;
 
 public interface SmsService {
@@ -7,6 +13,21 @@ public interface SmsService {
 
     String verify(String phone, String code);
 
+    default String verifyToken(String token) {
+        try {
+            Claims claims = Jwts.parser()
+                    .setSigningKey(Constants.SMS_TOKEN_SECRET)
+                    .parseClaimsJws(token)
+                    .getBody();
+            if (claims.getExpiration().before(new Date())) {
+                throw new BusinessException("验证码已过期");
+            }
+            return claims.getSubject();
+        } catch (Exception e) {
+            throw new BusinessException("验证码无效");
+        }
+    }
+
     class SmsVerifyException extends Exception implements Supplier<SmsVerifyException> {
         public SmsVerifyException(String msg) {
             super(msg);

+ 2 - 2
src/main/java/com/izouma/nineth/web/SmsController.java

@@ -26,7 +26,7 @@ public class SmsController {
     }
 
     @GetMapping("/verify")
-    public void verify(@RequestParam String phone, @RequestParam String code) throws SmsService.SmsVerifyException {
-        smsService.verify(phone, code);
+    public String verify(@RequestParam String phone, @RequestParam String code) throws SmsService.SmsVerifyException {
+        return smsService.verify(phone, code);
     }
 }

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

@@ -173,8 +173,8 @@ public class UserController extends BaseController {
     }
 
     @PostMapping("/setTradeCode")
-    public void setTradeCode(@RequestParam String code, @RequestParam String tradeCode) {
-        userService.setTradeCode(SecurityUtils.getAuthenticatedUser().getId(), code, tradeCode);
+    public void setTradeCode(@RequestParam String token, @RequestParam String tradeCode) {
+        userService.setTradeCode(SecurityUtils.getAuthenticatedUser().getId(), token, tradeCode);
     }
 
     @PostMapping("/verifyTradeCode")

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 1
src/main/resources/genjson/PrivilegeOption.json


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

@@ -314,6 +314,22 @@ const router = new Router({
                     meta: {
                        title: '特权配置',
                     },
+               },
+                {
+                    path: '/privilegeOptionEdit',
+                    name: 'PrivilegeOptionEdit',
+                    component: () => import(/* webpackChunkName: "privilegeOptionEdit" */ '@/views/PrivilegeOptionEdit.vue'),
+                    meta: {
+                       title: '特权配置编辑',
+                    },
+                },
+                {
+                    path: '/privilegeOptionList',
+                    name: 'PrivilegeOptionList',
+                    component: () => import(/* webpackChunkName: "privilegeOptionList" */ '@/views/PrivilegeOptionList.vue'),
+                    meta: {
+                       title: '特权配置',
+                    },
                }
                 /**INSERT_LOCATION**/
             ]

+ 93 - 20
src/main/vue/src/views/CollectionEdit.vue

@@ -81,22 +81,31 @@
                             </el-table-column>
                         </el-table>
                     </el-form-item>
-                    <!-- <el-form-item label="特权" prop="privileges" style="width: calc(100vw - 450px)">
-                        <el-table :data="formData.privileges">
-                            <el-table-column></el-table-column>
-                        </el-table>
+                    <el-form-item label="特权" prop="privileges" style="width: calc(100vw - 450px)">
                         <el-table :data="privilegeOptions">
                             <el-table-column prop="name" label="可选特权" width="150"></el-table-column>
                             <el-table-column prop="description"></el-table-column>
-                            <el-table-column width="85" align="center">
+                            <el-table-column width="155" align="right">
                                 <template v-slot="{ row }">
-                                    <el-button size="mini" :disabled="!!row.added">
-                                        {{ row.added ? '已添加' : '添加' }}
+                                    <el-button size="mini" v-if="!row.added" @click="addPrivilege(row)">
+                                        添加
+                                    </el-button>
+                                    <el-button size="mini" v-if="!!row.added" plain @click="editPrivilege(row)">
+                                        编辑
+                                    </el-button>
+                                    <el-button
+                                        size="mini"
+                                        v-if="!!row.added"
+                                        type="danger"
+                                        plain
+                                        @click="delPrivilege(row)"
+                                    >
+                                        删除
                                     </el-button>
                                 </template>
                             </el-table-column>
                         </el-table>
-                    </el-form-item> -->
+                    </el-form-item>
                     <el-form-item>
                         <el-button size="mini" @click="addProperty" :disabled="!canEdit"> 添加特性 </el-button>
                     </el-form-item>
@@ -177,26 +186,61 @@
                 </el-form>
             </div>
         </div>
+
+        <el-dialog :visible.sync="showPrivilegeEditDialog" width="600px" :title="privilegeForm.name">
+            <el-form ref="privilegeForm" :model="privilegeForm" label-position="right" label-width="80px">
+                <el-form-item
+                    prop="name"
+                    label="详细内容"
+                    v-if="privilegeForm.type === 'text' || privilegeForm.type === 'exchange'"
+                >
+                    <el-input type="textarea" :autosize="{ minRows: 3 }" v-model="privilegeForm.detail"></el-input>
+                </el-form-item>
+                <el-form-item prop="name" label="二维码" v-if="privilegeForm.type === 'qrcode'">
+                    <single-upload v-model="privilegeForm.detail"></single-upload>
+                </el-form-item>
+                <el-form-item prop="intro" label="说明" v-if="privilegeForm.type === 'qrcode'">
+                    <el-input type="textarea" :autosize="{ minRows: 3 }" v-model="privilegeForm.intro"></el-input>
+                </el-form-item>
+            </el-form>
+            <div slot="footer">
+                <el-button @click="showPrivilegeEditDialog = false">取消</el-button>
+                <el-button type="primary" @click="savePrivilege">保存</el-button>
+            </div>
+        </el-dialog>
     </div>
 </template>
 <script>
 export default {
     name: 'CollectionEdit',
     created() {
-        if (this.$route.query.id) {
+        Promise.all([
+            new Promise((resolve, reject) => {
+                if (this.$route.query.id) {
+                    return this.$http
+                        .get('collection/get/' + this.$route.query.id)
+                        .then(res => {
+                            res.properties = res.properties || [];
+                            res.privileges = res.privileges || [];
+                            this.formData = res;
+                            resolve();
+                        })
+                        .catch(e => {
+                            console.log(e);
+                            this.$message.error(e.error);
+                            resolve();
+                        });
+                }
+                return resolve();
+            }),
             this.$http
-                .get('collection/get/' + this.$route.query.id)
+                .post('/privilegeOption/all', { size: 10000, query: { del: false } }, { body: 'json' })
                 .then(res => {
-                    res.properties = res.properties || [];
-                    this.formData = res;
+                    this.privilegeOptions = res.content;
+                    return Promise.resolve();
                 })
-                .catch(e => {
-                    console.log(e);
-                    this.$message.error(e.error);
-                });
-        }
-        this.$http.post('/privilegeOption/all', { size: 10000, query: { del: false } }, { body: 'json' }).then(res => {
-            this.privilegeOptions = res.content;
+        ]).then(() => {
+            console.log(this.formData, this.privilegeOptions);
         });
     },
     computed: {
@@ -352,7 +396,9 @@ export default {
                 { label: '转让', value: 'TRANSFER' }
             ],
             cateogories: ['收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他'],
-            privilegeOptions: []
+            privilegeOptions: [],
+            showPrivilegeEditDialog: false,
+            privilegeForm: {}
         };
     },
     methods: {
@@ -411,6 +457,33 @@ export default {
         },
         delProperty(i) {
             this.formData.properties.splice(i, 1);
+        },
+        addPrivilege(row) {
+            this.privilegeForm = { ...row };
+            this.showPrivilegeEditDialog = true;
+        },
+        editPrivilege(row) {
+            this.privilegeForm = { ...(this.formData.privileges.find(i => i.name === row.name) || {}) };
+            this.showPrivilegeEditDialog = true;
+        },
+        savePrivilege() {
+            this.$refs.privilegeForm
+                .validate()
+                .then(() => {
+                    let i = this.formData.privileges.findIndex(e => e.name === this.privilegeForm.name);
+                    if (i > -1) {
+                        this.$set(this.formData.privileges, i, { ...this.privilegeForm });
+                    } else {
+                        this.formData.privileges.push({ ...this.privilegeForm });
+                    }
+                    let ii = this.privilegeOptions.findIndex(i => i.name === this.privilegeForm.name);
+                    console.log(ii);
+                    this.$set(this.privilegeOptions[ii], 'added', true);
+                    this.showPrivilegeEditDialog = false;
+                })
+                .catch(e => {
+                    console.log(e);
+                });
         }
     }
 };

+ 156 - 94
src/main/vue/src/views/PrivilegeOptionEdit.vue

@@ -2,29 +2,48 @@
     <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="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="52px" label-position="right"
-                         size="small"
-                         style="max-width: 500px;">
-                        <el-form-item prop="name" label="名称">
-                                    <el-input v-model="formData.name"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="description" label="描述">
-                                    <el-input v-model="formData.description"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="icon" label="图标">
-                                    <single-upload v-model="formData.icon"></single-upload>
-                        </el-form-item>
+                <el-form
+                    :model="formData"
+                    :rules="rules"
+                    ref="form"
+                    label-width="94px"
+                    label-position="right"
+                    size="small"
+                    style="max-width: 500px"
+                >
+                    <el-form-item prop="name" label="名称">
+                        <el-input v-model="formData.name"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="description" label="描述">
+                        <el-input v-model="formData.description"></el-input>
+                    </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 prop="icon" label="图标">
+                        <multi-upload v-model="formData.icon"></multi-upload>
+                    </el-form-item>
+                    <el-form-item prop="once" label="一次性">
+                        <el-switch v-model="formData.once"></el-switch>
+                    </el-form-item>
+                    <el-form-item prop="refreshOnTransfer" label="交易后刷新">
+                        <el-switch v-model="formData.refreshOnTransfer"></el-switch>
+                    </el-form-item>
                     <el-form-item class="form-submit">
-                        <el-button @click="onSave" :loading="saving" type="primary">
-                            保存
-                        </el-button>
+                        <el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
                         <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
                             删除
                         </el-button>
@@ -36,92 +55,135 @@
     </div>
 </template>
 <script>
-    export default {
-        name: 'PrivilegeOptionEdit',
-        created() {
-            if (this.$route.query.id) {
-                this.$http
-                    .get('privilegeOption/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: {
-                    name: [
-                        {
-                            required: true,
-                            message: '请输入名称',
-                            trigger: 'blur'
-                        },
-                    ],
-                    description: [
-                        {
-                            required: true,
-                            message: '请输入描述',
-                            trigger: 'blur'
-                        },
-                    ],
-                    icon: [
-                        {
-                            required: true,
-                            message: '请输入图标',
-                            trigger: 'blur'
-                        },
-                    ],
-                },
-            }
-        },
-        methods: {
-            onSave() {
-                this.$refs.form.validate((valid) => {
-                    if (valid) {
-                        this.submit();
-                    } else {
-                        return false;
-                    }
+export default {
+    name: 'PrivilegeOptionEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('privilegeOption/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: { once: false, refreshOnTransfer: false },
+            rules: {
+                name: [
+                    {
+                        required: true,
+                        message: '请输入名称',
+                        trigger: 'blur'
+                    }
+                ],
+                description: [
+                    {
+                        required: true,
+                        message: '请输入描述',
+                        trigger: 'blur'
+                    }
+                ],
+                intro: [
+                    {
+                        required: true,
+                        message: '请输入说明',
+                        trigger: 'blur'
+                    }
+                ],
+                detail: [
+                    {
+                        required: true,
+                        message: '请输入详情',
+                        trigger: 'blur'
+                    }
+                ],
+                type: [
+                    {
+                        required: true,
+                        message: '请输入类型',
+                        trigger: 'blur'
+                    }
+                ],
+                icon: [
+                    {
+                        required: true,
+                        message: '请输入图标',
+                        trigger: 'blur'
+                    }
+                ],
+                once: [
+                    {
+                        required: true,
+                        message: '请输入一次性',
+                        trigger: 'blur'
+                    }
+                ],
+                refreshOnTransfer: [
+                    {
+                        required: true,
+                        message: '请输入交易后刷新',
+                        trigger: 'blur'
+                    }
+                ]
             },
-            submit() {
-                let data = {...this.formData};
+            typeOptions: [
+                { label: '文本', value: 'text' },
+                { label: '二维码', value: 'qrcode' },
+                { label: '验证码', value: 'code' },
+                { label: '铸造', value: 'exchange' }
+            ]
+        };
+    },
+    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('/privilegeOption/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(`/privilegeOption/del/${this.formData.id}`)
-                }).then(() => {
+            this.saving = true;
+            this.$http
+                .post('/privilegeOption/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(`/privilegeOption/del/${this.formData.id}`);
+                })
+                .then(() => {
                     this.$message.success('删除成功');
                     this.$router.go(-1);
-                }).catch(e => {
+                })
+                .catch(e => {
                     if (e !== 'cancel') {
                         console.log(e);
                         this.$message.error((e || {}).error || '删除失败');
                     }
-                })
-            },
+                });
         }
     }
+};
 </script>
 <style lang="less" scoped></style>

+ 150 - 123
src/main/vue/src/views/PrivilegeOptionList.vue

@@ -1,54 +1,64 @@
 <template>
-    <div  class="list-view">
+    <div class="list-view">
         <page-title>
-            <el-button @click="addRow" type="primary" icon="el-icon-plus" :disabled="fetchingData || downloading" class="filter-item">
+            <el-button
+                @click="addRow"
+                type="primary"
+                icon="el-icon-plus"
+                :disabled="fetchingData || downloading"
+                class="filter-item"
+            >
                 新增
             </el-button>
-            <el-button @click="download" icon="el-icon-upload2" :loading="downloading" :disabled="fetchingData" class="filter-item">
+            <el-button
+                @click="download"
+                icon="el-icon-upload2"
+                :loading="downloading"
+                :disabled="fetchingData"
+                class="filter-item"
+            >
                 导出
             </el-button>
         </page-title>
         <div class="filters-container">
             <el-input
-                    placeholder="搜索..."
-                    v-model="search"
-                    clearable
-                    class="filter-item search"
-                    @keyup.enter.native="getData"
+                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
+            :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="name" label="名称"> </el-table-column>
+            <el-table-column prop="description" label="描述"> </el-table-column>
+            <el-table-column prop="type" label="类型" :formatter="typeFormatter"> </el-table-column>
+            <el-table-column prop="icon" label="图标">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.icon[0]"
+                        fit="cover"
+                        :preview-src-list="row.icon"
+                    ></el-image>
+                </template>
             </el-table-column>
-                                <el-table-column prop="name" label="名称"
->
-                    </el-table-column>
-                    <el-table-column prop="description" label="描述"
->
-                    </el-table-column>
-                    <el-table-column prop="icon" label="图标"
->
-                            <template slot-scope="{row}">
-                                <el-image style="width: 30px; height: 30px"
-                                          :src="row.icon" fit="cover"
-                                          :preview-src-list="[row.icon]"></el-image>
-                            </template>
-                    </el-table-column>
-            <el-table-column
-                    label="操作"
-                    align="center"
-                    fixed="right"
-                    width="150">
-                <template slot-scope="{row}">
+            <el-table-column label="操作" align="center" fixed="right" width="150">
+                <template slot-scope="{ row }">
                     <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
                     <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
                 </template>
@@ -63,112 +73,129 @@
                     <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
+                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";
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
 
-    export default {
-        name: 'PrivilegeOptionList',
-        mixins: [pageableTable],
-        data() {
-            return {
-                multipleMode: false,
-                search: "",
-                url: "/privilegeOption/all",
-                downloading: false,
+export default {
+    name: 'PrivilegeOptionList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/privilegeOption/all',
+            downloading: false,
+            typeOptions: [
+                { label: '文本', value: 'text' },
+                { label: '二维码', value: 'qrcode' },
+                { label: '验证码', value: 'code' },
+                { label: '铸造', value: 'exchange' }
+            ]
+        };
+    },
+    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 '';
         },
-        computed: {
-            selection() {
-                return this.$refs.table.selection.map(i => i.id);
+        beforeGetData() {
+            return { search: this.search, query: { del: false } };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
             }
         },
-        methods: {
-            beforeGetData() {
-                return { search: this.search, query: { del: false } };
-            },
-            toggleMultipleMode(multipleMode) {
-                this.multipleMode = multipleMode;
-                if (!multipleMode) {
-                    this.$refs.table.clearSelection();
+        addRow() {
+            this.$router.push({
+                path: '/privilegeOptionEdit',
+                query: {
+                    ...this.$route.query
                 }
-            },
-            addRow() {
-                this.$router.push({
-                    path: "/privilegeOptionEdit",
-                    query: {
-                        ...this.$route.query
-                    }
-                });
-            },
-            editRow(row) {
-                this.$router.push({
-                    path: "/privilegeOptionEdit",
-                    query: {
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/privilegeOptionEdit',
+                query: {
                     id: row.id
-                    }
-                });
-            },
-            download() {
-                this.downloading = true;
-                this.$axios
-                    .get("/privilegeOption/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
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/privilegeOption/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);
                 });
-            },
-            operation2() {
-                this.$message('操作2');
-            },
-            deleteRow(row) {
-                this.$alert('删除将无法恢复,确认要删除么?', '警告', {type: 'error'}).then(() => {
-                    return this.$http.post(`/privilegeOption/del/${row.id}`)
-                }).then(() => {
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/privilegeOption/del/${row.id}`);
+                })
+                .then(() => {
                     this.$message.success('删除成功');
                     this.getData();
-                }).catch(e => {
+                })
+                .catch(e => {
                     if (e !== 'cancel') {
                         this.$message.error(e.error);
                     }
-                })
-            },
+                });
         }
     }
+};
 </script>
 <style lang="less" scoped>
 </style>

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

@@ -1,6 +1,7 @@
 package com.izouma.nineth;
 
 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;
@@ -13,6 +14,9 @@ import io.ipfs.api.MerkleNode;
 import io.ipfs.api.NamedStreamable;
 import io.ipfs.cid.Cid;
 import io.ipfs.multihash.Multihash;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
 import lombok.SneakyThrows;
 import net.coobird.thumbnailator.Thumbnails;
 import net.coobird.thumbnailator.name.Rename;
@@ -346,4 +350,22 @@ public class CommonTest {
                 .outputQuality(0.6)
                 .toFile("/Users/drew/Desktop/2.jpg");
     }
+
+    @Test
+    public void testJwt() {
+        String token = Jwts.builder()
+                .setClaims(new HashMap<>())
+                .setSubject("15077886171")
+                .setIssuedAt(new Date())
+                .setExpiration(new Date(new Date().getTime() + 10 * 60 * 1000)) //10min
+                .signWith(SignatureAlgorithm.HS512, Constants.SMS_TOKEN_SECRET)
+                .compact();
+        System.out.println(token);
+        Claims claims = Jwts.parser()
+                .setSigningKey(Constants.SMS_TOKEN_SECRET)
+                .parseClaimsJws(token)
+                .getBody();
+        System.out.println(claims.getSubject());
+        System.out.println(claims.getExpiration());
+    }
 }

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

@@ -0,0 +1,23 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.ApplicationTests;
+import com.izouma.nineth.domain.Collection;
+import com.izouma.nineth.repo.CollectionRepo;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CollectionServiceTest extends ApplicationTests {
+
+    @Autowired
+    private CollectionService collectionService;
+    @Autowired
+    private CollectionRepo    collectionRepo;
+
+    @Test
+    void toDTO() {
+        Collection collection = collectionRepo.findById(951L).get();
+        assert collection.getPic() != null;
+    }
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů