oak-frontend-base/src/index.ts

181 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<ED extends EntityDict> extends Context<ED> implements RunningContext<ED> {
getApplication: () => Application;
getToken: () => Token | undefined;
constructor(store: DebugStore<ED>, ga: () => Application, gt: () => Token | undefined) {
super(store);
this.getApplication = ga;
this.getToken = gt;
}
};
function createAspectProxy<ED extends BaseEntityDict & EntityDict,
AD extends Record<string, Aspect<ED>>,
FD extends Record<string, Feature<ED, AD>>>(
cacheStore: CacheStore<ED>,
storageSchema: StorageSchema<ED>,
triggers: Array<Trigger<ED, keyof ED>>,
applicationId: string,
features: BasicFeatures<ED, AD> & FD,
aspectDict?: AD,
initialData?: {
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
}): AspectProxy<ED, AD & typeof basicAspectDict> {
if (process.env.NODE_ENV === 'production') {
// todo 发请求到后台获取数据
throw new Error('method not implemented');
}
else {
// todo initialData
const executor = new TriggerExecutor<ED>();
const debugStore = new DebugStore<ED>(executor, storageSchema);
triggers.forEach(
(trigger) => debugStore.registerTrigger(trigger)
);
const connectAspectToDebugStore = (aspect: Aspect<ED>): (p: Parameters<typeof aspect>[0], frontContext: FrontContext<ED>) => ReturnType<typeof aspect> => {
return async (params: Parameters<typeof aspect>[0], frontContext: FrontContext<ED>) => {
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<ED extends EntityDict & BaseEntityDict, AD extends Record<string, Aspect<ED>>, FD extends Record<string, Feature<ED, AD>>>(
storageSchema: StorageSchema<ED>,
applicationId: string,
createFeatures: (basicFeatures: BasicFeatures<ED, AD>) => FD,
triggers?: Array<Trigger<ED, keyof ED>>,
aspectDict?: AD,
initialData?: {
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
}) {
const basicFeatures = createBasicFeatures<ED, AD>();
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<ED>(storageSchema);
// todo default triggers
const aspectProxy = createAspectProxy<ED, AD, FD>(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<ED>(cacheStore),
};
}
export * from './features/node';
export * from './FrontContext';
export * from './types/Feature';
export * from './features/cache';
export * from './features';