# 後台重構 - 以設定檔自動化生成第三方遊戲欄位
最近公司的需求變得沒有這麼急迫,可以花點時間還債了 QQ
我們的產品主要有彩票和第三方遊戲-
彩票泛指像是**六合彩、七星彩、飛艇**等常見的博弈遊戲;
第三方遊戲則是介接第三方廠商的 API 服務在前台用 iframe 打請求拿取 URL 遊玩的遊戲,像是**棋牌、視訊、電子、捕魚、體育**都是我們有提供的遊戲類別。
~~沒錯,我都跟朋友戲稱我是負責串接接性感荷官在線發牌的前端工程師。~~

我認真思考了一下後台在快速迭代下設計上的缺失,最主要會遇到的麻煩是**每次添加新的第三方遊戲類別都要浪費一堆時間找到組件補上欄位、新增路由**,還常常會東漏西漏,讓我改到快發瘋。
### 自動化
想到了一個非常簡單暴力的解法:
何不嘗試建立一個**設定檔**直接讓所有組件、路由、選項都依賴此設定檔生成就好?

我一直有在思考是不是可以這樣做,但是苦於新創迭代需求太頻繁害我來不及擦屁股 QQ,既然今天終於抽出時間讓我重構了,那就開始著手嘗試吧!
```javascript
// @/config/external-groups.js
export default [
{ id: 1, name: '棋牌', code: 'poker' },
{ id: 2, name: '視訊', code: 'live' },
{ id: 3, name: '電子', code: 'slot' },
{ id: 4, name: '捕魚', code: 'fish' },
{ id: 5, name: '體育', code: 'sport' },
]
```
對,就是這麼樸實無華, KISS 對每個人都好,把要用到的 attr 用陣列包一包即可。
以下是各 attr 的實際用途:
- id - 生成各遊戲類別之注單的預設 query (ex: ?group=1)
- name - 生成各遊戲類別的欄位 label 跟選項的 label
- code - 生成各遊戲類別的欄位 attr 和選項的 value
分析一下以這個方式重構帶來的利弊:
- 好處-生成遊戲類別一律只要改動設定檔就好,**節省大量時間**,且類別排序能完全一致。
- 壞處-程式碼**可讀性變差了一點**,新人可能需要追 code 搞懂我搞了什麼花樣。
覺得能省掉至少半天到一天的時程這點還蠻划算的,且後端的欄位 key 都有一致讓我很溫馨,於是開始動工!
後台專案目前是以 Vue2, Vue Router 和 element-ui 為技術棧,下面的例子會以這些技術為基礎做講解。
### Router
接下來就是一路無聊的 for-loop 到底。
感謝 ES6 的 **展開運算符**,我們可以依賴設定檔自動生成注單路由。
```javascript
// @/router.js
import Router from 'vue-router'
import EXTERNAL_GROUPS from '@/config/external-groups'
export default new Router({
routes: [
// 其他路由
...EXTERNAL_GROUPS.map((group) => ({
path: `/report/${group.code}-bet-record`,
name: `report-${group.code}-bet-record`,
component: () => import('./views/Report/ExternalBetRecord'),
meta: {
title: `${group.name}注单`, // 頁面標題
providerCategory: group.id, // 注單頁 API 的 default query
// 其他 meta key 如權限、頁面類別...等ㄎ
},
})),
// 其他路由
],
})
```
### Options
要讓 html template 渲染記得把設定檔放進 data 裡。
```htmlembedded
<html>
<el-select v-model="query.type" placeholder="全部">
<el-option label="全部" value="" />
<el-option label="彩票佣金收入" value="lottery_commission" />
<el-option
v-for="type in EXTERNAL_GROUPS"
:key="`external-${ type.code }-commission`"
:label="`${ type.name }佣金收入`"
:value="`${ type.code }_commission`"
/>
<el-option label="佣金取款" value="withdraw" />
<el-option label="佣金提取" value="transfer" />
</el-select>
</html>
```
### Table
```htmlembedded
<html>
<el-table-column
v-for="type in EXTERNAL_GROUPS"
:key="`external-${ type.code }-profit-and-amount`"
:label="type.name"
align="right"
v-slot="{ row }"
>
<p
:class="profitColor(row[`${ type.code }_profit`])"
@click="routerGo(row.time, `${ type.code }_record`)"
>
{{ row[`${ type.code }_profit`] | currency('¥') }}
</p>
<p>{{ row[`${ type.code }_amount`] | currency('¥') }}</p>
</el-table-column>
</html>
```
### Filters
之前用 filters 寫 switch-case 的部分打掉改用 methods 由 map 去找對應值。
```javascript
// @/views/Agent/CommissionRecord.vue
import EXTERNAL_GROUPS from '@/config/external-groups'
export default {
name: 'CommissionRecord',
data() {
return {
typeMap: {
withdraw: '佣金取款',
transfer: '佣金提取',
profitspace_commission: '彩票佣金收入',
revenue_commission: '彩票佣金收入',
...EXTERNAL_GROUPS.reduce((map, type) => {
map[`${type.code}_commission`] = `${type.name}佣金收入`
return map
}, {}),
},
}
},
methods: {
formatType(value) {
return this.typeMap[value] || ''
},
},
}
```
大功告成,同事以後加第三方的遊戲類別時應該會很舒爽。
不過因為這包專案沒 i18n 所以才這麼輕鬆,之後還要煩惱怎麼自動加 i18n 跟客製化注單的欄位 (不是每個欄位都這麼簡單跑個 for-loop 就能輕鬆搞定的 QQ)
###### tags: `Refactor` `JavaScript` `Vue` `Vue Router` `element-ui`