国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

行星工單系統部分實現(1)-流程控制

lijy91 / 1712人閱讀

摘要:為使客服同學有序協同處理客戶問題客服技術團隊打造了多渠道整合的,可靈活配置的,便于流轉的行星工單系統。所以根據這個處理思路,我們建立工單系統的流程控制方案。據此,我們用類管道函數實現了簡單的流程控制。行為傳參通過標志指定,僅能指定單行為。

1.工單系統的表格頁

工單系統是一種網絡軟件系統,根據不同組織,部門和外部客戶的需求,來由針對的管理,維護和追蹤一系列的問題和請求。大多用于記錄、處理、跟蹤一項工作的完成情況。
為使客服同學有序、協同處理客戶問題,客服技術團隊打造了多渠道整合的,可靈活配置的,便于流轉的行星工單系統。

由于工單系統從屬后臺系統,頁面展示基本以如下表格頁為主.為統一表格頁行為,故將其統一封裝成表格頁組件.

表格頁組件分為如下幾塊:

篩選項區域

表格主體

分頁組件

操作按鈕

行內操作鏈接


其中分頁組件可通過向表格頁組件傳入標志來控制其顯示狀態、篩選項區域及表格主體內容可通過向表格頁組件傳入表格列配置及表單項配置實現不同頁面間各異的邏輯。

但由于操作按鈕、行內操作鏈接的邏輯各頁基本不同,無法將其封入表格頁組件。因此采用事件處理這部分邏輯從列表頁組件中解耦,我們將不同表格頁按鈕操作名稱作為唯一標識,在入口文件中將系統全量事件集注冊到全局,將標識與事件集中事件建立起對應關系,以此實現上述需求。

然后我們發現,這些操作按鈕的邏輯,基本為以下幾類行為的聚合:

彈出提示框讓用戶操作

彈出表單類組件獲取一部分數據

表單及其他數據校驗

調用接口去拉取/更新數據

而且比較重要的一點是,它們都是串行的。如編輯用戶的操作:

從接口拉取用戶數據

彈出表單組件將數據填入同時供用戶修改

校驗用戶提交數據

調用接口提交數據。

這幾步操作中,如果流程中某一節點的行為出現問題,比較合理的處理方案是截停流程,并拋出一個錯誤響應給開發者或用戶。所以根據這個處理思路,我們建立工單系統的流程控制方案。

2.簡單的管道

論及串行執行流程行為,并將當前行為的執行結果作為下一行為的參數的操作,我們首先想到的是vue及angular中的管道。下面我們用Array.reduce去實現一個簡單的管道.

const grep = ({srcData, fnList, context}) => fnList.reduce((dist, currFn) => currFn.call(context, dist), srcData)

此函數邏輯很簡單,即遍歷行為集,取各項行為執行,再借用reduce特性將上一行為結果傳入下一行為。據此,我們用類管道函數實現了簡單的流程控制。

如下調用:

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內部強制更改fnList的上下文,故fnList內部函數不可用箭頭函數及bind進行上下文綁定

2.為了靈活適應需求,不強制規范流轉數據格式,或造成多次遍歷等性能浪費,由于前端數據量較小,故不考慮性能問題.

3.流程控制

但是管道有兩個不能解決的問題:

1.不能處理異步需求:如需求為接口返回結果之后,將結果作為下一個函數的參數。鑒于fnList(流程行為集)中如果存在異步函數,那么異步流程行為函數在流程中不能流轉數據或僅能流轉promise對象嗎,無法完成上述需求。

2.當前流程節點行為只能獲取上個流程節點行為執行結果:我們需要更靈活的獲取之前行為結果的方式,如跨節點獲取、多節點獲取。

為解決以上兩點我們初步構建事務流處理函數如下:

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 = "" } = actionItem
        const ownParams = getRestObj(actionItem, ["actionKey", "from"])
        const cacheReal = cache.then ? await cache : cache //初次非promise
        const { continueStatus: prevContinueStatus, resultCache: prevResultCache } = cacheReal //continueStatus繼續執行標識   resultCache各個行為運行結果緩存
        const paramsFromCache = prevResultCache[from]
        const params = paramsFromCache ? objDeepMerge(ownParams, paramsFromCache) : ownParams // 混合兩個源的參數
        const actionFn = eventActionMap[actionKey] 
        let result = prevContinueStatus && await actionFn.call(that, params, stateObj, prevResultCache) // 顯式指定行為上下文為當前頁面vue實例
        result = result === undefined ? true : result
        const continueStatus = prevContinueStatus && !!result
        const resultCache = { ...prevResultCache, [actionKey]: result }
        return { continueStatus, resultCache }  // 更新運行標識和緩存
    }, { continueStatus: true, resultCache: resCache || {} })
}

