diff --git a/es/aspects/sms.js b/es/aspects/sms.js index 8d62ce21d..f40d39509 100644 --- a/es/aspects/sms.js +++ b/es/aspects/sms.js @@ -51,6 +51,7 @@ export async function syncSmsTemplate(params, context) { id: await generateNewIdAsync(), origin, systemId, + templateCode, templateName, templateContent, syncAt: Date.now(), diff --git a/es/checkers/index.d.ts b/es/checkers/index.d.ts index 1c141fe16..7669a44a6 100644 --- a/es/checkers/index.d.ts +++ b/es/checkers/index.d.ts @@ -1,2 +1,2 @@ -declare const checkers: (import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker)[]; +declare const checkers: (import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker)[]; export default checkers; diff --git a/es/components/extraFile/commit/index.d.ts b/es/components/extraFile/commit/index.d.ts index 2be033b31..9ad649b09 100644 --- a/es/components/extraFile/commit/index.d.ts +++ b/es/components/extraFile/commit/index.d.ts @@ -12,9 +12,9 @@ declare const _default: ; - state?: Record; -}; -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; } diff --git a/es/entities/ToDo.js b/es/entities/ToDo.js index 438f800ce..c022e6d2c 100644 --- a/es/entities/ToDo.js +++ b/es/entities/ToDo.js @@ -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: { diff --git a/es/oak-app-domain/I18n/Schema.d.ts b/es/oak-app-domain/I18n/Schema.d.ts index 93a316895..f90f70de1 100644 --- a/es/oak-app-domain/I18n/Schema.d.ts +++ b/es/oak-app-domain/I18n/Schema.d.ts @@ -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; export type EntityDef = { Schema: Schema; OpSchema: OpSchema; - Action: OakMakeAction | string; + Action: OakMakeAction | string; Selection: Selection; Aggregation: Aggregation; Operation: Operation; diff --git a/es/oak-app-domain/I18n/Storage.js b/es/oak-app-domain/I18n/Storage.js index 9c4d8f7cf..fdd6b8711 100644 --- a/es/oak-app-domain/I18n/Storage.js +++ b/es/oak-app-domain/I18n/Storage.js @@ -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: [ { diff --git a/es/oak-app-domain/ToDo/Action.d.ts b/es/oak-app-domain/ToDo/Action.d.ts index 750369aa2..8390a55e7 100644 --- a/es/oak-app-domain/ToDo/Action.d.ts +++ b/es/oak-app-domain/ToDo/Action.d.ts @@ -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; diff --git a/es/oak-app-domain/ToDo/Action.js b/es/oak-app-domain/ToDo/Action.js index 97cf77457..1e32c0e81 100644 --- a/es/oak-app-domain/ToDo/Action.js +++ b/es/oak-app-domain/ToDo/Action.js @@ -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 }; diff --git a/es/oak-app-domain/ToDo/Schema.d.ts b/es/oak-app-domain/ToDo/Schema.d.ts index 65835b978..d1c1c614a 100644 --- a/es/oak-app-domain/ToDo/Schema.d.ts +++ b/es/oak-app-domain/ToDo/Schema.d.ts @@ -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; - state?: Record; -}; -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; + targetFilter: Object; action: Q_StringValue; redirectTo: JsonFilter; iState: Q_EnumValue; @@ -70,8 +63,7 @@ export type Projection = { title?: number; description?: number; targetEntity?: number; - targetEntityId?: number; - condition?: number | JsonProjection; + targetFilter?: number | Object; action?: number; redirectTo?: number | JsonProjection; iState?: number; @@ -105,10 +97,6 @@ export type SortAttr = { description: number; } | { targetEntity: number; -} | { - targetEntityId: number; -} | { - condition: number; } | { action: number; } | { diff --git a/es/oak-app-domain/ToDo/Storage.js b/es/oak-app-domain/ToDo/Storage.js index 042ab2bca..bacac5409 100644 --- a/es/oak-app-domain/ToDo/Storage.js +++ b/es/oak-app-domain/ToDo/Storage.js @@ -15,13 +15,8 @@ export const desc = { length: 32 } }, - targetEntityId: { - type: "varchar", - params: { - length: 64 - } - }, - condition: { + targetFilter: { + notNull: true, type: "object" }, action: { diff --git a/es/oak-app-domain/ToDo/locales/zh_CN.json b/es/oak-app-domain/ToDo/locales/zh_CN.json index bf53e5bee..ce58e6ff9 100644 --- a/es/oak-app-domain/ToDo/locales/zh_CN.json +++ b/es/oak-app-domain/ToDo/locales/zh_CN.json @@ -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": "已完成" } } } diff --git a/es/triggers/index.d.ts b/es/triggers/index.d.ts index 0770eae70..487031783 100644 --- a/es/triggers/index.d.ts +++ b/es/triggers/index.d.ts @@ -1,2 +1,2 @@ -declare const _default: (import("oak-domain").Trigger> | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger>)[]; +declare const _default: (import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger>)[]; export default _default; diff --git a/es/triggers/toDo.d.ts b/es/triggers/toDo.d.ts new file mode 100644 index 000000000..95d028df6 --- /dev/null +++ b/es/triggers/toDo.d.ts @@ -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>(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操作时(操作条件是filter),将对应的todo完成 + * 必须在entity的action的后trigger中调用 + * @param entity + * @param filter 传入的filter限定查询todo的范围,在todo中的targetFilter查找相同限制下的行,和创建toDo要保持一致(但要考虑action可能造成的数据变化) + * @param action + * @param context + */ +export declare function completeToDo>(entity: T, filter: ED[T]['Selection']['filter'], action: ED[T]['Action'], context: Cxt): Promise; diff --git a/es/triggers/toDo.js b/es/triggers/toDo.js new file mode 100644 index 000000000..38546cab5 --- /dev/null +++ b/es/triggers/toDo.js @@ -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; +} diff --git a/lib/checkers/index.d.ts b/lib/checkers/index.d.ts index 1c141fe16..46b6c8266 100644 --- a/lib/checkers/index.d.ts +++ b/lib/checkers/index.d.ts @@ -1,2 +1,2 @@ -declare const checkers: (import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker)[]; +declare const checkers: (import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker | import("oak-domain").Checker)[]; export default checkers; diff --git a/lib/entities/ToDo.d.ts b/lib/entities/ToDo.d.ts index 538698f55..a86c95b9a 100644 --- a/lib/entities/ToDo.d.ts +++ b/lib/entities/ToDo.d.ts @@ -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; - state?: Record; -}; -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; } diff --git a/lib/entities/ToDo.js b/lib/entities/ToDo.js index 9463e4b9d..2508f4863 100644 --- a/lib/entities/ToDo.js +++ b/lib/entities/ToDo.js @@ -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: { diff --git a/lib/oak-app-domain/I18n/Schema.d.ts b/lib/oak-app-domain/I18n/Schema.d.ts index 93a316895..f90f70de1 100644 --- a/lib/oak-app-domain/I18n/Schema.d.ts +++ b/lib/oak-app-domain/I18n/Schema.d.ts @@ -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; export type EntityDef = { Schema: Schema; OpSchema: OpSchema; - Action: OakMakeAction | string; + Action: OakMakeAction | string; Selection: Selection; Aggregation: Aggregation; Operation: Operation; diff --git a/lib/oak-app-domain/I18n/Storage.js b/lib/oak-app-domain/I18n/Storage.js index dbcbf61a9..5e5fed31d 100644 --- a/lib/oak-app-domain/I18n/Storage.js +++ b/lib/oak-app-domain/I18n/Storage.js @@ -38,8 +38,8 @@ exports.desc = { } }, static: true, - actionType: "readOnly", - actions: action_1.readOnlyActions, + actionType: "crud", + actions: action_1.genericActions, indexes: [ { name: 'namespace_language', diff --git a/lib/oak-app-domain/ToDo/Action.d.ts b/lib/oak-app-domain/ToDo/Action.d.ts index 750369aa2..8390a55e7 100644 --- a/lib/oak-app-domain/ToDo/Action.d.ts +++ b/lib/oak-app-domain/ToDo/Action.d.ts @@ -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; diff --git a/lib/oak-app-domain/ToDo/Action.js b/lib/oak-app-domain/ToDo/Action.js index fcfda9af8..7772e656a 100644 --- a/lib/oak-app-domain/ToDo/Action.js +++ b/lib/oak-app-domain/ToDo/Action.js @@ -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 }; diff --git a/lib/oak-app-domain/ToDo/Schema.d.ts b/lib/oak-app-domain/ToDo/Schema.d.ts index 65835b978..d1c1c614a 100644 --- a/lib/oak-app-domain/ToDo/Schema.d.ts +++ b/lib/oak-app-domain/ToDo/Schema.d.ts @@ -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; - state?: Record; -}; -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; + targetFilter: Object; action: Q_StringValue; redirectTo: JsonFilter; iState: Q_EnumValue; @@ -70,8 +63,7 @@ export type Projection = { title?: number; description?: number; targetEntity?: number; - targetEntityId?: number; - condition?: number | JsonProjection; + targetFilter?: number | Object; action?: number; redirectTo?: number | JsonProjection; iState?: number; @@ -105,10 +97,6 @@ export type SortAttr = { description: number; } | { targetEntity: number; -} | { - targetEntityId: number; -} | { - condition: number; } | { action: number; } | { diff --git a/lib/oak-app-domain/ToDo/Storage.js b/lib/oak-app-domain/ToDo/Storage.js index dd637cdd6..c4b30664c 100644 --- a/lib/oak-app-domain/ToDo/Storage.js +++ b/lib/oak-app-domain/ToDo/Storage.js @@ -18,13 +18,8 @@ exports.desc = { length: 32 } }, - targetEntityId: { - type: "varchar", - params: { - length: 64 - } - }, - condition: { + targetFilter: { + notNull: true, type: "object" }, action: { diff --git a/lib/oak-app-domain/ToDo/locales/zh_CN.json b/lib/oak-app-domain/ToDo/locales/zh_CN.json index bf53e5bee..ce58e6ff9 100644 --- a/lib/oak-app-domain/ToDo/locales/zh_CN.json +++ b/lib/oak-app-domain/ToDo/locales/zh_CN.json @@ -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": "已完成" } } } diff --git a/lib/triggers/index.d.ts b/lib/triggers/index.d.ts index 0770eae70..6fb3c0b29 100644 --- a/lib/triggers/index.d.ts +++ b/lib/triggers/index.d.ts @@ -1,2 +1,2 @@ -declare const _default: (import("oak-domain").Trigger> | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger>)[]; +declare const _default: (import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger | import("oak-domain").Trigger | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger> | import("oak-domain").Trigger>)[]; export default _default; diff --git a/lib/triggers/toDo.d.ts b/lib/triggers/toDo.d.ts new file mode 100644 index 000000000..95d028df6 --- /dev/null +++ b/lib/triggers/toDo.d.ts @@ -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>(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操作时(操作条件是filter),将对应的todo完成 + * 必须在entity的action的后trigger中调用 + * @param entity + * @param filter 传入的filter限定查询todo的范围,在todo中的targetFilter查找相同限制下的行,和创建toDo要保持一致(但要考虑action可能造成的数据变化) + * @param action + * @param context + */ +export declare function completeToDo>(entity: T, filter: ED[T]['Selection']['filter'], action: ED[T]['Action'], context: Cxt): Promise; diff --git a/lib/triggers/toDo.js b/lib/triggers/toDo.js new file mode 100644 index 000000000..713f79d4c --- /dev/null +++ b/lib/triggers/toDo.js @@ -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; diff --git a/src/entities/ToDo.ts b/src/entities/ToDo.ts index 93a11ef41..6dd7658b5 100644 --- a/src/entities/ToDo.ts +++ b/src/entities/ToDo.ts @@ -6,35 +6,28 @@ import { ActionDef } from 'oak-domain/lib/types/Action'; export type RedirectToProps = { - pathname: string; - props?: Record; - state?: Record; -}; - -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 = { 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: { diff --git a/src/triggers/toDo.ts b/src/triggers/toDo.ts new file mode 100644 index 000000000..87ae8eb6b --- /dev/null +++ b/src/triggers/toDo.ts @@ -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>( + 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操作时(操作条件是filter),将对应的todo完成 + * 必须在entity的action的后trigger中调用 + * @param entity + * @param filter 传入的filter限定查询todo的范围,在todo中的targetFilter查找相同限制下的行,和创建toDo要保持一致(但要考虑action可能造成的数据变化) + * @param action + * @param context + */ +export async function completeToDo>( + 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; +} \ No newline at end of file