import { StorageSchema } from 'oak-domain/lib/types/Storage'; import { Trigger } from "oak-domain/lib/types/Trigger"; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-domain/EntityDict'; import { aspectDict as basicAspectDict } from 'oak-general-business'; import { Aspect } from 'oak-domain/lib/types/Aspect'; import { Feature } from './types/Feature'; import { initialize as createBasicFeatures, BasicFeatures } from './features/index'; import { assign, intersection, keys, mapValues, pull } from 'lodash'; import { EntityDict } from 'oak-domain/lib/types/Entity'; import { FrontContext } from './FrontContext'; import { RunningContext } from "oak-domain/lib/types/Context"; import { DebugStore, Context } from 'oak-debug-store'; import { Schema as Application } from "oak-domain/lib/base-domain/Application/Schema"; import { Schema as Token } from 'oak-domain/lib/base-domain/Token/Schema'; import { TriggerExecutor } from "oak-trigger-executor"; import { AspectProxy } from './types/AspectProxy'; import { CacheStore } from './dataStore/CacheStore'; class DebugRunningContext extends Context implements RunningContext { getApplication: () => Application; getToken: () => Token | undefined; constructor(store: DebugStore, ga: () => Application, gt: () => Token | undefined) { super(store); this.getApplication = ga; this.getToken = gt; } }; function createAspectProxy>, FD extends Record>>( cacheStore: CacheStore, storageSchema: StorageSchema, triggers: Array>, applicationId: string, features: BasicFeatures & FD, aspectDict?: AD, initialData?: { [T in keyof ED]?: Array; }): AspectProxy { if (process.env.NODE_ENV === 'production') { // todo 发请求到后台获取数据 throw new Error('method not implemented'); } else { // todo initialData const executor = new TriggerExecutor(); const debugStore = new DebugStore(executor, storageSchema); triggers.forEach( (trigger) => debugStore.registerTrigger(trigger) ); const connectAspectToDebugStore = (aspect: Aspect): (p: Parameters[0], frontContext: FrontContext) => ReturnType => { return async (params: Parameters[0], frontContext: FrontContext) => { const context2 = new Context(debugStore); const { result: [application] } = await debugStore.select('application', { data: { id: 1, systemId: 1, system: { id: 1, }, }, filter: { id: applicationId, } }, context2); const getApplication = () => application as Application; const tokenValue = await features.token.get(frontContext as any, 'value') as string; let token: Token | undefined; if (tokenValue) { const { result } = await debugStore.select('token', { data: { id: 1, userId: 1, playerId: 1, }, filter: { id: tokenValue, } }, context2); token = result[0] as Token; // todo 判断 token的合法性 } const getToken = () => token; const runningContext = new DebugRunningContext(debugStore, getApplication, getToken) const result = aspect(params, runningContext); cacheStore.sync(runningContext.opRecords, frontContext); return result; } }; const aspectDict2 = assign({}, basicAspectDict, aspectDict); const aspectProxy = mapValues(aspectDict2, ele => connectAspectToDebugStore(ele)); return aspectProxy as any; } } export function initialize>, FD extends Record>>( storageSchema: StorageSchema, applicationId: string, createFeatures: (basicFeatures: BasicFeatures) => FD, triggers?: Array>, aspectDict?: AD, initialData?: { [T in keyof ED]?: Array; }) { const basicFeatures = createBasicFeatures(); const userDefinedfeatures = createFeatures(basicFeatures); const intersect = intersection(keys(basicFeatures), keys(userDefinedfeatures)); if (intersect.length > 0) { throw new Error(`用户定义的feature中不能和系统feature同名:「${intersect.join(',')}」`); } const features = assign(basicFeatures, userDefinedfeatures); const cacheStore = new CacheStore(storageSchema); // todo default triggers const aspectProxy = createAspectProxy(cacheStore, storageSchema, triggers || [], applicationId, features, aspectDict, initialData); keys(features).forEach( ele => { features[ele].setAspectProxy(aspectProxy); // 为action注入逻辑,在顶层的action调用返回时,回调subscribe相关函数 const originActionFn = features[ele]['action']; features[ele]['action'] = (context, params) => { const topAction = context.topAction; if (context.topAction) { context.topAction = false; } let result; try { result = originActionFn.call(features[ele], context, params); } catch(e) { context.topAction = topAction; throw e; } context.topAction = topAction; if (topAction) { callbacks.forEach( ele => ele() ); } return result; } } ); const callbacks: Array<() => void> = []; const subscribe = (callback: () => void) => { callbacks.push(callback); return () => { pull(callbacks, callback); }; }; return { subscribe, features, createContext: () => new FrontContext(cacheStore), }; } export * from './features/node'; export * from './FrontContext'; export * from './types/Feature'; export * from './features/cache'; export * from './features';