和trigger相关的数据结构

This commit is contained in:
Xu Chang 2022-04-11 18:50:53 +08:00
parent fe08c014f6
commit 3d1b71e02b
11 changed files with 120 additions and 87 deletions

View File

@ -1,18 +1,67 @@
import assert from 'assert';
import { EntityDict } from "oak-domain/lib/types/Entity";
import { BaseEntityDict } from "oak-general-business/lib/base-ed/EntityDict";
import { EntityDict } from 'oak-domain/lib/types/Entity';
import { Context as BaseContext } from 'oak-memory-tree-store';
import { Schema as Application } from "oak-general-business/lib/base-ed/Application/Schema";
import { Schema as Token } from 'oak-general-business/lib/base-ed/Token/Schema';
import { DebugStore } from './debugStore';
import { RuntimeContext } from 'oak-general-business';
export class DebugContext<ED extends EntityDict> extends BaseContext<ED> {
export class DebugContext<ED extends BaseEntityDict & EntityDict> extends BaseContext<ED> implements RuntimeContext<ED> {
getApplication: () => Application | undefined;
getToken: () => Token | undefined;
async initGetFn(applicationId?: string, tokenValue?: string) {
if (applicationId) {
const { result: [application] } = await this.rowStore.select('application', {
data: {
id: 1,
systemId: 1,
system: {
id: 1,
},
},
filter: {
id: applicationId,
}
}, this);
this.getApplication = () => application as Application;
}
if (tokenValue) {
const { result } = await this.rowStore.select('token', {
data: {
id: 1,
userId: 1,
playerId: 1,
},
filter: {
id: tokenValue,
}
}, this);
const token = result[0] as Token;
// todo 判断 token的合法性
this.getToken = () => token;
}
}
constructor(store: DebugStore<ED>, applicationId?: string, tokenValue?: string) {
super(store);
this.getApplication = () => undefined;
this.getToken = () => undefined;
this.initGetFn(applicationId, tokenValue);
}
txn?: {
events: {
commit: Array<(context: DebugContext<ED>) => Promise<void>>;
rollback: Array<(context: DebugContext<ED>) => Promise<void>>;
}
};
on (event: "commit" | "rollback", callback: (context: any) => Promise<void>): void {
on(event: "commit" | "rollback", callback: (context: any) => Promise<void>): void {
this.txn!.events[event].push(callback);
}
async begin(options?: object): Promise<void> {
assert(!this.txn);
await super.begin();

View File

@ -1,12 +1,12 @@
import { EntityDef, EntityDict, OperationResult, SelectionResult } from "oak-domain/lib/types/Entity";
import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { TreeStore } from 'oak-memory-tree-store';
import { DebugContext } from './context';
import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor';
import { Trigger, TriggerEntityShape } from "oak-domain/lib/types/Trigger";
import { TriggerExecutor, Trigger } from 'oak-general-business';
import { StorageSchema } from "oak-domain/lib/types/Storage";
export class DebugStore<ED extends EntityDict> extends TreeStore<ED> {
export class DebugStore<ED extends EntityDict & BaseEntityDict> extends TreeStore<ED> {
private executor: TriggerExecutor<ED>;

View File

@ -2,10 +2,9 @@ import { assign } from 'lodash';
import { DebugStore } from './debugStore';
import { DebugContext } from './context';
import { FormCreateData, Selection, EntityDict } from "oak-domain/lib/types/Entity";
import { EntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { BaseEntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { StorageSchema } from 'oak-domain/lib/types/Storage';
import { Trigger } from 'oak-domain/lib/types/Trigger';
import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor';
import { Trigger, TriggerExecutor } from 'oak-general-business';
import { data as generalData, triggers as generalTriggers } from 'oak-general-business';
import { FrontContext } from '../FrontContext';

View File

@ -1,5 +1,5 @@
import { DeduceSelection, EntityDict, OperateParams, OpRecord } from 'oak-domain/lib/types/Entity';
import { Aspect } from 'oak-general-business/lib/types/Aspect';
import { Aspect } from 'oak-general-business';
import { Action, Feature } from '../types/Feature';
import { assign } from 'lodash';
import { FrontContext } from '../FrontContext';
@ -36,12 +36,17 @@ export class Cache<ED extends EntityDict, AD extends Record<string, Aspect<ED>>>
}
@Action
async operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], params?: OperateParams) {
async operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], commit: boolean = true, params?: OperateParams) {
const context = new FrontContext(this.cacheStore);
let result: Awaited<ReturnType<typeof this.cacheStore.operate>>;
try {
result = await this.cacheStore.operate(entity, operation, context, params);
await context.commit();
if (commit) {
await context.commit();
}
else {
await context.rollback();
}
}
catch(err) {
await context.rollback();

View File

@ -1,6 +1,6 @@
import { EntityDict } from 'oak-domain/lib/types/Entity';
import { EntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { Aspect } from 'oak-general-business/lib/types/Aspect';
import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { Aspect } from 'oak-general-business';
import { Cache } from './cache';
import { Location } from './location';

View File

@ -1,5 +1,5 @@
import { EntityDict, OpRecord } from 'oak-domain/lib/types/Entity';
import { Aspect } from 'oak-general-business/lib/types/Aspect';
import { Aspect } from 'oak-general-business';
import { Action, Feature } from '../types/Feature';
export class Location extends Feature<EntityDict, Record<string, Aspect<EntityDict>>> {

View File

@ -1,6 +1,6 @@
import { set, cloneDeep, pull, unset } from 'lodash';
import { DeduceCreateOperation, DeduceFilter, DeduceOperation, DeduceSelection, DeduceUpdateOperation, EntityDict, EntityShape, FormCreateData, OperationResult, OpRecord, SelectionResult } from 'oak-domain/lib/types/Entity';
import { Aspect } from 'oak-general-business/lib/types/Aspect';
import { Aspect } from 'oak-general-business';
import { combineFilters } from 'oak-domain/lib/store/filter';
import { Action, Feature } from '../types/Feature';
import { Cache } from './cache';
@ -116,7 +116,7 @@ class ListNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T e
this.pagination = pagination || DEFAULT_PAGINATION;
}
composeAction(): DeduceOperation<ED[T]['Schema']> | DeduceOperation<ED[T]['Schema']>[] | undefined {
composeOperation(): DeduceOperation<ED[T]['Schema']> | DeduceOperation<ED[T]['Schema']>[] | undefined {
if (!this.isDirty()) {
return;
}
@ -129,7 +129,7 @@ class ListNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T e
}
const actions = [];
for (const node of this.children) {
const subAction = node.composeAction();
const subAction = node.composeOperation();
if (subAction) {
actions.push(subAction);
}
@ -266,7 +266,7 @@ class SingleNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T
}
}
composeAction(): DeduceOperation<ED[T]['Schema']> | undefined {
composeOperation(): DeduceOperation<ED[T]['Schema']> | undefined {
if (!this.isDirty()) {
return;
}
@ -281,7 +281,7 @@ class SingleNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T
},
} as DeduceUpdateOperation<ED[T]['Schema']>;
for (const attr in this.children) {
const subAction = this.children[attr]!.composeAction();
const subAction = this.children[attr]!.composeOperation();
if (subAction) {
assign(action.data, {
[attr]: subAction,
@ -483,7 +483,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
this.schema = schema;
}
private async applyAction<T extends keyof ED>(
private async applyOperation<T extends keyof ED>(
entity: T, value: Partial<ED[T]['Schema']> | Partial<ED[T]['Schema']>[] | undefined,
operation: DeduceOperation<ED[T]['Schema']> | DeduceOperation<ED[T]['Schema']>[],
projection: DeduceSelection<ED[T]['Schema']>['data']): Promise<Partial<ED[T]['Schema']> | Partial<ED[T]['Schema']>[] | undefined> {
@ -492,7 +492,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
for (const action of operation) {
switch (action.action) {
case 'create': {
value.push(await this.applyAction(entity, undefined, action, projection) as Partial<ED[T]['Schema']>);
value.push(await this.applyOperation(entity, undefined, action, projection) as Partial<ED[T]['Schema']>);
}
case 'remove': {
const { filter } = action;
@ -508,7 +508,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
const row = value.find(
ele => ele.id === filter!.id
);
await this.applyAction(entity, row!, action, projection);
await this.applyOperation(entity, row!, action, projection);
}
}
}
@ -518,7 +518,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
if (value instanceof Array) {
// todo 这里还有种可能不是对所有的行只对指定id的行操作
return (await Promise.all(value.map(
async (row) => await this.applyAction(entity, row, operation, projection)
async (row) => await this.applyOperation(entity, row, operation, projection)
))).filter(
ele => !!ele
) as Partial<ED[T]['Schema']>[];
@ -534,7 +534,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
else if (relation === 2) {
// 基于entity/entityId的多对一
if (projection[attr]) {
set(row, attr, await this.applyAction(attr, row[attr], actionData[attr]!, projection[attr]!));
set(row, attr, await this.applyOperation(attr, row[attr], actionData[attr]!, projection[attr]!));
if (row[attr]) {
assign(row, {
entity: attr,
@ -551,7 +551,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
}
else if (typeof relation === 'string') {
if (projection[attr]) {
set(row, attr, await this.applyAction(relation, row[attr], actionData[attr]!, projection[attr]!));
set(row, attr, await this.applyOperation(relation, row[attr], actionData[attr]!, projection[attr]!));
if (row[attr]) {
assign(row, {
[`${attr}Id`]: row[attr]!['id'],
@ -567,7 +567,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
else {
assert(relation instanceof Array);
if (projection[attr]) {
set(row, attr, await this.applyAction(relation[0], row[attr], actionData[attr]!, projection[attr]!));
set(row, attr, await this.applyOperation(relation[0], row[attr], actionData[attr]!, projection[attr]!));
row[attr]!.forEach(
(ele: ED[keyof ED]['Schema']) => {
if (relation[1]) {
@ -646,9 +646,9 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
const node = await this.findNode(path);
let value = await node.getValue(this.cache);
if (node.isDirty()) {
const operation = node.composeAction();
const operation = node.composeOperation();
const projection = node.getProjection();
value = (await this.applyAction(node.getEntity(), value, operation!, projection as any));
value = (await this.applyOperation(node.getEntity(), value, operation!, projection as any));
}
if (value instanceof Array) {
@ -657,6 +657,11 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
return [value!];
}
async isDirty(path: string) {
const node = await this.findNode(path);
return node.isDirty();
}
private async findNode(path: string) {
const paths = path.split('.');
let node = this.root[paths[0]];
@ -725,5 +730,30 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
}
}
}
@Action
async execute(path: string, isTry?: boolean) {
const node = await this.findNode(path);
const operation = node.composeOperation();
// 先在cache中尝试能否执行如果权限上否决了在这里就失败
if (operation instanceof Array) {
for (const oper of operation) {
await this.cache.operate(node.getEntity(), oper, false);
}
}
else if (operation) {
await this.cache.operate(node.getEntity(), operation, false);
}
else {
return;
}
if (isTry) {
return;
}
console.log('exeucte to aspectProxy');
// this.getAspectProxy().operate
}
}

View File

@ -1,4 +1,4 @@
import { EntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { BaseEntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { aspectDict as basicAspectDict } from 'oak-general-business';
import { Action, Feature } from '../types/Feature';
import { Cache } from './cache';

View File

@ -1,9 +1,9 @@
import { StorageSchema } from 'oak-domain/lib/types/Storage';
import { Trigger } from "oak-domain/lib/types/Trigger";
import { EntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { Trigger } from "oak-general-business";
import { BaseEntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict';
import { aspectDict as basicAspectDict } from 'oak-general-business';
import { Aspect } from 'oak-general-business/lib/types/Aspect';
import { Aspect } from 'oak-general-business';
import { Feature, subscribe } from './types/Feature';
import { createDebugStore } from './debugStore';
@ -19,17 +19,6 @@ import { Schema as Token } from 'oak-general-business/lib/base-ed/Token/Schema';
import { AspectProxy } from './types/AspectProxy';
import { CacheStore } from './cacheStore/CacheStore';
class DebugRuntimeContext<ED extends EntityDict> extends DebugContext<ED> implements RuntimeContext<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>>>(
@ -52,39 +41,9 @@ function createAspectProxy<ED extends BaseEntityDict & EntityDict,
const connectAspectToDebugStore = (aspect: Aspect<ED>): (p: Parameters<typeof aspect>[0]) => ReturnType<typeof aspect> => {
return async (params: Parameters<typeof aspect>[0]) => {
const context2 = new DebugContext(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.getValue();
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 DebugRuntimeContext(debugStore, getApplication, getToken);
const runningContext = new DebugContext(debugStore, applicationId, tokenValue);
await runningContext.begin();
let aspectCompeleted = false;
try {

View File

@ -1,4 +1,4 @@
import { Aspect } from "oak-general-business/lib/types/Aspect";
import { Aspect } from "oak-general-business";
import { EntityDict } from "oak-domain/lib/types/Entity";
import { FrontContext } from "../FrontContext";

View File

@ -1,5 +1,5 @@
import { pull } from 'lodash';
import { Aspect } from 'oak-general-business/lib/types/Aspect';
import { Aspect } from 'oak-general-business';
import { EntityDict } from 'oak-domain/lib/types/Entity';
import { aspectDict as basicAspectDict} from 'oak-general-business';
import { FrontContext } from '../FrontContext';
@ -8,7 +8,6 @@ import { AspectProxy } from './AspectProxy';
export abstract class Feature<ED extends EntityDict, AD extends Record<string, Aspect<ED>>> {
private aspectProxy?: AspectProxy<ED, AD & typeof basicAspectDict>;
private context?: FrontContext<ED>;
protected getAspectProxy() {
return this.aspectProxy!;
@ -17,14 +16,6 @@ export abstract class Feature<ED extends EntityDict, AD extends Record<string, A
setAspectProxy(aspectProxy: AspectProxy<ED, AD & typeof basicAspectDict>) {
this.aspectProxy = aspectProxy;
}
protected getContext() {
return this.context!;
}
setContext(context: FrontContext<ED>) {
this.context = context;
}
}