vona-cli-set-api 1.1.173 → 1.1.175

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/cli/templates/tools/crudAnalysis/aggregate/src/dto/{{resourceName}}AggregateRes.tsx_ +11 -0
  2. package/cli/templates/tools/crudAnalysis/common/src/lib/{{resourceName}}Analysis.ts_ +14 -0
  3. package/cli/templates/tools/crudAnalysis/common/test/{{resourceName}}Analysis.test.ts_ +81 -0
  4. package/cli/templates/tools/crudAnalysis/group/src/dto/{{resourceName}}GroupResItem.tsx_ +11 -0
  5. package/cli/templates/tools/crudBasic/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
  6. package/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
  7. package/cli/templates/tools/crudBasic/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
  8. package/cli/templates/tools/crudBasic/boilerplate/test/{{resourceName}}.test.ts_ +4 -0
  9. package/cli/templates/tools/crudBasic/snippets/3-en-us.ts +2 -0
  10. package/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +2 -0
  11. package/cli/templates/tools/crudStart/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
  12. package/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
  13. package/cli/templates/tools/crudStart/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
  14. package/cli/templates/tools/crudStart/boilerplate/test/{{resourceName}}.test.ts_ +1 -1
  15. package/cli/templates/tools/crudStart/snippets/3-en-us.ts +2 -0
  16. package/cli/templates/tools/crudStart/snippets/4-zh-cn.ts +2 -0
  17. package/dist/index.js +15 -1
  18. package/dist/lib/crudAnalysis.d.ts +2 -0
  19. package/dist-cli/templates/tools/crudBasic/snippets/3-en-us.js +2 -0
  20. package/dist-cli/templates/tools/crudBasic/snippets/4-zh-cn.js +2 -0
  21. package/dist-cli/templates/tools/crudStart/snippets/3-en-us.js +2 -0
  22. package/dist-cli/templates/tools/crudStart/snippets/4-zh-cn.js +2 -0
  23. package/package.json +4 -4
