cabloy 5.1.248 → 5.1.250

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 (121) hide show
  1. package/.cabloy-version +1 -1
  2. package/.github/workflows/create-cabloy.yml +50 -3
  3. package/.vscode/settings.json +1 -2
  4. package/CHANGELOG.md +31 -0
  5. package/package.json +1 -7
  6. package/repo-agent-governance/managed-assets.json +12 -12
  7. package/repo-agent-governance/skills/cabloy-spec-execution/references/status-and-evidence.md +1 -1
  8. package/repo-agent-governance/skills/cabloy-spec-generation/references/canonical-spec-input.md +1 -1
  9. package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-specs-document-set.md +2 -2
  10. package/repo-agent-governance/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +1 -1
  11. package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.mjs +73 -22
  12. package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.test.mjs +98 -7
  13. package/repo-docs/ai/playbook-spec-execution.md +1 -1
  14. package/repo-docs/backend/crud-workflow.md +92 -1
  15. package/repo-docs/backend/dto-guide.md +16 -1
  16. package/repo-docs/backend/dto-infer-generation.md +23 -5
  17. package/repo-docs/backend/orm-aggregate-group-guide.md +32 -0
  18. package/repo-docs/backend/validation-guide.md +38 -2
  19. package/repo-e2e/specs/cabloy-basic.spec.ts +9 -2
  20. package/vona/package.original.json +1 -7
  21. package/vona/packages-cli/cli/package.json +1 -1
  22. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/aggregate/src/dto/{{resourceName}}AggregateRes.tsx_ +11 -0
  23. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/common/src/lib/{{resourceName}}Analysis.ts_ +14 -0
  24. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/common/test/{{resourceName}}Analysis.test.ts_ +81 -0
  25. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/group/src/dto/{{resourceName}}GroupResItem.tsx_ +11 -0
  26. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
  27. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
  28. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
  29. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/test/{{resourceName}}.test.ts_ +4 -0
  30. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/3-en-us.ts +2 -0
  31. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +2 -0
  32. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
  33. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
  34. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
  35. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/test/{{resourceName}}.test.ts_ +1 -1
  36. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/3-en-us.ts +2 -0
  37. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/4-zh-cn.ts +2 -0
  38. package/vona/packages-cli/cli-set-api/package.json +1 -1
  39. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.crudBasic.ts +2 -0
  40. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.crudStart.ts +2 -0
  41. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.ts +9 -1
  42. package/vona/packages-cli/cli-set-api/src/lib/crudAnalysis.ts +11 -0
  43. package/vona/packages-cli/cli-set-api/test/crudAnalysis.test.ts +385 -0
  44. package/vona/packages-utils/babel-plugin-vona-bean-module/package.json +1 -1
  45. package/vona/packages-vona/vona/package.json +1 -1
  46. package/vona/packages-vona/vona-core/package.json +1 -1
  47. package/vona/packages-vona/vona-mock/package.json +1 -1
  48. package/vona/pnpm-lock.yaml +76 -1008
  49. package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +18 -0
  50. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +2 -0
  51. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +2 -0
  52. package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +28 -1
  53. package/vona/src/suite/a-training/modules/training-student/src/dto/studentAggregateRes.tsx +11 -0
  54. package/vona/src/suite/a-training/modules/training-student/src/dto/studentGroupResItem.tsx +11 -0
  55. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectReq.tsx +2 -0
  56. package/vona/src/suite/a-training/modules/training-student/src/lib/studentAnalysis.ts +12 -0
  57. package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +22 -1
  58. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +4 -0
  59. package/vona/src/suite/a-training/modules/training-student/test/studentAnalysis.test.ts +422 -0
  60. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  61. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  62. package/vona/src/suite-vendor/a-captcha/modules/a-captcha/package.json +1 -1
  63. package/vona/src/suite-vendor/a-captcha/package.json +1 -1
  64. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  65. package/vona/src/suite-vendor/a-vona/modules/a-hmr/package.json +1 -1
  66. package/vona/src/suite-vendor/a-vona/modules/a-locale/package.json +1 -1
  67. package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
  68. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  69. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/utils.ts +1 -1
  70. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  71. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/.metadata/index.ts +26 -26
  72. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/aggregateResult.ts +53 -0
  73. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/controls.ts +163 -0
  74. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/execute.ts +49 -0
  75. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/index.ts +3 -0
  76. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/policy.ts +140 -0
  77. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_cache.ts +4 -2
  78. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_crud_inner.ts +8 -3
  79. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dto.ts +3 -0
  80. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoAggregate.ts +4 -1
  81. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoAnalysis.ts +34 -0
  82. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/index.ts +1 -0
  83. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/analysis.ts +137 -0
  84. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/index.ts +1 -0
  85. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsAggr.ts +1 -1
  86. package/vona/src/suite-vendor/a-vona/modules/a-orm/test/aggregateResult.test.ts +81 -0
  87. package/vona/src/suite-vendor/a-vona/modules/a-orm/test/analysis.test.ts +208 -0
  88. package/vona/src/suite-vendor/a-vona/modules/a-orm/test/analysis.types.test.ts +112 -0
  89. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  90. package/vona/src/suite-vendor/a-vona/modules/a-queue/package.json +1 -1
  91. package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/package.json +1 -1
  92. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  93. package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
  94. package/vona/src/suite-vendor/a-vona/modules/a-web/src/.metadata/index.ts +40 -40
  95. package/vona/src/suite-vendor/a-vona/modules/a-web/src/bean/pipe.filter.ts +44 -3
  96. package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/analysisFilter.ts +69 -0
  97. package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/decorator/arguments.ts +6 -4
  98. package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/index.ts +1 -0
  99. package/vona/src/suite-vendor/a-vona/modules/a-web/src/types/dto.ts +2 -1
  100. package/vona/src/suite-vendor/a-vona/modules/a-web/src/types/filter.ts +7 -2
  101. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  102. package/zova/package.original.json +0 -6
  103. package/zova/packages-cli/cli/package.json +2 -2
  104. package/zova/packages-cli/cli-set-front/package.json +2 -2
  105. package/zova/packages-utils/zova-jsx/package.json +2 -2
  106. package/zova/packages-utils/zova-vite/package.json +1 -1
  107. package/zova/packages-zova/zova/package.json +3 -3
  108. package/zova/packages-zova/zova-core/package.json +1 -1
  109. package/zova/pnpm-lock.yaml +11 -963
  110. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.header.tsx +4 -1
  111. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.tabs.tsx +2 -2
  112. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +16 -2
  113. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +201 -7
  114. package/zova/src/suite/a-training/modules/training-student/src/api/trainingStudent.ts +42 -0
  115. package/zova/src/suite/a-training/modules/training-student/src/apiSchema/trainingStudent.ts +10 -0
  116. package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
  117. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/package.json +1 -1
  118. package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
  119. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  120. package/zova/src/suite-vendor/a-zova/package.json +5 -5
  121. package/vona/.textlintrc.json +0 -19
