import { OperateOption, EntityDict, SelectOption, OakUnloggedInException, OakUserUnpermittedException, StorageSchema, } from 'oak-domain/lib/types'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; import { judgeRelation } from 'oak-domain/lib/store/relation'; import { assert } from 'oak-domain/lib/utils/assert'; export async function operate< ED extends BaseEntityDict & EntityDict, T extends keyof ED, Cxt extends AsyncContext, OP extends OperateOption >( params: { entity: T; operation: ED[T]['Operation'] | ED[T]['Operation'][]; option?: OP; }, context: Cxt ) { const { entity, operation, option } = params; /* const userId = context.getCurrentUserId(); if (!userId) { // operate默认必须用户登录 throw new OakUnloggedInException(); } */ if (!context.allowUserUpdate()) { throw new OakUserUnpermittedException('您被禁更新'); } if (operation instanceof Array) { const result = []; for (const oper of operation) { const r = await context.operate( entity, oper, option || {} ); result.push(r); } return result; } return await context.operate( entity, operation, option || {} ); } /** * 因为有cascadeSelect,这里要按查询的要求build返回的结构树,告知每一层上相关的id/total/aggr信息 * @param schema * @param entity * @param result * @returns */ function buildResultTree( schema: StorageSchema, entity: T, result: Partial[] ) { const pruneInner = (e: keyof ED, r: Partial, tree: Record) => { for (const attr in r) { if (attr.endsWith('$$aggr')) { tree[attr] = r[attr]; } else if (typeof r[attr] === 'object') { const rel = judgeRelation(schema, e, attr); if (rel === 2 || typeof rel === 'string') { tree[attr] = {}; pruneInner(rel === 2 ? attr : rel, r[attr]!, tree[attr]); } else if (rel instanceof Array) { assert(r[attr] as any instanceof Array); tree[attr] = { data: {}, }; if (r[attr].hasOwnProperty('#total')) { tree[attr].total = r[attr]['#total']; } r[attr].forEach( (rr: any) => { tree[attr].data[rr.id as string] = {}; pruneInner(rel[0], rr, tree[attr].data[rr.id as string]); } ) } } } }; const root = { data: {}, } as { data: Record; total?: number; } /** * 这个total是在cascadeStore的selectAsync处理的,有点古怪 */ if (result.hasOwnProperty('#total')) { root.total = (result as any)['#total']; } result.forEach( (row) => { root.data[row.id!] = {}; pruneInner(entity, row, root.data[row.id!]); } ); return root; } export async function select< ED extends BaseEntityDict & EntityDict, T extends keyof ED, Cxt extends AsyncContext, OP extends SelectOption >( params: { entity: T; selection: ED[T]['Selection']; option?: OP; getCount?: true; maxCount?: number; }, context: Cxt ) { const { entity, selection, option } = params; let selection2 = selection; const data = await context.select( entity, selection2, option || {} ); return buildResultTree(context.getSchema(), entity, data); } export async function aggregate< ED extends BaseEntityDict & EntityDict, T extends keyof ED, Cxt extends AsyncContext, OP extends SelectOption >(params: { entity: T, aggregation: ED[T]['Aggregation'], option?: OP, }, context: Cxt) { const { entity, aggregation, option } = params; return context.aggregate(entity, aggregation, option || {}); } export async function fetchRows< ED extends EntityDict & BaseEntityDict, OP extends SelectOption, Cxt extends AsyncContext, >( params: Array<{ entity: keyof ED; selection: ED[keyof ED]['Selection']; option?: OP; }>, context: Cxt ) { await Promise.all( params.map((ele) => context.select( ele.entity, ele.selection, ele.option || {} ) ) ); } export async function count< ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext, OP extends SelectOption >( params: { entity: T; selection: Pick; option?: OP; }, context: Cxt ) { const { entity, selection, option } = params; const { filter } = selection; const count = await context.count( entity, Object.assign({}, { filter }), option || {} ); return count; } /* export type AspectDict = { operation: (options: { entity: T, operation: ED[T]['Operation'], params?: OperateParams }, context: Context) => Promise; select: ( options: { entity: T, selection: S, params?: object }, context: Context) => Promise>; }; */