toDo的一些设计与实现

This commit is contained in:
Xu Chang 2023-11-30 15:45:06 +08:00
parent dd0c750a2f
commit 37eaf35998
28 changed files with 475 additions and 111 deletions

View File

@ -1,2 +1,2 @@
declare const checkers: (import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "address", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "token", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "user", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "application", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "mobile", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "message", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "parasite", import("..").RuntimeCxt>)[];
declare const checkers: (import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "message", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "mobile", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "address", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "token", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "user", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "application", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").RuntimeCxt> | import("oak-domain").Checker<import("../oak-app-domain").EntityDict, "parasite", import("..").RuntimeCxt>)[];
export default checkers;

View File

@ -1,7 +1,7 @@
import { Style } from '../../../../types/Style';
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../../oak-app-domain").EntityDict, keyof import("../../../../oak-app-domain").EntityDict, false, {
style: Style;
entity: "system" | "application" | "platform";
entity: "application" | "system" | "platform";
entityId: string;
name: string;
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;

View File

@ -12,7 +12,7 @@ declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends key
type?: ButtonProps['type'] | AmButtonProps['type'];
executeText?: string | undefined;
buttonProps?: (ButtonProps & {
color?: "primary" | "success" | "warning" | "default" | "danger" | undefined;
color?: "default" | "success" | "warning" | "primary" | "danger" | undefined;
fill?: "none" | "solid" | "outline" | undefined;
size?: "small" | "large" | "middle" | "mini" | undefined;
block?: boolean | undefined;
@ -24,9 +24,9 @@ declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends key
type?: "button" | "reset" | "submit" | undefined;
shape?: "default" | "rounded" | "rectangular" | undefined;
children?: import("react").ReactNode;
} & Pick<import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "id" | "onMouseUp" | "onMouseDown" | "onTouchStart" | "onTouchEnd"> & {
} & Pick<import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "id" | "onMouseDown" | "onMouseUp" | "onTouchEnd" | "onTouchStart"> & {
className?: string | undefined;
style?: (import("react").CSSProperties & Partial<Record<"--text-color" | "--background-color" | "--border-radius" | "--border-width" | "--border-style" | "--border-color", string>>) | undefined;
style?: (import("react").CSSProperties & Partial<Record<"--background-color" | "--border-color" | "--text-color" | "--border-radius" | "--border-width" | "--border-style", string>>) | undefined;
tabIndex?: number | undefined;
} & import("react").AriaAttributes) | undefined;
afterCommit?: AfterCommit;

11
es/entities/ToDo.d.ts vendored
View File

@ -1,19 +1,14 @@
import { String, Text } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
export type RedirectToProps = {
pathname: string;
props?: Record<string, any>;
state?: Record<string, any>;
};
export type Condition = {
condition?: any;
batchPath: string;
singlePath?: string;
};
export interface Schema extends EntityShape {
title: Text;
description?: Text;
targetEntity: String<32>;
targetEntityId?: String<64>;
condition?: Condition;
targetFilter: Object;
action: String<32>;
redirectTo: RedirectToProps;
}

View File

@ -1,7 +1,7 @@
;
const IActionDef = {
stm: {
done: ['active', 'done'],
complete: ['active', 'done'],
},
};
const entityDesc = {
@ -13,8 +13,7 @@ const entityDesc = {
title: '标题',
description: '描述',
targetEntity: '对象实体',
targetEntityId: '对象实体Id',
condition: '过滤条件',
targetFilter: '过滤条件',
action: '动作',
redirectTo: '重定向页面',
},
@ -22,7 +21,7 @@ const entityDesc = {
collaborator: '协作者',
},
action: {
done: '完成',
complete: '完成',
},
v: {
iState: {

View File

@ -1,7 +1,7 @@
import { Q_DateValue, Q_StringValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
import { OneOf } from "oak-domain/lib/types/Polyfill";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction } from "oak-domain/lib/types/Entity";
import { ReadOnlyAction } from "oak-domain/lib/actions/action";
import { GenericAction } from "oak-domain/lib/actions/action";
import { String } from "oak-domain/lib/types/DataType";
import { EntityShape } from "oak-domain/lib/types/Entity";
export type OpSchema = EntityShape & {
@ -91,7 +91,7 @@ export type I18nIdSubQuery = Selection<I18nIdProjection>;
export type EntityDef = {
Schema: Schema;
OpSchema: OpSchema;
Action: OakMakeAction<ReadOnlyAction> | string;
Action: OakMakeAction<GenericAction> | string;
Selection: Selection;
Aggregation: Aggregation;
Operation: Operation;

View File

@ -1,4 +1,4 @@
import { readOnlyActions as actions } from "oak-domain/lib/actions/action";
import { genericActions as actions } from "oak-domain/lib/actions/action";
export const desc = {
attributes: {
module: {
@ -35,7 +35,7 @@ export const desc = {
}
},
static: true,
actionType: "readOnly",
actionType: "crud",
actions,
indexes: [
{

View File

@ -1,7 +1,7 @@
import { ActionDef } from "oak-domain/lib/types/Action";
import { GenericAction } from "oak-domain/lib/actions/action";
export type IState = 'active' | 'done' | string;
export type IAction = 'done' | string;
export type IAction = 'complete' | string;
export type ParticularAction = IAction;
export declare const actions: string[];
export type Action = GenericAction | ParticularAction | string;

View File

@ -1,9 +1,9 @@
const IActionDef = {
stm: {
done: ['active', 'done'],
complete: ['active', 'done'],
},
};
export const actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "done"];
export const actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "complete"];
export const ActionDefDict = {
iState: IActionDef
};

View File

@ -9,19 +9,14 @@ import { EntityShape } from "oak-domain/lib/types/Entity";
import * as Relation from "../Relation/Schema";
import * as UserRelation from "../UserRelation/Schema";
export type RedirectToProps = {
pathname: string;
props?: Record<string, any>;
state?: Record<string, any>;
};
export type Condition = {
condition?: any;
batchPath: string;
singlePath?: string;
};
export type OpSchema = EntityShape & {
title: Text;
description?: Text | null;
targetEntity: String<32>;
targetEntityId?: String<64> | null;
condition?: Condition | null;
targetFilter: Object;
action: String<32>;
redirectTo: RedirectToProps;
iState?: IState | null;
@ -31,8 +26,7 @@ export type Schema = EntityShape & {
title: Text;
description?: Text | null;
targetEntity: String<32>;
targetEntityId?: String<64> | null;
condition?: Condition | null;
targetFilter: Object;
action: String<32>;
redirectTo: RedirectToProps;
iState?: IState | null;
@ -51,8 +45,7 @@ type AttrFilter = {
title: Q_StringValue;
description: Q_StringValue;
targetEntity: Q_StringValue;
targetEntityId: Q_StringValue;
condition: JsonFilter<Condition>;
targetFilter: Object;
action: Q_StringValue;
redirectTo: JsonFilter<RedirectToProps>;
iState: Q_EnumValue<IState>;
@ -70,8 +63,7 @@ export type Projection = {
title?: number;
description?: number;
targetEntity?: number;
targetEntityId?: number;
condition?: number | JsonProjection<Condition>;
targetFilter?: number | Object;
action?: number;
redirectTo?: number | JsonProjection<RedirectToProps>;
iState?: number;
@ -105,10 +97,6 @@ export type SortAttr = {
description: number;
} | {
targetEntity: number;
} | {
targetEntityId: number;
} | {
condition: number;
} | {
action: number;
} | {

View File

@ -15,13 +15,8 @@ export const desc = {
length: 32
}
},
targetEntityId: {
type: "varchar",
params: {
length: 64
}
},
condition: {
targetFilter: {
notNull: true,
type: "object"
},
action: {

View File

@ -1 +1 @@
{ "name": "待办", "attr": { "iState": "状态", "title": "标题", "description": "描述", "targetEntity": "对象实体", "targetEntityId": "对象实体Id", "condition": "过滤条件", "action": "动作", "redirectTo": "重定向页面" }, "r": { "collaborator": "协作者" }, "action": { "done": "完成" }, "v": { "iState": { "active": "待办", "done": "已完成" } } }
{ "name": "待办", "attr": { "iState": "状态", "title": "标题", "description": "描述", "targetEntity": "对象实体", "targetFilter": "过滤条件", "action": "动作", "redirectTo": "重定向页面" }, "r": { "collaborator": "协作者" }, "action": { "complete": "完成" }, "v": { "iState": { "active": "待办", "done": "已完成" } } }

24
es/triggers/toDo.d.ts vendored Normal file
View File

@ -0,0 +1,24 @@
import { EntityDict } from '../oak-app-domain';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
/**
* todo例程entity对象上满足filter条件的action进行处理的行创建一个todo
* @param entity
* @param filter
* @param action
* @param userIds
*/
export declare function createToDo<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends BackendRuntimeContext<ED>>(entity: T, filter: ED[T]['Selection']['filter'], action: ED[T]['Action'], context: Cxt, data: {
title: string;
description?: string;
redirectTo: EntityDict['toDo']['OpSchema']['redirectTo'];
}, userIds?: string[]): Promise<1 | 0>;
/**
* todo例程entity对象上进行action操作时filtertodo完成
* entity的action的后trigger中调用
* @param entity
* @param filter filter限定查询todo的范围todo中的targetFilter查找相同限制下的行toDo要保持一致action可能造成的数据变化
* @param action
* @param context
*/
export declare function completeToDo<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends BackendRuntimeContext<ED>>(entity: T, filter: ED[T]['Selection']['filter'], action: ED[T]['Action'], context: Cxt): Promise<number>;

111
es/triggers/toDo.js Normal file
View File

@ -0,0 +1,111 @@
import { uniq } from 'oak-domain/lib/utils/lodash';
import { getUserRelationsByActions } from 'oak-domain/lib/store/RelationAuth';
import { generateNewIdAsync } from 'oak-domain';
import assert from 'assert';
/**
* 创建todo例程为entity对象上满足filter条件的需要以action进行处理的行创建一个todo
* @param entity
* @param filter
* @param action
* @param userIds
*/
export async function createToDo(entity, filter, action, context, data, userIds) {
const count = await context.count('toDo', {
filter: {
targetEntity: entity,
targetFilter: JSON.stringify(filter),
action,
iState: 'active',
},
count: 1
}, {});
// 已有则无需创建
if (count > 0) {
return 0;
}
let userIds2 = userIds;
if (!userIds2) {
// 为对此对象有此action的用户创建userRelation
const { userRelations, userEntities } = await getUserRelationsByActions({
entity,
filter,
actions: [action],
}, context);
userIds2 = uniq(userRelations.map(ele => ele.userId).concat(userEntities.map(ele => ele.userId)));
}
const [relation] = await context.select('relation', {
data: {
id: 1,
},
filter: {
entity: 'toDo',
name: 'collaborator',
}
}, { dontCollect: true });
await context.operate('toDo', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
targetEntity: entity,
targetFilter: filter,
action,
...data,
userRelation$entity: await Promise.all(userIds2.map(async (userId) => ({
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
userId,
relationId: relation.id,
}
})))
},
}, { dontCollect: true });
return 1;
}
/**
* 完成todo例程当在entity对象上进行action操作时操作条件是filter将对应的todo完成
* 必须在entity的action的后trigger中调用
* @param entity
* @param filter 传入的filter限定查询todo的范围在todo中的targetFilter查找相同限制下的行和创建toDo要保持一致但要考虑action可能造成的数据变化
* @param action
* @param context
*/
export async function completeToDo(entity, filter, action, context) {
const toDos = await context.select('toDo', {
data: {
id: 1,
iState: 1,
targetEntity: 1,
targetFilter: 1,
action: 1,
},
filter: {
targetEntity: entity,
targetFilter: filter,
action,
}
}, {});
assert(toDos.length > 0, `${entity}相关的todo进行完成操作时找不到对应的数据。filter是${JSON.stringify(filter)}`);
let completed = 0;
for (const toDo of toDos) {
const { id, targetEntity, targetFilter, action } = toDo;
const count = await context.count(targetEntity, {
filter: targetFilter,
count: 1,
}, {});
if (count === 0) {
await context.operate('toDo', {
id: await generateNewIdAsync(),
action: 'complete',
data: {},
filter: {
id: id,
}
}, {});
completed++;
}
}
return completed;
}

View File

@ -1,19 +1,14 @@
import { String, Text } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
export type RedirectToProps = {
pathname: string;
props?: Record<string, any>;
state?: Record<string, any>;
};
export type Condition = {
condition?: any;
batchPath: string;
singlePath?: string;
};
export interface Schema extends EntityShape {
title: Text;
description?: Text;
targetEntity: String<32>;
targetEntityId?: String<64>;
condition?: Condition;
targetFilter: Object;
action: String<32>;
redirectTo: RedirectToProps;
}

View File

@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
;
const IActionDef = {
stm: {
done: ['active', 'done'],
complete: ['active', 'done'],
},
};
const entityDesc = {
@ -15,8 +15,7 @@ const entityDesc = {
title: '标题',
description: '描述',
targetEntity: '对象实体',
targetEntityId: '对象实体Id',
condition: '过滤条件',
targetFilter: '过滤条件',
action: '动作',
redirectTo: '重定向页面',
},
@ -24,7 +23,7 @@ const entityDesc = {
collaborator: '协作者',
},
action: {
done: '完成',
complete: '完成',
},
v: {
iState: {

View File

@ -1,7 +1,7 @@
import { Q_DateValue, Q_StringValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
import { OneOf } from "oak-domain/lib/types/Polyfill";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction } from "oak-domain/lib/types/Entity";
import { ReadOnlyAction } from "oak-domain/lib/actions/action";
import { GenericAction } from "oak-domain/lib/actions/action";
import { String } from "oak-domain/lib/types/DataType";
import { EntityShape } from "oak-domain/lib/types/Entity";
export type OpSchema = EntityShape & {
@ -91,7 +91,7 @@ export type I18nIdSubQuery = Selection<I18nIdProjection>;
export type EntityDef = {
Schema: Schema;
OpSchema: OpSchema;
Action: OakMakeAction<ReadOnlyAction> | string;
Action: OakMakeAction<GenericAction> | string;
Selection: Selection;
Aggregation: Aggregation;
Operation: Operation;

View File

@ -38,8 +38,8 @@ exports.desc = {
}
},
static: true,
actionType: "readOnly",
actions: action_1.readOnlyActions,
actionType: "crud",
actions: action_1.genericActions,
indexes: [
{
name: 'namespace_language',

View File

@ -1,7 +1,7 @@
import { ActionDef } from "oak-domain/lib/types/Action";
import { GenericAction } from "oak-domain/lib/actions/action";
export type IState = 'active' | 'done' | string;
export type IAction = 'done' | string;
export type IAction = 'complete' | string;
export type ParticularAction = IAction;
export declare const actions: string[];
export type Action = GenericAction | ParticularAction | string;

View File

@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.ActionDefDict = exports.actions = void 0;
const IActionDef = {
stm: {
done: ['active', 'done'],
complete: ['active', 'done'],
},
};
exports.actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "done"];
exports.actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "complete"];
exports.ActionDefDict = {
iState: IActionDef
};

View File

@ -9,19 +9,14 @@ import { EntityShape } from "oak-domain/lib/types/Entity";
import * as Relation from "../Relation/Schema";
import * as UserRelation from "../UserRelation/Schema";
export type RedirectToProps = {
pathname: string;
props?: Record<string, any>;
state?: Record<string, any>;
};
export type Condition = {
condition?: any;
batchPath: string;
singlePath?: string;
};
export type OpSchema = EntityShape & {
title: Text;
description?: Text | null;
targetEntity: String<32>;
targetEntityId?: String<64> | null;
condition?: Condition | null;
targetFilter: Object;
action: String<32>;
redirectTo: RedirectToProps;
iState?: IState | null;
@ -31,8 +26,7 @@ export type Schema = EntityShape & {
title: Text;
description?: Text | null;
targetEntity: String<32>;
targetEntityId?: String<64> | null;
condition?: Condition | null;
targetFilter: Object;
action: String<32>;
redirectTo: RedirectToProps;
iState?: IState | null;
@ -51,8 +45,7 @@ type AttrFilter = {
title: Q_StringValue;
description: Q_StringValue;
targetEntity: Q_StringValue;
targetEntityId: Q_StringValue;
condition: JsonFilter<Condition>;
targetFilter: Object;
action: Q_StringValue;
redirectTo: JsonFilter<RedirectToProps>;
iState: Q_EnumValue<IState>;
@ -70,8 +63,7 @@ export type Projection = {
title?: number;
description?: number;
targetEntity?: number;
targetEntityId?: number;
condition?: number | JsonProjection<Condition>;
targetFilter?: number | Object;
action?: number;
redirectTo?: number | JsonProjection<RedirectToProps>;
iState?: number;
@ -105,10 +97,6 @@ export type SortAttr = {
description: number;
} | {
targetEntity: number;
} | {
targetEntityId: number;
} | {
condition: number;
} | {
action: number;
} | {

View File

@ -18,13 +18,8 @@ exports.desc = {
length: 32
}
},
targetEntityId: {
type: "varchar",
params: {
length: 64
}
},
condition: {
targetFilter: {
notNull: true,
type: "object"
},
action: {

View File

@ -1 +1 @@
{ "name": "待办", "attr": { "iState": "状态", "title": "标题", "description": "描述", "targetEntity": "对象实体", "targetEntityId": "对象实体Id", "condition": "过滤条件", "action": "动作", "redirectTo": "重定向页面" }, "r": { "collaborator": "协作者" }, "action": { "done": "完成" }, "v": { "iState": { "active": "待办", "done": "已完成" } } }
{ "name": "待办", "attr": { "iState": "状态", "title": "标题", "description": "描述", "targetEntity": "对象实体", "targetFilter": "过滤条件", "action": "动作", "redirectTo": "重定向页面" }, "r": { "collaborator": "协作者" }, "action": { "complete": "完成" }, "v": { "iState": { "active": "待办", "done": "已完成" } } }

View File

@ -1,2 +1,2 @@
declare const _default: (import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "account", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>>)[];
declare const _default: (import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").RuntimeCxt> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain").Trigger<import("../oak-app-domain").EntityDict, "account", import("..").BackendRuntimeContext<import("../oak-app-domain").EntityDict>>)[];
export default _default;

24
lib/triggers/toDo.d.ts vendored Normal file
View File

@ -0,0 +1,24 @@
import { EntityDict } from '../oak-app-domain';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
/**
* todo例程entity对象上满足filter条件的action进行处理的行创建一个todo
* @param entity
* @param filter
* @param action
* @param userIds
*/
export declare function createToDo<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends BackendRuntimeContext<ED>>(entity: T, filter: ED[T]['Selection']['filter'], action: ED[T]['Action'], context: Cxt, data: {
title: string;
description?: string;
redirectTo: EntityDict['toDo']['OpSchema']['redirectTo'];
}, userIds?: string[]): Promise<0 | 1>;
/**
* todo例程entity对象上进行action操作时filtertodo完成
* entity的action的后trigger中调用
* @param entity
* @param filter filter限定查询todo的范围todo中的targetFilter查找相同限制下的行toDo要保持一致action可能造成的数据变化
* @param action
* @param context
*/
export declare function completeToDo<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends BackendRuntimeContext<ED>>(entity: T, filter: ED[T]['Selection']['filter'], action: ED[T]['Action'], context: Cxt): Promise<number>;

117
lib/triggers/toDo.js Normal file
View File

@ -0,0 +1,117 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.completeToDo = exports.createToDo = void 0;
const tslib_1 = require("tslib");
const lodash_1 = require("oak-domain/lib/utils/lodash");
const RelationAuth_1 = require("oak-domain/lib/store/RelationAuth");
const oak_domain_1 = require("oak-domain");
const assert_1 = tslib_1.__importDefault(require("assert"));
/**
* 创建todo例程为entity对象上满足filter条件的需要以action进行处理的行创建一个todo
* @param entity
* @param filter
* @param action
* @param userIds
*/
async function createToDo(entity, filter, action, context, data, userIds) {
const count = await context.count('toDo', {
filter: {
targetEntity: entity,
targetFilter: JSON.stringify(filter),
action,
iState: 'active',
},
count: 1
}, {});
// 已有则无需创建
if (count > 0) {
return 0;
}
let userIds2 = userIds;
if (!userIds2) {
// 为对此对象有此action的用户创建userRelation
const { userRelations, userEntities } = await (0, RelationAuth_1.getUserRelationsByActions)({
entity,
filter,
actions: [action],
}, context);
userIds2 = (0, lodash_1.uniq)(userRelations.map(ele => ele.userId).concat(userEntities.map(ele => ele.userId)));
}
const [relation] = await context.select('relation', {
data: {
id: 1,
},
filter: {
entity: 'toDo',
name: 'collaborator',
}
}, { dontCollect: true });
await context.operate('toDo', {
id: await (0, oak_domain_1.generateNewIdAsync)(),
action: 'create',
data: {
id: await (0, oak_domain_1.generateNewIdAsync)(),
targetEntity: entity,
targetFilter: filter,
action,
...data,
userRelation$entity: await Promise.all(userIds2.map(async (userId) => ({
id: await (0, oak_domain_1.generateNewIdAsync)(),
action: 'create',
data: {
id: await (0, oak_domain_1.generateNewIdAsync)(),
userId,
relationId: relation.id,
}
})))
},
}, { dontCollect: true });
return 1;
}
exports.createToDo = createToDo;
/**
* 完成todo例程当在entity对象上进行action操作时操作条件是filter将对应的todo完成
* 必须在entity的action的后trigger中调用
* @param entity
* @param filter 传入的filter限定查询todo的范围在todo中的targetFilter查找相同限制下的行和创建toDo要保持一致但要考虑action可能造成的数据变化
* @param action
* @param context
*/
async function completeToDo(entity, filter, action, context) {
const toDos = await context.select('toDo', {
data: {
id: 1,
iState: 1,
targetEntity: 1,
targetFilter: 1,
action: 1,
},
filter: {
targetEntity: entity,
targetFilter: filter,
action,
}
}, {});
(0, assert_1.default)(toDos.length > 0, `${entity}相关的todo进行完成操作时找不到对应的数据。filter是${JSON.stringify(filter)}`);
let completed = 0;
for (const toDo of toDos) {
const { id, targetEntity, targetFilter, action } = toDo;
const count = await context.count(targetEntity, {
filter: targetFilter,
count: 1,
}, {});
if (count === 0) {
await context.operate('toDo', {
id: await (0, oak_domain_1.generateNewIdAsync)(),
action: 'complete',
data: {},
filter: {
id: id,
}
}, {});
completed++;
}
}
return completed;
}
exports.completeToDo = completeToDo;

View File

@ -6,35 +6,28 @@ import { ActionDef } from 'oak-domain/lib/types/Action';
export type RedirectToProps = {
pathname: string;
props?: Record<string, any>;
state?: Record<string, any>;
};
export type Condition = {
condition?: any;
batchPath: string;
singlePath?: string;
};
export interface Schema extends EntityShape {
title: Text;
description?: Text;
targetEntity: String<32>;
targetEntityId?: String<64>;
condition?: Condition;
targetFilter: Object;
action: String<32>;
redirectTo: RedirectToProps;
};
type Relation = 'collaborator';
type IState = 'active' | 'done'
type IAction = 'done'; //触发器执行
type IAction = 'complete'; //触发器执行
const IActionDef: ActionDef<IAction, IState> = {
stm: {
done: ['active', 'done'],
complete: ['active', 'done'],
},
};
@ -57,8 +50,7 @@ const entityDesc: EntityDesc<
title: '标题',
description: '描述',
targetEntity: '对象实体',
targetEntityId: '对象实体Id',
condition: '过滤条件',
targetFilter: '过滤条件',
action: '动作',
redirectTo: '重定向页面',
},
@ -66,7 +58,7 @@ const entityDesc: EntityDesc<
collaborator: '协作者',
},
action: {
done: '完成',
complete: '完成',
},
v: {
iState: {

142
src/triggers/toDo.ts Normal file
View File

@ -0,0 +1,142 @@
import { EntityDict } from '../oak-app-domain';
import { uniq } from 'oak-domain/lib/utils/lodash';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { getUserRelationsByActions } from 'oak-domain/lib/store/RelationAuth';
import { generateNewIdAsync } from 'oak-domain';
import assert from 'assert';
/**
* todo例程entity对象上满足filter条件的action进行处理的行创建一个todo
* @param entity
* @param filter
* @param action
* @param userIds
*/
export async function createToDo<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends BackendRuntimeContext<ED>>(
entity: T,
filter: ED[T]['Selection']['filter'],
action: ED[T]['Action'],
context: Cxt,
data: {
title: string,
description?: string,
redirectTo: EntityDict['toDo']['OpSchema']['redirectTo'],
},
userIds?: string[]) {
const count = await context.count('toDo', {
filter: {
targetEntity: entity as string,
targetFilter: JSON.stringify(filter),
action,
iState: 'active',
},
count: 1
}, {});
// 已有则无需创建
if (count > 0) {
return 0;
}
let userIds2 = userIds;
if (!userIds2) {
// 为对此对象有此action的用户创建userRelation
const { userRelations, userEntities } = await getUserRelationsByActions({
entity,
filter,
actions: [action],
}, context);
userIds2 = uniq(userRelations.map(ele => ele.userId).concat(userEntities.map(ele => ele.userId)));
}
const [relation] = await context.select('relation', {
data: {
id: 1,
},
filter: {
entity: 'toDo',
name: 'collaborator',
}
}, { dontCollect: true });
await context.operate('toDo', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
targetEntity: entity as string,
targetFilter: filter,
action,
...data,
userRelation$entity:
await Promise.all(userIds2!.map(
async (userId) => ({
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
userId,
relationId: relation!.id,
}
}
)
))
},
}, { dontCollect: true });
return 1;
}
/**
* todo例程entity对象上进行action操作时filtertodo完成
* entity的action的后trigger中调用
* @param entity
* @param filter filter限定查询todo的范围todo中的targetFilter查找相同限制下的行toDo要保持一致action可能造成的数据变化
* @param action
* @param context
*/
export async function completeToDo<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends BackendRuntimeContext<ED>>(
entity: T,
filter: ED[T]['Selection']['filter'],
action: ED[T]['Action'],
context: Cxt) {
const toDos = await context.select('toDo', {
data: {
id: 1,
iState: 1,
targetEntity: 1,
targetFilter: 1,
action: 1,
},
filter: {
targetEntity: entity as string,
targetFilter: filter,
action,
}
}, {});
assert(toDos.length > 0, `${entity as string}相关的todo进行完成操作时找不到对应的数据。filter是${JSON.stringify(filter)}`);
let completed = 0;
for (const toDo of toDos) {
const { id, targetEntity, targetFilter, action } = toDo;
const count = await context.count(targetEntity!, {
filter: targetFilter,
count: 1,
}, {});
if (count === 0) {
await context.operate('toDo', {
id: await generateNewIdAsync(),
action: 'complete',
data: {},
filter: {
id: id!,
}
}, {});
completed ++;
}
}
return completed;
}