@@ -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
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.174",
3
+ "version": "1.1.175",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
@@ -5,6 +5,7 @@ import { combineApiPathControllerAndActionRaw } from '@cabloy/utils';
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
 
8
+ import { renderCrudAnalysis } from '../crudAnalysis.ts';
8
9
  import { __ThisSetName__ } from '../this.ts';
9
10
 
10
11
  declare module '@cabloy/cli' {
@@ -55,6 +56,7 @@ export class CliToolsCrudBasic extends BeanCliBase {
55
56
  snippetsPath: 'tools/crudBasic/snippets',
56
57
  boilerplatePath: 'tools/crudBasic/boilerplate',
57
58
  });
59
+ await renderCrudAnalysis(this, targetDir, __ThisSetName__);
58
60
  // tools.metadata
59
61
  if (!argv.nometadata) {
60
62
  await this.helper.invokeCli([':tools:metadata', moduleName], { cwd: argv.projectPath });
@@ -5,6 +5,7 @@ import { combineApiPathControllerAndActionRaw } from '@cabloy/utils';
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
 
8
+ import { renderCrudAnalysis } from '../crudAnalysis.ts';
8
9
  import { __ThisSetName__ } from '../this.ts';
9
10
 
10
11
  declare module '@cabloy/cli' {
@@ -55,6 +56,7 @@ export class CliToolsCrudStart extends BeanCliBase {
55
56
  snippetsPath: 'tools/crudStart/snippets',
56
57
  boilerplatePath: 'tools/crudStart/boilerplate',
57
58
  });
59
+ await renderCrudAnalysis(this, targetDir, __ThisSetName__);
58
60
  // tools.metadata
59
61
  if (!argv.nometadata) {
60
62
  await this.helper.invokeCli([':tools:metadata', moduleName], { cwd: argv.projectPath });
@@ -613,6 +613,8 @@ export class CliToolsMasterDetail extends BeanCliBase {
613
613
  service: path.join(detailRoot, 'src/service', `${argv.detailResourceName}.ts`),
614
614
  dtoDir: path.join(detailRoot, 'src/dto'),
615
615
  test: path.join(detailRoot, 'test', `${argv.detailResourceName}.test.ts`),
616
+ analysisTest: path.join(detailRoot, 'test', `${argv.detailResourceName}Analysis.test.ts`),
617
+ analysis: path.join(detailRoot, 'src/lib', `${argv.detailResourceName}Analysis.ts`),
616
618
  metaVersion: path.join(detailRoot, 'src/bean', 'meta.version.ts'),
617
619
  metaIndex: path.join(detailRoot, 'src/bean', 'meta.index.ts'),
618
620
  };
@@ -627,7 +629,13 @@ export class CliToolsMasterDetail extends BeanCliBase {
627
629
 
628
630
  private _removeStandaloneDetailSurface() {
629
631
  const files = this._detailPaths();
630
- for (const file of [files.controller, files.service, files.test]) {
632
+ for (const file of [
633
+ files.controller,
634
+ files.service,
635
+ files.test,
636
+ files.analysisTest,
637
+ files.analysis,
638
+ ]) {
631
639
  if (fs.existsSync(file)) {
632
640
  fs.rmSync(file, { force: true });
633
641
  }
@@ -0,0 +1,11 @@
1
+ import type { BeanCliBase } from '@cabloy/cli';
2
+
3
+ export async function renderCrudAnalysis(cli: BeanCliBase, targetDir: string, setName: string) {
4
+ for (const part of ['common', 'group', 'aggregate']) {
5
+ await cli.template.renderBoilerplateAndSnippets({
6
+ targetDir,
7
+ setName,
8
+ boilerplatePath: `tools/crudAnalysis/${part}`,
9
+ });
10
+ }
11
+ }