成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

行星工單系統(tǒng)部分實(shí)現(xiàn)(1)-流程控制

lijy91 / 1721人閱讀

摘要:為使客服同學(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

相關(guān)文章

  • 中文維基百科文本數(shù)據(jù)獲取與預(yù)處理

    摘要:下載數(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...

    loostudy 評論0 收藏0
  • 模板方法簡介

    摘要:雖然不支持抽象類的自動注入,我們依舊可以進(jìn)一步靈活運(yùn)用模板方法模式中的鉤子方法思想,將類中所需要的屬性,創(chuàng)建好方法作為鉤子,這樣就不再局限與自身的限制了。 前言 在《重構(gòu)》這本書中,提到了很多種的代碼的壞味道,有一種就是重復(fù)的代碼,以及各種各樣的Switch 與 if/else 判斷,面對這種情況,可以利用 java 的多態(tài)來進(jìn)行替換。 今天要講的模板方法就是其中一種利用多態(tài)減少重復(fù)代...

    diabloneo 評論0 收藏0
  • 你認(rèn)為的智慧社區(qū)是怎樣的?

    摘要:作為業(yè)主的惘魍早上出門,坐著電梯下樓,單元樓下電瓶車電動車有序的在智能充電棚充電,看著社區(qū)里面的花草樹木整潔衛(wèi)生的環(huán)境,心情也舒暢起來。智慧社區(qū),未來可期,至簡可期 ? ? 作為業(yè)主的惘魍早上出門 ,坐著電梯下樓,單元樓下電瓶車、電動車有序的在智能充電棚充電,看著社區(qū)里面的花草樹木、整潔衛(wèi)生...

    NicolasHe 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<