摘要:為使客服同學(xué)有序協(xié)同處理客戶問題客服技術(shù)團(tuán)隊(duì)打造了多渠道整合的,可靈活配置的,便于流轉(zhuǎn)的行星工單系統(tǒng)。所以根據(jù)這個處理思路,我們建立工單系統(tǒng)的流程控制方案。據(jù)此,我們用類管道函數(shù)實(shí)現(xiàn)了簡單的流程控制。行為傳參通過標(biāo)志指定,僅能指定單行為。
1.工單系統(tǒng)的表格頁
工單系統(tǒng)是一種網(wǎng)絡(luò)軟件系統(tǒng),根據(jù)不同組織,部門和外部客戶的需求,來由針對的管理,維護(hù)和追蹤一系列的問題和請求。大多用于記錄、處理、跟蹤一項(xiàng)工作的完成情況。
為使客服同學(xué)有序、協(xié)同處理客戶問題,客服技術(shù)團(tuán)隊(duì)打造了多渠道整合的,可靈活配置的,便于流轉(zhuǎn)的行星工單系統(tǒng)。
由于工單系統(tǒng)從屬后臺系統(tǒng),頁面展示基本以如下表格頁為主.為統(tǒng)一表格頁行為,故將其統(tǒng)一封裝成表格頁組件.
表格頁組件分為如下幾塊:
篩選項(xiàng)區(qū)域
表格主體
分頁組件
操作按鈕
行內(nèi)操作鏈接
其中分頁組件可通過向表格頁組件傳入標(biāo)志來控制其顯示狀態(tài)、篩選項(xiàng)區(qū)域及表格主體內(nèi)容可通過向表格頁組件傳入表格列配置及表單項(xiàng)配置實(shí)現(xiàn)不同頁面間各異的邏輯。
但由于操作按鈕、行內(nèi)操作鏈接的邏輯各頁基本不同,無法將其封入表格頁組件。因此采用事件處理這部分邏輯從列表頁組件中解耦,我們將不同表格頁按鈕操作名稱作為唯一標(biāo)識,在入口文件中將系統(tǒng)全量事件集注冊到全局,將標(biāo)識與事件集中事件建立起對應(yīng)關(guān)系,以此實(shí)現(xiàn)上述需求。
然后我們發(fā)現(xiàn),這些操作按鈕的邏輯,基本為以下幾類行為的聚合:
彈出提示框讓用戶操作
彈出表單類組件獲取一部分?jǐn)?shù)據(jù)
表單及其他數(shù)據(jù)校驗(yàn)
調(diào)用接口去拉取/更新數(shù)據(jù)
而且比較重要的一點(diǎn)是,它們都是串行的。如編輯用戶的操作:
從接口拉取用戶數(shù)據(jù)
彈出表單組件將數(shù)據(jù)填入同時供用戶修改
校驗(yàn)用戶提交數(shù)據(jù)
調(diào)用接口提交數(shù)據(jù)。
這幾步操作中,如果流程中某一節(jié)點(diǎn)的行為出現(xiàn)問題,比較合理的處理方案是截停流程,并拋出一個錯誤響應(yīng)給開發(fā)者或用戶。所以根據(jù)這個處理思路,我們建立工單系統(tǒng)的流程控制方案。
2.簡單的管道論及串行執(zhí)行流程行為,并將當(dāng)前行為的執(zhí)行結(jié)果作為下一行為的參數(shù)的操作,我們首先想到的是vue及angular中的管道。下面我們用Array.reduce去實(shí)現(xiàn)一個簡單的管道.
const grep = ({srcData, fnList, context}) => fnList.reduce((dist, currFn) => currFn.call(context, dist), srcData)
此函數(shù)邏輯很簡單,即遍歷行為集,取各項(xiàng)行為執(zhí)行,再借用reduce特性將上一行為結(jié)果傳入下一行為。據(jù)此,我們用類管道函數(shù)實(shí)現(xiàn)了簡單的流程控制。
如下調(diào)用:
function filterLessThanFive(srcData){ //[1,3,4,6,7,8] => [1,3,4] return srcData.filter(item => Number.isInteger(item) ? item > 5 : false) } function sum(srcData){ // [1,3,4] => 1+3+4 => 8 return srcData.reduce((dist,item) => dist+(parseInt(item, 10)), 0) } function plusOne(srcData){ // 8+1 => 9 return srcData+1 } const fnList = [filterLessThanFive, sum, plusOne] const srcData = [1,3,4,6,7,8] const res = grep({context:this, fnList, srcData})
注意:
1.由于grep內(nèi)部強(qiáng)制更改fnList的上下文,故fnList內(nèi)部函數(shù)不可用箭頭函數(shù)及bind進(jìn)行上下文綁定
2.為了靈活適應(yīng)需求,不強(qiáng)制規(guī)范流轉(zhuǎn)數(shù)據(jù)格式,或造成多次遍歷等性能浪費(fèi),由于前端數(shù)據(jù)量較小,故不考慮性能問題.
3.流程控制但是管道有兩個不能解決的問題:
1.不能處理異步需求:如需求為接口返回結(jié)果之后,將結(jié)果作為下一個函數(shù)的參數(shù)。鑒于fnList(流程行為集)中如果存在異步函數(shù),那么異步流程行為函數(shù)在流程中不能流轉(zhuǎn)數(shù)據(jù)或僅能流轉(zhuǎn)promise對象嗎,無法完成上述需求。
2.當(dāng)前流程節(jié)點(diǎn)行為只能獲取上個流程節(jié)點(diǎn)行為執(zhí)行結(jié)果:我們需要更靈活的獲取之前行為結(jié)果的方式,如跨節(jié)點(diǎn)獲取、多節(jié)點(diǎn)獲取。
為解決以上兩點(diǎn)我們初步構(gòu)建事務(wù)流處理函數(shù)如下:
export async function actionFnSimple(actionList, eventMap) { const that = this // 獲取組件上下文 const defaultEventMap = { ...eventMapSrc, ...this }// 默認(rèn)行為集 const eventActionMap = eventMap || defaultEventMap const result = await actionList .reduce(async (cache, actionItem) => { const { actionKey, from = "" } = actionItem const ownParams = getRestObj(actionItem, ["actionKey", "from"]) const cacheReal = cache.then ? await cache : cache //初次非promise const { continueStatus: prevContinueStatus, resultCache: prevResultCache } = cacheReal //continueStatus繼續(xù)執(zhí)行標(biāo)識 resultCache各個行為運(yùn)行結(jié)果緩存 const paramsFromCache = prevResultCache[from] const params = paramsFromCache ? objDeepMerge(ownParams, paramsFromCache) : ownParams // 混合兩個源的參數(shù) const actionFn = eventActionMap[actionKey] let result = prevContinueStatus && await actionFn.call(that, params, stateObj, prevResultCache) // 顯式指定行為上下文為當(dāng)前頁面vue實(shí)例 result = result === undefined ? true : result const continueStatus = prevContinueStatus && !!result const resultCache = { ...prevResultCache, [actionKey]: result } return { continueStatus, resultCache } // 更新運(yùn)行標(biāo)識和緩存 }, { continueStatus: true, resultCache: resCache || {} }) }
此函數(shù)通過同類管道函數(shù)grep一樣,借助Array.reduce遍歷傳入的配置數(shù)組(actionList),從每項(xiàng)配置(actionItem)中取得行為標(biāo)識(actionKey)后,從通用行為庫(eventMap)和上下文中獲取到當(dāng)前行為(actionFn)去執(zhí)行當(dāng)前行為。
不同之處在于,grep僅流轉(zhuǎn)操作后的數(shù)據(jù).而此函數(shù)通過流轉(zhuǎn):
1.繼續(xù)執(zhí)行標(biāo)志 (continueStatus),并在執(zhí)行當(dāng)前行為(actionFn)前校驗(yàn)此標(biāo)志,來實(shí)現(xiàn)出現(xiàn)異常后停止后續(xù)行為的操作。
2.全部行為返回的結(jié)果的緩存 (resultCache),并結(jié)合從每項(xiàng)配置(actionItem)中獲取參數(shù)來源(from)來靈活指派其傳參,使當(dāng)前行為的傳參不拘泥于僅為上一行為執(zhí)行結(jié)果。
另外對于當(dāng)前事務(wù)為異步的情況,我們利用async的特性(1.表象為同步處理 2.返回promise ),并將其用到reduce處理函數(shù)中。最終將reduce中每項(xiàng)的處理函數(shù)揉為一個整體promise
現(xiàn)在我們的流程控制函數(shù)初具雛形,其調(diào)用方式如下:
function deleteUser(that, params = {}) { const { id, name } = params const config = [ { actionKey: "showConfirmModal", msg: `確認(rèn)刪除員工:【${name}】?` }, { actionKey: "simpleReq", url: "/user/transfer/removeUser", data: { userId: id }, otherOption: { errorMsg: "用戶刪除失敗!", succMsg: "用戶刪除成功!" } }, { actionKey: "initTable", }, ] actionFnSimple.bind(that, config) }
其中showConfirmModal,simpleReq取自通用行為集, initTable取自事件觸發(fā)組件的methods, 如下:
const eventActionMap = { // 截取部分通用行為集 showConfirmModal(params) { const { msg = "確認(rèn)執(zhí)行當(dāng)前操作?", title = "提醒" } = params return new Promise(resolve => { const onOk = () => {resolve(true)} const onCancel = () => {resolve(false)} Modal.confirm({ title, content: msg, onOk, onCancel }) }) }, async simpleReq(params) { let { url, data, resPath = [], otherOption } = params const res = await fetchApi(this, url, data, otherOption) const resData = getIn(res, resPath, {}) if (!res) return false return { data: resData } }, }4.更靈活的流程控制
雖然我們的流程控制函數(shù)可以處理簡單需求,但是將其應(yīng)用于處理復(fù)雜需求還需要解決以下幾點(diǎn)問題:
1.行為只能通過通用行為庫及組件上下文提供,過于單一。如map后端數(shù)據(jù)這種無需復(fù)用的行為存入行為庫較為不妥,又因其不屬于組件邏輯,故也不能放入組件中。因此需建立用戶手動指定行為(customFn)以解決此問題。
2.由于行為結(jié)果緩存以actionkey作為唯一標(biāo)識,流程中若引用多個通用行為會造成同名結(jié)果緩存被覆蓋,,故引入別名(alias)機(jī)制以區(qū)分引用多個通用行為結(jié)果緩存的問題。
2.行為傳參通過from標(biāo)志指定,,from僅能指定單行為。如果有從多處表單取值校驗(yàn)并請求接口的需求,則無法實(shí)現(xiàn)。一種解決方案是判斷from內(nèi)容,若為全量標(biāo)志(建議用symbol建議避免與actionKey沖突),則將各行為結(jié)果緩存(resultCache)傳入。但此解決方案會向行為傳入冗余參數(shù)。 另一種方案是允許from為數(shù)組,通過from數(shù)組中的actionkey去resultCache中取得所需行為的結(jié)果,,再傳入當(dāng)前行為。兩種方案皆可實(shí)現(xiàn)此需求,,鑒于第二種方案沒有冗余傳參,此處我們采用方案二。
據(jù)此我們得到流程控制函數(shù)如下:
export async function actionFnSimple(actionList, eventMap) { const that = this const defaultEventMap = { ...eventMapSrc, ...this } const eventActionMap = eventMap || defaultEventMap const result = await actionList .reduce(async (cache, actionItem) => { const { actionKey, from = "", customFn, alias } = actionItem const ownParams = getRestObj(actionItem, ["actionKey", "from", "customFn"]) const cacheReal = cache.then ? await cache : cache const { continueStatus: prevContinueStatus, resultCache: prevResultCache } = cacheReal const paramsFromCache = Array.isArray(from) ? from.reduce((dist, fromItem) => ({ ...dist, [fromItem]: prevResultCache[fromItem] }), {}) : prevResultCache[from] // 新增支持指定多個from const params = paramsFromCache ? objDeepMerge(ownParams, paramsFromCache) : ownParams const actionFn = isFunction(customFn) ? customFn : eventActionMap[actionKey] // 新增支持自定義行為 let result = prevContinueStatus && await actionFn.call(that, params, stateObj, prevResultCache) result = result === undefined ? true : result const continueStatus = prevContinueStatus && !!result const resultCache = { ...prevResultCache, [alias || actionKey]: result } // 新增別名機(jī)制 return { continueStatus, resultCache } }, { continueStatus: true, resultCache: resCache || {} }) }5.錯誤處理機(jī)制
我們的流程處理函數(shù)雖然可以處理復(fù)雜邏輯,且可以通過在流程節(jié)點(diǎn)的行為里返回false去截停流程。但在截停流程后,并未對用戶作出反饋。且當(dāng)行為產(chǎn)生異常時(邏輯錯誤返回false),并不能及時反饋給用戶,也并未在控制臺輸出日志, 這樣對用戶、開發(fā)者都不友好。
故當(dāng)流程節(jié)點(diǎn)行為返回false時,我們引入錯誤處理機(jī)制如下:
觸發(fā)此節(jié)點(diǎn)配置文件中的onError函數(shù)
向控制臺輸出錯誤日志
export async function actionFnSimple(actionList, eventMap) { const that = this const defaultEventMap = { ...eventMapSrc, ...this } const eventActionMap = eventMap || defaultEventMap const result = await actionList .reduce(async (cache, actionItem) => { const { actionKey, from = "", customFn, alias } = actionItem const ownParams = getRestObj(actionItem, ["actionKey", "from", "customFn"]) const cacheReal = cache.then ? await cache : cache const { continueStatus: prevContinueStatus, resultCache: prevResultCache } = cacheReal const paramsFromCache = Array.isArray(from) ? from.reduce((dist, fromItem) => ({ ...dist, [fromItem]: prevResultCache[fromItem] }), {}) : prevResultCache[from] const params = paramsFromCache ? objDeepMerge(ownParams, paramsFromCache) : ownParams const actionFn = isFunction(customFn) ? customFn : eventActionMap[actionKey] let result = prevContinueStatus && await actionFn.call(that, params, stateObj, prevResultCache) result = result === undefined ? true : result const isError = prevContinueStatus && !result if(isError && onError){ // 新增錯誤處理機(jī)制 const errorIsText = typeof onError === "string" const errorConsoleText = `CONTEXT_NAME: ${that.name};ACTION_NAME: ${alias||actionKey}` console.log("ACTION_FN_ERROR", errorConsoleText, params, result) if(onError){ errorIsText ? (()=>{that.$Message && that.$Message.error(onError)})() : onError() } } const continueStatus = prevContinueStatus && !!result const resultCache = { ...prevResultCache, [alias || actionKey]: result } return { continueStatus, resultCache } }, { continueStatus: true, resultCache: resCache || {} }) }6.TODO
鑒于繼續(xù)執(zhí)行標(biāo)志及行為結(jié)果緩存的共享特性, 可將流程處理函數(shù)主體作為方法封入class,繼續(xù)執(zhí)行標(biāo)志及行為結(jié)果緩存可作為class屬性來實(shí)現(xiàn)其共享。
流程處理函數(shù)核心通過promise流來實(shí)現(xiàn),可改用事件流的形式。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98485.html
摘要:下載數(shù)據(jù)方法使用官方的數(shù)據(jù)最新打包的中文文檔下載地址是。中文維基數(shù)據(jù)繁簡混雜大家都說存在這個問題,但的網(wǎng)站是將繁體中文和簡體中文分開處理的,所以一直覺得從數(shù)據(jù)庫到結(jié)構(gòu)都應(yīng)有方法將兩者區(qū)分開,暫罷,待有空研究其數(shù)據(jù)時再議。 照例,先講下環(huán)境,Mac OSX 10.11.2 ,Python 3.4.3。 下載數(shù)據(jù) 方法1:使用官方dump的xml數(shù)據(jù) 最新打包的中文文檔下載地址是:http...
摘要:作為業(yè)主的惘魍早上出門,坐著電梯下樓,單元樓下電瓶車電動車有序的在智能充電棚充電,看著社區(qū)里面的花草樹木整潔衛(wèi)生的環(huán)境,心情也舒暢起來。智慧社區(qū),未來可期,至簡可期 ? ? 作為業(yè)主的惘魍早上出門 ,坐著電梯下樓,單元樓下電瓶車、電動車有序的在智能充電棚充電,看著社區(qū)里面的花草樹木、整潔衛(wèi)生...
閱讀 3128·2021-11-10 11:36
閱讀 3322·2021-10-13 09:40
閱讀 6147·2021-09-26 09:46
閱讀 675·2019-08-30 15:55
閱讀 1419·2019-08-30 15:53
閱讀 1589·2019-08-29 13:55
閱讀 3005·2019-08-29 12:46
閱讀 3218·2019-08-29 12:34