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.
- package/cli/templates/tools/crudAnalysis/aggregate/src/dto/{{resourceName}}AggregateRes.tsx_ +11 -0
- package/cli/templates/tools/crudAnalysis/common/src/lib/{{resourceName}}Analysis.ts_ +14 -0
- package/cli/templates/tools/crudAnalysis/common/test/{{resourceName}}Analysis.test.ts_ +81 -0
- package/cli/templates/tools/crudAnalysis/group/src/dto/{{resourceName}}GroupResItem.tsx_ +11 -0
- package/cli/templates/tools/crudBasic/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
- package/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
- package/cli/templates/tools/crudBasic/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
- package/cli/templates/tools/crudBasic/boilerplate/test/{{resourceName}}.test.ts_ +4 -0
- package/cli/templates/tools/crudBasic/snippets/3-en-us.ts +2 -0
- package/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +2 -0
- package/cli/templates/tools/crudStart/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
- package/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
- package/cli/templates/tools/crudStart/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
- package/cli/templates/tools/crudStart/boilerplate/test/{{resourceName}}.test.ts_ +1 -1
- package/cli/templates/tools/crudStart/snippets/3-en-us.ts +2 -0
- package/cli/templates/tools/crudStart/snippets/4-zh-cn.ts +2 -0
- package/dist/index.js +15 -1
- package/dist/lib/crudAnalysis.d.ts +2 -0
- package/dist-cli/templates/tools/crudBasic/snippets/3-en-us.js +2 -0
- package/dist-cli/templates/tools/crudBasic/snippets/4-zh-cn.js +2 -0
- package/dist-cli/templates/tools/crudStart/snippets/3-en-us.js +2 -0
- package/dist-cli/templates/tools/crudStart/snippets/4-zh-cn.js +2 -0
- 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
|
|
@@ -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.
|
|
4
|
-
"gitHead": "
|
|
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.
|
|
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.
|
|
80
|
+
"vona-core": "^5.1.55",
|
|
81
81
|
"why-is-node-running": "^3.2.2",
|
|
82
82
|
"yargs-parser": "^22.0.0"
|
|
83
83
|
}
|