import { uuidv1, Data, replayWithLatest, useGeneralDriver } from './libs/mobius-utils' export type uuidGenerator = (scene?: 'batch'|'update'|'record') => string export interface UpdateManagerOptions { uuidGenerator?: uuidGenerator } export class UpdateManager { public generateUuid: uuidGenerator private _updateRequest: any[] | null | undefined constructor (options: UpdateManagerOptions) { const { uuidGenerator } = options this.generateUuid = (scene) => { let id try { id = uuidv1() } catch (e) { if (uuidGenerator == null) { throw (new Error('Please privide an uuidGenerator cause default generator is invalid in current environment!')) } id = uuidGenerator(scene) } return id } this.reset() } reset (): void { this._updateRequest = null this._batchId = '' this._updateDetail = null this._updateSummary = null this._collectRequest = null this._collectResult = null this._updateItems = null this._acceptResult = null this._parsedAcceptResult = null } // [{ type, detail: { ... } }] parseUpdateRequest (request) { this._updateRequest = request // TODO: 为 createRecord request 补充 id console.log('[UpdateManager] parsedUpdateRequest', request) return this._updateRequest } generateBatchId () { this._batchId = this.generateUuid() return this._batchId } /** * { * batchId, * payload: { * [updateId]: { batchId, updateId, type, detail: { ... } } * } * } */ createUpdateDetail ({ batchId }) { const updateDetail = { batchId: batchId, batchTime: Date.now(), payload: this._updateRequest.reduce((updates, request) => { const updateId = this.generateUuid() updates[updateId] = { ...request, updateId } return updates }, {}) } console.log('[UpdateManager] updateDetail', updateDetail) this._updateDetail = updateDetail return this._updateDetail } parseUpdateSummary (summary) { this._updateSummary = summary return this._updateSummary } /** * { * batchId, * payload: { * batch: batchId, * updates: { * [updateId]: { updateId, recordId }, * ... * } * } * } */ createCollectRequest ({ batchId }) { const collectRequest = { batchId: batchId, payload: { batch: batchId, updates: Object.entries(this._updateSummary.payload).reduce((acc, cur) => { const [updateId, { update: { detail: { recordId } } }] = cur acc[updateId] = { updateId, recordId } return acc }, {}) } } console.log('[UpdateManager] collectRequest', collectRequest) this._collectRequest = collectRequest return this._collectRequest } createUpdateItems (collectResult) { this._collectResult = collectResult console.log('[UpdateManager] collectResult', collectResult) const { batchId, payload: { batch, updates } } = collectResult const updateSummary = this._updateSummary.payload const updateDetail = this._updateDetail const updateItems = { batchId: batchId, payload: Object.values(updateSummary).reduce((items, cur) => { const { update: { type, detail, updateId }, updateTime } = cur items[updateId] = { type: type, batchId: batchId, batchTime: updateDetail.batchTime, lastBatchId: batch.lastBatchId, updateId: updateId, updateTime: updateTime, lastUpdateId: updates[updateId].lastUpdateId, detail: detail } return items }, {}) } console.log('[UpdateManager] updateItems', updateItems) this._updateItems = updateItems return this._updateItems } parseAcceptResult (acceptResult) { this._acceptResult = acceptResult const { batchId } = acceptResult const parsedAcceptResult = { batchId: batchId, payload: { isSuccess: Object.values(acceptResult.payload).every(b => b) } } console.log('[UpdateContainer] parsedAcceptResult', parsedAcceptResult) this._parsedAcceptResult = parsedAcceptResult return this._parsedAcceptResult } } export interface updateManagerDriverOptions { uuidGenerator?: uuidGenerator } export interface updateManagerDriverInstance { inputs: Record outputs: Record } export const updateManagerDriver = (options: updateManagerDriverOptions): updateManagerDriverInstance => { const { uuidGenerator } = options const updateManager = new UpdateManager({ uuidGenerator }) const processInD = Data.empty() const executeStatusRD = replayWithLatest(1, Data.empty()) const updateRequestInD = Data.empty() const updateSummaryInD = Data.empty() const collectResultInD = Data.empty() const acceptResultInD = Data.empty() // const endUpdateD = Data.empty() const updateDetailD = Data.empty() const collectRequestD = Data.empty() const updateItemsD = Data.empty() // 接收到调度器的运行状态之后,进行相应的处理 processInD.subscribeValue(processStatus => { const { status } = processStatus console.log('[UpdateManagerDriver] receive status', status) if (status === 'start') { executeStatusRD.mutate(() => ({ status: 'start' })) } if (status === 'update') { executeStatusRD.mutate(() => ({ status: 'update' })) const updateDetail = updateManager.createUpdateDetail({ batchId: updateManager._batchId }) updateDetailD.mutate(() => updateDetail) } if (status === 'end') { updateManager.reset() console.log('[UpdateManagerDriver] reset updateManager instance') executeStatusRD.mutate(() => ({ status })) } }) // 接收到 UpdateRequest 之后,进行更新前的准备工作 // -> 解析 UpdateRequest // -> 生成 BatchId // -> 广播 StartUpdateStatus updateRequestInD.subscribeValue(request => { updateManager.parseUpdateRequest(request) const batchId = updateManager.generateBatchId() executeStatusRD.mutate(() => ({ status: 'init' })) }) // 接收到 UpdateSummary 之后,广播一条 CollectRequest updateSummaryInD.subscribeValue(updateSummary => { const { batchId } = updateManager.parseUpdateSummary(updateSummary) const collectRequest = updateManager.createCollectRequest({ batchId }) collectRequestD.mutate(() => collectRequest) }) // 接收到 CollectResult 之后, collectResultInD.subscribeValue(collectResult => { const updateItems = updateManager.createUpdateItems(collectResult) updateItemsD.mutate(() => updateItems) }) // 接收到 AcceptResult 之后,解析 AcceptResult 并将本次更新结果上报 acceptResultInD.subscribeValue(acceptResult => { const { batchId, payload: { isSuccess } } = updateManager.parseAcceptResult(acceptResult) if (isSuccess) { executeStatusRD.mutate(() => ({ status: 'update', result: 'success', batchId })) } else { executeStatusRD.mutate(() => ({ status: 'update', result: 'fail', batchId })) } }) return { inputs: { process: processInD, updateRequest: updateRequestInD, updateSummary: updateSummaryInD, collectResult: collectResultInD, acceptResult: acceptResultInD }, outputs: { executeStatus: executeStatusRD, updateDetail: updateDetailD, collectRequest: collectRequestD, updateItems: updateItemsD } } } export const useUpdateManagerDriver = useGeneralDriver(updateManagerDriver)