此函數通過同類管道函數grep一樣,借助Array.reduce遍歷傳入的配置數組(actionList),從每項配置(actionItem)中取得行為標識(actionKey)后,從通用行為庫(eventMap)和上下文中獲取到當前行為(actionFn)去執行當前行為。

不同之處在于,grep僅流轉操作后的數據.而此函數通過流轉:

1.繼續執行標志 (continueStatus),并在執行當前行為(actionFn)前校驗此標志,來實現出現異常后停止后續行為的操作。

2.全部行為返回的結果的緩存 (resultCache),并結合從每項配置(actionItem)中獲取參數來源(from)來靈活指派其傳參,使當前行為的傳參不拘泥于僅為上一行為執行結果。

另外對于當前事務為異步的情況,我們利用async的特性(1.表象為同步處理 2.返回promise ),并將其用到reduce處理函數中。最終將reduce中每項的處理函數揉為一個整體promise

現在我們的流程控制函數初具雛形,其調用方式如下:

function deleteUser(that, params = {}) {
    const { id, name } = params
    const config = [
        {
            actionKey: "showConfirmModal", msg: `確認刪除員工:【${name}】?`
        },
        {
            actionKey: "simpleReq",
            url: "/user/transfer/removeUser",
            data: { userId: id },
            otherOption: {
                errorMsg: "用戶刪除失敗!",
                succMsg: "用戶刪除成功!"
            }
        },
        {
            actionKey: "initTable",
        },
    ]
    actionFnSimple.bind(that, config)
}

其中showConfirmModal,simpleReq取自通用行為集, initTable取自事件觸發組件的methods, 如下:

const eventActionMap = { // 截取部分通用行為集
     showConfirmModal(params) {
        const { msg = "確認執行當前操作?", 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.更靈活的流程控制

雖然我們的流程控制函數可以處理簡單需求,但是將其應用于處理復雜需求還需要解決以下幾點問題:

1.行為只能通過通用行為庫及組件上下文提供,過于單一。如map后端數據這種無需復用的行為存入行為庫較為不妥,又因其不屬于組件邏輯,故也不能放入組件中。因此需建立用戶手動指定行為(customFn)以解決此問題。

2.由于行為結果緩存以actionkey作為唯一標識,流程中若引用多個通用行為會造成同名結果緩存被覆蓋,,故引入別名(alias)機制以區分引用多個通用行為結果緩存的問題。

2.行為傳參通過from標志指定,,from僅能指定單行為。如果有從多處表單取值校驗并請求接口的需求,則無法實現。一種解決方案是判斷from內容,若為全量標志(建議用symbol建議避免與actionKey沖突),則將各行為結果緩存(resultCache)傳入。但此解決方案會向行為傳入冗余參數。 另一種方案是允許from為數組,通過from數組中的actionkey去resultCache中取得所需行為的結果,,再傳入當前行為。兩種方案皆可實現此需求,,鑒于第二種方案沒有冗余傳參,此處我們采用方案二。

據此我們得到流程控制函數如下:

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 } // 新增別名機制
        return { continueStatus, resultCache } 
    }, { continueStatus: true, resultCache: resCache || {} })
}
5.錯誤處理機制

我們的流程處理函數雖然可以處理復雜邏輯,且可以通過在流程節點的行為里返回false去截停流程。但在截停流程后,并未對用戶作出反饋。且當行為產生異常時(邏輯錯誤返回false),并不能及時反饋給用戶,也并未在控制臺輸出日志, 這樣對用戶、開發者都不友好。

故當流程節點行為返回false時,我們引入錯誤處理機制如下:

觸發此節點配置文件中的onError函數

向控制臺輸出錯誤日志

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){ // 新增錯誤處理機制
            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

鑒于繼續執行標志及行為結果緩存的共享特性, 可將流程處理函數主體作為方法封入class,繼續執行標志及行為結果緩存可作為class屬性來實現其共享。

流程處理函數核心通過promise流來實現,可改用事件流的形式。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98485.html

相關文章

  • 中文維基百科文本數據獲取與預處理

    摘要:下載數據方法使用官方的數據最新打包的中文文檔下載地址是。中文維基數據繁簡混雜大家都說存在這個問題,但的網站是將繁體中文和簡體中文分開處理的,所以一直覺得從數據庫到結構都應有方法將兩者區分開,暫罷,待有空研究其數據時再議。 照例,先講下環境,Mac OSX 10.11.2 ,Python 3.4.3。 下載數據 方法1:使用官方dump的xml數據 最新打包的中文文檔下載地址是:http...

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

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

    diabloneo 評論0 收藏0
  • 你認為的智慧社區是怎樣的?

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

    NicolasHe 評論0 收藏0

發表評論

0條評論

lijy91

|高級講師

TA的文章

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