@@ -0,0 +1,11 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+
3
+ import { $Dto } from 'vona-module-a-orm';
4
+ import { Dto } from 'vona-module-a-web';
5
+
6
+ import { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
7
+
8
+ export interface IDtoOptions<%=argv.resourceNameCapitalize%>AggregateRes extends IDecoratorDtoOptions {}
9
+
10
+ @Dto<IDtoOptions<%=argv.resourceNameCapitalize%>AggregateRes>()
11
+ export class Dto<%=argv.resourceNameCapitalize%>AggregateRes extends $Dto.analysisAggregate(<%=argv.resourceName%>Analysis) {}
@@ -0,0 +1,14 @@
1
+ import { $defineAnalysis } from 'vona-module-a-orm';
2
+
3
+ import { Model<%=argv.resourceNameCapitalize%> } from '../model/<%=argv.resourceName%>.ts';
4
+
5
+ // One allowlist drives request controls, response fields, and bounded execution.
6
+ export const <%=argv.resourceName%>Analysis = $defineAnalysis(() => Model<%=argv.resourceNameCapitalize%>, {
7
+ groups: ['name'],
8
+ aggrs: { count: ['*'] },
9
+ defaults: {
10
+ group: { groups: ['name'], aggrs: { count: ['*'] } },
11
+ aggregate: { aggrs: { count: ['*'] } },
12
+ },
13
+ limits: { maxGroups: 3, maxMetrics: 10, maxGroupRows: 1000 },
14
+ });
@@ -0,0 +1,81 @@
1
+ import assert from 'node:assert/strict';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { describe, it } from 'node:test';
4
+ import { app } from 'vona-mock';
5
+
6
+ import { <%=argv.resourceName%>Analysis } from '../src/lib/<%=argv.resourceName%>Analysis.ts';
7
+
8
+ describe('<%=argv.resourceName%>Analysis.test.ts', { concurrency: false }, () => {
9
+ it('returns default and requested-only string counts with validated controls', async () => {
10
+ await app.bean.executor.mockCtx(async () => {
11
+ const scope = app.scope('<%=argv.moduleInfo.relativeName%>');
12
+ const name = `__analysis_${randomUUID()}`;
13
+ const fixture = await scope.model.<%=argv.resourceName%>.insert({ name });
14
+ try {
15
+ await app.bean.passport.signinMock();
16
+ const queries = [
17
+ { name },
18
+ // List controls are validated but do not paginate the analysis population.
19
+ { where: { id: fixture.id }, columns: ['id'], orders: 'name,desc', pageNo: 2, pageSize: 1 },
20
+ ];
21
+ for (const query of queries) {
22
+ const groups = await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/group', { query });
23
+ assert.deepEqual(groups, [{ name, count_all: '1' }]);
24
+ assert.deepEqual(await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/group', {
25
+ query: { ...query, groups: JSON.stringify(['name']), aggrs: JSON.stringify({ count: ['*'] }) },
26
+ }), groups);
27
+ assert.deepEqual(await scope.service.<%=argv.resourceName%>.group({
28
+ where: { id: fixture.id }, groups: ['name'], aggrs: { count: ['*'] },
29
+ }), groups);
30
+ const aggregate = await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/aggregate', { query });
31
+ assert.deepEqual(aggregate, { count_all: '1' });
32
+ assert.deepEqual(await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/aggregate', {
33
+ query: { ...query, aggrs: JSON.stringify({ count: ['*'] }) },
34
+ }), aggregate);
35
+ assert.deepEqual(await scope.service.<%=argv.resourceName%>.aggregate({
36
+ where: { id: fixture.id }, aggrs: { count: ['*'] },
37
+ }), aggregate);
38
+ }
39
+ for (const action of ['group', 'aggregate'] as const) {
40
+ for (const controls of [
41
+ { pageNo: 2, pageSize: 0 },
42
+ { aggrs: JSON.stringify({}) },
43
+ { aggrs: JSON.stringify({ count: ['*', '*'] }) },
44
+ { aggrs: JSON.stringify({ count: ['unknown'] }) },
45
+ { aggrs: '{invalid-json' },
46
+ ]) {
47
+ await assert.rejects(() => app.bean.executor.performAction('get', `<%=argv.moduleActionPathRaw%>/${action}`, {
48
+ query: { name, ...controls },
49
+ }));
50
+ }
51
+ }
52
+ // The shared policy does not enable analysis controls on ordinary select.
53
+ await assert.rejects(() => app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>', {
54
+ query: { name, aggrs: JSON.stringify({ count: ['*'] }) },
55
+ }));
56
+ for (const groups of [[], ['name', 'name'], ['description'], ['unknown']]) {
57
+ await assert.rejects(() => app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/group', {
58
+ query: { name, groups: JSON.stringify(groups) },
59
+ }));
60
+ }
61
+ await assert.rejects(() => scope.service.<%=argv.resourceName%>.group({ aggrs: { count: [] } }));
62
+ assert.deepEqual(<%=argv.resourceName%>Analysis.defaults.group, {
63
+ groups: ['name'], aggrs: { count: ['*'] },
64
+ });
65
+ await assert.rejects(() => app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/aggregate', {
66
+ query: { name, groups: JSON.stringify(['name']) },
67
+ }));
68
+ await assert.rejects(() => scope.service.<%=argv.resourceName%>.aggregate({ aggrs: { count: [] } }));
69
+ assert.deepEqual(<%=argv.resourceName%>Analysis.defaults.aggregate, { aggrs: { count: ['*'] } });
70
+ await scope.model.<%=argv.resourceName%>.deleteById(fixture.id);
71
+ for (const query of queries) {
72
+ assert.deepEqual(await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/group', { query }), []);
73
+ assert.deepEqual(await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/aggregate', { query }), { count_all: '0' });
74
+ }
75
+ } finally {
76
+ await scope.model.<%=argv.resourceName%>.deleteById(fixture.id, { disableDeleted: true });
77
+ await app.bean.passport.signout();
78
+ }
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,11 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+
3
+ import { $Dto } from 'vona-module-a-orm';
4
+ import { Dto } from 'vona-module-a-web';
5
+
6
+ import { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
7
+
8
+ export interface IDtoOptions<%=argv.resourceNameCapitalize%>GroupResItem extends IDecoratorDtoOptions {}
9
+
10
+ @Dto<IDtoOptions<%=argv.resourceNameCapitalize%>GroupResItem>()
11
+ export class Dto<%=argv.resourceNameCapitalize%>GroupResItem extends $Dto.analysisGroup(<%=argv.resourceName%>Analysis) {}
@@ -1,5 +1,5 @@
1
1
  import type { TableIdentity } from 'table-identity';
2
- import type { IQueryParams } from 'vona-module-a-orm';
2
+ import type { IQueryParams, TypeAnalysisGroupParams, TypeAnalysisAggregateParams } from 'vona-module-a-orm';
3
3
  import type { IDecoratorControllerOptions } from 'vona-module-a-web';
4
4
 
5
5
  import { BeanBase } from 'vona';
@@ -17,6 +17,9 @@ import { Dto<%=argv.resourceNameCapitalize%>SelectReq } from '../dto/<%=argv.res
17
17
  import { Dto<%=argv.resourceNameCapitalize%>SelectRes } from '../dto/<%=argv.resourceName%>SelectRes.tsx';
18
18
  import { Dto<%=argv.resourceNameCapitalize%>Update } from '../dto/<%=argv.resourceName%>Update.tsx';
19
19
  import { Dto<%=argv.resourceNameCapitalize%>View } from '../dto/<%=argv.resourceName%>View.tsx';
20
+ import { Dto<%=argv.resourceNameCapitalize%>GroupResItem } from '../dto/<%=argv.resourceName%>GroupResItem.tsx';
21
+ import { Dto<%=argv.resourceNameCapitalize%>AggregateRes } from '../dto/<%=argv.resourceName%>AggregateRes.tsx';
22
+ import type { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
20
23
 
21
24
  export interface IControllerOptions<%=argv.resourceNameCapitalize%> extends IDecoratorControllerOptions {}
22
25
 
@@ -39,6 +42,20 @@ export class Controller<%=argv.resourceNameCapitalize%> extends BeanBase {
39
42
  return await this.scope.service.<%=argv.resourceName%>.select(params);
40
43
  }
41
44
 
45
+ @Web.get('group', { summary: $locale('<%=argv.resourceNameCapitalize%>Group') })
46
+ @Api.body(v.array(v.object(Dto<%=argv.resourceNameCapitalize%>GroupResItem)))
47
+ @Passport.rbac({ actionInherit: 'select' })
48
+ async group(@Arg.filter(Dto<%=argv.resourceNameCapitalize%>SelectReq, { mode: 'group' }) params: TypeAnalysisGroupParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>GroupResItem[]> {
49
+ return await this.scope.service.<%=argv.resourceName%>.group(params);
50
+ }
51
+
52
+ @Web.get('aggregate', { summary: $locale('<%=argv.resourceNameCapitalize%>Aggregate') })
53
+ @Api.body(Dto<%=argv.resourceNameCapitalize%>AggregateRes)
54
+ @Passport.rbac({ actionInherit: 'select' })
55
+ async aggregate(@Arg.filter(Dto<%=argv.resourceNameCapitalize%>SelectReq, { mode: 'aggregate' }) params: TypeAnalysisAggregateParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>AggregateRes> {
56
+ return await this.scope.service.<%=argv.resourceName%>.aggregate(params);
57
+ }
58
+
42
59
  @Web.get(':id', { summary: $locale('<%=argv.resourceNameCapitalize%>View') })
43
60
  @Api.body(v.optional(), v.object(Dto<%=argv.resourceNameCapitalize%>View))
44
61
  @Passport.rbac()
@@ -7,10 +7,12 @@ import z from 'zod';
7
7
  import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
8
8
 
9
9
  import { Entity<%=argv.resourceNameCapitalize%> } from '../entity/<%=argv.resourceName%>.tsx';
10
+ import { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
10
11
 
11
12
  export interface IDtoOptions<%=argv.resourceNameCapitalize%>SelectReq extends IDecoratorDtoOptions {}
12
13
 
13
14
  @Dto<IDtoOptions<%=argv.resourceNameCapitalize%>SelectReq>({
15
+ analysis: <%=argv.resourceName%>Analysis,
14
16
  openapi: { filter: { table: '<%=argv.moduleResourceName%>' } },
15
17
  fields: {
16
18
  name: $makeSchema(v.optional(), z.string()),
@@ -1,9 +1,10 @@
1
1
  import type { TableIdentity } from 'table-identity';
2
- import type { IQueryParams } from 'vona-module-a-orm';
2
+ import type { IQueryParams, TypeAnalysisGroupParams, TypeAnalysisAggregateParams } from 'vona-module-a-orm';
3
3
 
4
4
  import { BeanBase } from 'vona';
5
5
  import { Service } from 'vona-module-a-bean';
6
6
  import { Core } from 'vona-module-a-core';
7
+ import { $executeAnalysisGroup, $executeAnalysisAggregate } from 'vona-module-a-orm';
7
8
 
8
9
  import type { Dto<%=argv.resourceNameCapitalize%>Create } from '../dto/<%=argv.resourceName%>Create.tsx';
9
10
  import type { Dto<%=argv.resourceNameCapitalize%>SelectRes } from '../dto/<%=argv.resourceName%>SelectRes.tsx';
@@ -11,6 +12,10 @@ import type { Dto<%=argv.resourceNameCapitalize%>Update } from '../dto/<%=argv.r
11
12
  import type { Dto<%=argv.resourceNameCapitalize%>View } from '../dto/<%=argv.resourceName%>View.tsx';
12
13
  import type { Entity<%=argv.resourceNameCapitalize%> } from '../entity/<%=argv.resourceName%>.tsx';
13
14
  import type { Model<%=argv.resourceNameCapitalize%> } from '../model/<%=argv.resourceName%>.ts';
15
+ import type { Dto<%=argv.resourceNameCapitalize%>GroupResItem } from '../dto/<%=argv.resourceName%>GroupResItem.tsx';
16
+ import type { Dto<%=argv.resourceNameCapitalize%>AggregateRes } from '../dto/<%=argv.resourceName%>AggregateRes.tsx';
17
+
18
+ import { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
14
19
 
15
20
  @Service()
16
21
  export class Service<%=argv.resourceNameCapitalize%> extends BeanBase {
@@ -22,6 +27,14 @@ export class Service<%=argv.resourceNameCapitalize%> extends BeanBase {
22
27
  return await this.scope.model.<%=argv.resourceName%>.selectAndCount(params);
23
28
  }
24
29
 
30
+ async group(params?: TypeAnalysisGroupParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>GroupResItem[]> {
31
+ return await $executeAnalysisGroup(this.scope.model.<%=argv.resourceName%>, <%=argv.resourceName%>Analysis, params);
32
+ }
33
+
34
+ async aggregate(params?: TypeAnalysisAggregateParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>AggregateRes> {
35
+ return await $executeAnalysisAggregate(this.scope.model.<%=argv.resourceName%>, <%=argv.resourceName%>Analysis, params);
36
+ }
37
+
25
38
  async view(id: TableIdentity): Promise<Dto<%=argv.resourceNameCapitalize%>View | undefined> {
26
39
  return await this.scope.model.<%=argv.resourceName%>.getById(id);
27
40
  }
@@ -29,6 +29,8 @@ describe('<%=argv.resourceName%>.test.ts', { concurrency: false }, () => {
29
29
  const expectedSummaries = {
30
30
  create: ['post', moduleApiPath, 'Create <%=argv.resourceNameCapitalize%>'],
31
31
  select: ['get', moduleApiPath, 'Query <%=argv.resourceNameCapitalize%> List'],
32
+ group: ['get', `${moduleApiPath}/group`, 'Group <%=argv.resourceNameCapitalize%>'],
33
+ aggregate: ['get', `${moduleApiPath}/aggregate`, 'Aggregate <%=argv.resourceNameCapitalize%>'],
32
34
  view: ['get', `${moduleApiPath}/{id}`, 'View <%=argv.resourceNameCapitalize%>'],
33
35
  update: ['patch', `${moduleApiPath}/{id}`, 'Update <%=argv.resourceNameCapitalize%>'],
34
36
  delete: ['delete', `${moduleApiPath}/{id}`, 'Delete <%=argv.resourceNameCapitalize%>'],
@@ -54,6 +56,8 @@ describe('<%=argv.resourceName%>.test.ts', { concurrency: false }, () => {
54
56
  const expectedInherits = {
55
57
  create: undefined,
56
58
  select: undefined,
59
+ group: `${controllerBeanFullName}#select`,
60
+ aggregate: `${controllerBeanFullName}#select`,
57
61
  view: undefined,
58
62
  update: undefined,
59
63
  delete: undefined,
@@ -40,6 +40,8 @@ export default metadataCustomSnippet({
40
40
  [`${resource}Update`]: `Update ${resource}`,
41
41
  [`${resource}Delete`]: `Delete ${resource}`,
42
42
  [`${resource}DeleteBulk`]: `Bulk Delete ${resource}`,
43
+ [`${resource}Group`]: `Group ${resource}`,
44
+ [`${resource}Aggregate`]: `Aggregate ${resource}`,
43
45
  };
44
46
  return locale_transform({ ast, argv, resources });
45
47
  },
@@ -23,6 +23,8 @@ export default metadataCustomSnippet({
23
23
  [`${resource}Update`]: `更新${resource}`,
24
24
  [`${resource}Delete`]: `删除${resource}`,
25
25
  [`${resource}DeleteBulk`]: `批量删除${resource}`,
26
+ [`${resource}Group`]: `${resource}分组统计`,
27
+ [`${resource}Aggregate`]: `${resource}汇总统计`,
26
28
  };
27
29
  return locale_transform({ ast, argv, resources });
28
30
  },
@@ -1,5 +1,5 @@
1
1
  import type { TableIdentity } from 'table-identity';
2
- import type { IQueryParams } from 'vona-module-a-orm';
2
+ import type { IQueryParams, TypeAnalysisGroupParams, TypeAnalysisAggregateParams } from 'vona-module-a-orm';
3
3
  import type { IRbacScopeAccess } from 'vona-module-a-rbac';
4
4
  import type { IDecoratorControllerOptions } from 'vona-module-a-web';
5
5
 
@@ -18,6 +18,9 @@ import { Dto<%=argv.resourceNameCapitalize%>SelectReq } from '../dto/<%=argv.res
18
18
  import { Dto<%=argv.resourceNameCapitalize%>SelectRes } from '../dto/<%=argv.resourceName%>SelectRes.tsx';
19
19
  import { Dto<%=argv.resourceNameCapitalize%>Update } from '../dto/<%=argv.resourceName%>Update.tsx';
20
20
  import { Dto<%=argv.resourceNameCapitalize%>View } from '../dto/<%=argv.resourceName%>View.tsx';
21
+ import { Dto<%=argv.resourceNameCapitalize%>GroupResItem } from '../dto/<%=argv.resourceName%>GroupResItem.tsx';
22
+ import { Dto<%=argv.resourceNameCapitalize%>AggregateRes } from '../dto/<%=argv.resourceName%>AggregateRes.tsx';
23
+ import type { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
21
24
 
22
25
  export interface IControllerOptions<%=argv.resourceNameCapitalize%> extends IDecoratorControllerOptions {}
23
26
 
@@ -40,6 +43,20 @@ export class Controller<%=argv.resourceNameCapitalize%> extends BeanBase {
40
43
  return await this.scope.service.<%=argv.resourceName%>.select(params);
41
44
  }
42
45
 
46
+ @Web.get('group', { summary: $locale('<%=argv.resourceNameCapitalize%>Group') })
47
+ @Api.body(v.array(v.object(Dto<%=argv.resourceNameCapitalize%>GroupResItem)))
48
+ @Passport.rbac({ actionInherit: 'select' })
49
+ async group(@Arg.filter(Dto<%=argv.resourceNameCapitalize%>SelectReq, { mode: 'group' }) params: TypeAnalysisGroupParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>GroupResItem[]> {
50
+ return await this.scope.service.<%=argv.resourceName%>.group(params);
51
+ }
52
+
53
+ @Web.get('aggregate', { summary: $locale('<%=argv.resourceNameCapitalize%>Aggregate') })
54
+ @Api.body(Dto<%=argv.resourceNameCapitalize%>AggregateRes)
55
+ @Passport.rbac({ actionInherit: 'select' })
56
+ async aggregate(@Arg.filter(Dto<%=argv.resourceNameCapitalize%>SelectReq, { mode: 'aggregate' }) params: TypeAnalysisAggregateParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>AggregateRes> {
57
+ return await this.scope.service.<%=argv.resourceName%>.aggregate(params);
58
+ }
59
+
43
60
  @Web.get(':id', { summary: $locale('<%=argv.resourceNameCapitalize%>View') })
44
61
  @Api.body(v.optional(), v.object(Dto<%=argv.resourceNameCapitalize%>View))
45
62
  @Passport.rbac()
@@ -7,10 +7,12 @@ import z from 'zod';
7
7
  import { ZovaRender } from 'zova-rest-cabloy-start-admin';
8
8
 
9
9
  import { Entity<%=argv.resourceNameCapitalize%> } from '../entity/<%=argv.resourceName%>.tsx';
10
+ import { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
10
11
 
11
12
  export interface IDtoOptions<%=argv.resourceNameCapitalize%>SelectReq extends IDecoratorDtoOptions {}
12
13
 
13
14
  @Dto<IDtoOptions<%=argv.resourceNameCapitalize%>SelectReq>({
15
+ analysis: <%=argv.resourceName%>Analysis,
14
16
  openapi: { filter: { table: '<%=argv.moduleResourceName%>' } },
15
17
  fields: {
16
18
  name: $makeSchema(v.optional(), z.string()),
@@ -1,9 +1,10 @@
1
1
  import type { TableIdentity } from 'table-identity';
2
- import type { IQueryParams } from 'vona-module-a-orm';
2
+ import type { IQueryParams, TypeAnalysisGroupParams, TypeAnalysisAggregateParams } from 'vona-module-a-orm';
3
3
 
4
4
  import { BeanBase } from 'vona';
5
5
  import { Service } from 'vona-module-a-bean';
6
6
  import { Core } from 'vona-module-a-core';
7
+ import { $executeAnalysisGroup, $executeAnalysisAggregate } from 'vona-module-a-orm';
7
8
 
8
9
  import type { Dto<%=argv.resourceNameCapitalize%>Create } from '../dto/<%=argv.resourceName%>Create.tsx';
9
10
  import type { Dto<%=argv.resourceNameCapitalize%>SelectRes } from '../dto/<%=argv.resourceName%>SelectRes.tsx';
@@ -11,6 +12,10 @@ import type { Dto<%=argv.resourceNameCapitalize%>Update } from '../dto/<%=argv.r
11
12
  import type { Dto<%=argv.resourceNameCapitalize%>View } from '../dto/<%=argv.resourceName%>View.tsx';
12
13
  import type { Entity<%=argv.resourceNameCapitalize%> } from '../entity/<%=argv.resourceName%>.tsx';
13
14
  import type { Model<%=argv.resourceNameCapitalize%> } from '../model/<%=argv.resourceName%>.ts';
15
+ import type { Dto<%=argv.resourceNameCapitalize%>GroupResItem } from '../dto/<%=argv.resourceName%>GroupResItem.tsx';
16
+ import type { Dto<%=argv.resourceNameCapitalize%>AggregateRes } from '../dto/<%=argv.resourceName%>AggregateRes.tsx';
17
+
18
+ import { <%=argv.resourceName%>Analysis } from '../lib/<%=argv.resourceName%>Analysis.ts';
14
19
 
15
20
  @Service()
16
21
  export class Service<%=argv.resourceNameCapitalize%> extends BeanBase {
@@ -22,6 +27,14 @@ export class Service<%=argv.resourceNameCapitalize%> extends BeanBase {
22
27
  return await this.scope.model.<%=argv.resourceName%>.selectAndCount(params);
23
28
  }
24
29
 
30
+ async group(params?: TypeAnalysisGroupParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>GroupResItem[]> {
31
+ return await $executeAnalysisGroup(this.scope.model.<%=argv.resourceName%>, <%=argv.resourceName%>Analysis, params);
32
+ }
33
+
34
+ async aggregate(params?: TypeAnalysisAggregateParams<typeof <%=argv.resourceName%>Analysis>): Promise<Dto<%=argv.resourceNameCapitalize%>AggregateRes> {
35
+ return await $executeAnalysisAggregate(this.scope.model.<%=argv.resourceName%>, <%=argv.resourceName%>Analysis, params);
36
+ }
37
+
25
38
  async view(id: TableIdentity): Promise<Dto<%=argv.resourceNameCapitalize%>View | undefined> {
26
39
  return await this.scope.model.<%=argv.resourceName%>.getById(id);
27
40
  }
@@ -19,7 +19,7 @@ describe('<%=argv.resourceName%>.test.ts', () => {
19
19
  await app.bean.passport.signinMock();
20
20
  try {
21
21
  app.bean.passport.current!.roles = [];
22
- const actions = ['create', 'select', 'view', 'update', 'delete'];
22
+ const actions = ['create', 'select', 'view', 'update', 'delete', 'group', 'aggregate'];
23
23
  const permissions = await Promise.all(
24
24
  actions.map(action =>
25
25
  app.bean.permission.retrievePermissionAction(
@@ -40,6 +40,8 @@ export default metadataCustomSnippet({
40
40
  [`${resource}View`]: `View ${resource}`,
41
41
  [`${resource}Update`]: `Update ${resource}`,
42
42
  [`${resource}Delete`]: `Delete ${resource}`,
43
+ [`${resource}Group`]: `Group ${resource}`,
44
+ [`${resource}Aggregate`]: `Aggregate ${resource}`,
43
45
  };
44
46
  return locale_transform({ ast, argv, resources });
45
47
  },
@@ -23,6 +23,8 @@ export default metadataCustomSnippet({
23
23
  [`${resource}View`]: `查看${resource}`,
24
24
  [`${resource}Update`]: `更新${resource}`,
25
25
  [`${resource}Delete`]: `删除${resource}`,
26
+ [`${resource}Group`]: `${resource}分组统计`,
27
+ [`${resource}Aggregate`]: `${resource}汇总统计`,
26
28
  };
27
29
  return locale_transform({ ast, argv, resources });
28
30
  },
package/dist/index.js CHANGED
@@ -1968,6 +1968,16 @@ class CliToolsCrud extends BeanCliBase {
1968
1968
  }
1969
1969
  }
1970
1970
 
1971
+ async function renderCrudAnalysis(cli, targetDir, setName) {
1972
+ for (const part of ['common', 'group', 'aggregate']) {
1973
+ await cli.template.renderBoilerplateAndSnippets({
1974
+ targetDir,
1975
+ setName,
1976
+ boilerplatePath: `tools/crudAnalysis/${part}`
1977
+ });
1978
+ }
1979
+ }
1980
+
1971
1981
  class CliToolsCrudBasic extends BeanCliBase {
1972
1982
  async execute() {
1973
1983
  const {
@@ -1999,6 +2009,7 @@ class CliToolsCrudBasic extends BeanCliBase {
1999
2009
  snippetsPath: 'tools/crudBasic/snippets',
2000
2010
  boilerplatePath: 'tools/crudBasic/boilerplate'
2001
2011
  });
2012
+ await renderCrudAnalysis(this, targetDir, __ThisSetName__);
2002
2013
  // tools.metadata
2003
2014
  if (!argv.nometadata) {
2004
2015
  await this.helper.invokeCli([':tools:metadata', moduleName], {
@@ -2039,6 +2050,7 @@ class CliToolsCrudStart extends BeanCliBase {
2039
2050
  snippetsPath: 'tools/crudStart/snippets',
2040
2051
  boilerplatePath: 'tools/crudStart/boilerplate'
2041
2052
  });
2053
+ await renderCrudAnalysis(this, targetDir, __ThisSetName__);
2042
2054
  // tools.metadata
2043
2055
  if (!argv.nometadata) {
2044
2056
  await this.helper.invokeCli([':tools:metadata', moduleName], {
@@ -2662,6 +2674,8 @@ class CliToolsMasterDetail extends BeanCliBase {
2662
2674
  service: path.join(detailRoot, 'src/service', `${argv.detailResourceName}.ts`),
2663
2675
  dtoDir: path.join(detailRoot, 'src/dto'),
2664
2676
  test: path.join(detailRoot, 'test', `${argv.detailResourceName}.test.ts`),
2677
+ analysisTest: path.join(detailRoot, 'test', `${argv.detailResourceName}Analysis.test.ts`),
2678
+ analysis: path.join(detailRoot, 'src/lib', `${argv.detailResourceName}Analysis.ts`),
2665
2679
  metaVersion: path.join(detailRoot, 'src/bean', 'meta.version.ts'),
2666
2680
  metaIndex: path.join(detailRoot, 'src/bean', 'meta.index.ts')
2667
2681
  };
@@ -2672,7 +2686,7 @@ class CliToolsMasterDetail extends BeanCliBase {
2672
2686
  }
2673
2687
  _removeStandaloneDetailSurface() {
2674
2688
  const files = this._detailPaths();
2675
- for (const file of [files.controller, files.service, files.test]) {
2689
+ for (const file of [files.controller, files.service, files.test, files.analysisTest, files.analysis]) {
2676
2690
  if (fs.existsSync(file)) {
2677
2691
  fs.rmSync(file, {
2678
2692
  force: true
@@ -0,0 +1,2 @@
1
+ import type { BeanCliBase } from '@cabloy/cli';
2
+ export declare function renderCrudAnalysis(cli: BeanCliBase, targetDir: string, setName: string): Promise<void>;
@@ -30,6 +30,8 @@ export default metadataCustomSnippet({
30
30
  [`${resource}Update`]: `Update ${resource}`,
31
31
  [`${resource}Delete`]: `Delete ${resource}`,
32
32
  [`${resource}DeleteBulk`]: `Bulk Delete ${resource}`,
33
+ [`${resource}Group`]: `Group ${resource}`,
34
+ [`${resource}Aggregate`]: `Aggregate ${resource}`,
33
35
  };
34
36
  return locale_transform({ ast, argv, resources });
35
37
  },
@@ -20,6 +20,8 @@ export default metadataCustomSnippet({
20
20
  [`${resource}Update`]: `更新${resource}`,
21
21
  [`${resource}Delete`]: `删除${resource}`,
22
22
  [`${resource}DeleteBulk`]: `批量删除${resource}`,
23
+ [`${resource}Group`]: `${resource}分组统计`,
24
+ [`${resource}Aggregate`]: `${resource}汇总统计`,
23
25
  };
24
26
  return locale_transform({ ast, argv, resources });
25
27
  },
@@ -30,6 +30,8 @@ export default metadataCustomSnippet({
30
30
  [`${resource}View`]: `View ${resource}`,
31
31
  [`${resource}Update`]: `Update ${resource}`,
32
32
  [`${resource}Delete`]: `Delete ${resource}`,
33
+ [`${resource}Group`]: `Group ${resource}`,
34
+ [`${resource}Aggregate`]: `Aggregate ${resource}`,
33
35
  };
34
36
  return locale_transform({ ast, argv, resources });
35
37
  },
@@ -20,6 +20,8 @@ export default metadataCustomSnippet({
20
20
  [`${resource}View`]: `查看${resource}`,
21
21
  [`${resource}Update`]: `更新${resource}`,
22
22
  [`${resource}Delete`]: `删除${resource}`,
23
+ [`${resource}Group`]: `${resource}分组统计`,
24
+ [`${resource}Aggregate`]: `${resource}汇总统计`,
23
25
  };
24
26
  return locale_transform({ ast, argv, resources });
25
27
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.173",
4
- "gitHead": "92441b184c11319b1f477f9bb6fdb1e5e47404ed",
3
+ "version": "1.1.175",
4
+ "gitHead": "b0a4dbbda3b804afdfacf0792ad37a6e45724349",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
7
7
  "framework",
@@ -63,7 +63,7 @@
63
63
  "@rollup/plugin-typescript": "^12.3.0",
64
64
  "@swc-node/register": "^1.12.1",
65
65
  "babel-plugin-transform-typescript-metadata": "^0.3.2",
66
- "babel-plugin-vona-bean-module": "^1.1.10",
66
+ "babel-plugin-vona-bean-module": "^1.1.11",
67
67
  "chalk": "^3.0.0",
68
68
  "cli-table3": "^0.6.5",
69
69
  "compressing": "^1.10.4",
@@ -77,7 +77,7 @@
77
77
  "rollup": "^4.60.1",
78
78
  "urllib": "^4.9.0",
79
79
  "uuid": "^11.1.0",
80
- "vona-core": "^5.1.53",
80
+ "vona-core": "^5.1.55",
81
81
  "why-is-node-running": "^3.2.2",
82
82
  "yargs-parser": "^22.0.0"
83
83
  }