@things-factory/kpi 10.0.0-beta.99 → 10.0.0-y.0
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/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/kpi-value-provider.js +8 -4
- package/dist-server/controllers/kpi-value-provider.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-query.d.ts +9 -0
- package/dist-server/service/kpi-value/kpi-value-query.js +48 -0
- package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
|
@@ -21,10 +21,10 @@ class KpiValueProvider {
|
|
|
21
21
|
domain: { id: this.options.domainId }
|
|
22
22
|
};
|
|
23
23
|
if (kpi.periodType === 'ALLTIME') {
|
|
24
|
-
// ALLTIME 타입은 valueDate 무시하고 최신
|
|
24
|
+
// ALLTIME 타입은 valueDate 무시하고 최신 version → createdAt 순으로 1건
|
|
25
25
|
const value = await valueRepo.findOne({
|
|
26
26
|
where: whereCondition,
|
|
27
|
-
order: { createdAt: 'DESC' }
|
|
27
|
+
order: { version: 'DESC', createdAt: 'DESC' }
|
|
28
28
|
});
|
|
29
29
|
if (!value) {
|
|
30
30
|
throw new Error(`KPI value not found: kpi='${name}', org='${this.options.org ?? ''}' (ALLTIME type)`);
|
|
@@ -32,9 +32,13 @@ class KpiValueProvider {
|
|
|
32
32
|
return value.value;
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
|
-
//
|
|
35
|
+
// 같은 (kpi, valueDate, org, domain) 좌표에 여러 version 가능 →
|
|
36
|
+
// version DESC 으로 latest 보장.
|
|
36
37
|
whereCondition.valueDate = this.options.valueDate;
|
|
37
|
-
const value = await valueRepo.findOne({
|
|
38
|
+
const value = await valueRepo.findOne({
|
|
39
|
+
where: whereCondition,
|
|
40
|
+
order: { version: 'DESC' }
|
|
41
|
+
});
|
|
38
42
|
if (!value) {
|
|
39
43
|
throw new Error(`KPI value not found: kpi='${name}', org='${this.options.org ?? ''}', valueDate='${this.options.valueDate}'`);
|
|
40
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-value-provider.js","sourceRoot":"","sources":["../../server/controllers/kpi-value-provider.ts"],"names":[],"mappings":";;;AAAA,iDAAqD;AACrD,4CAAwC;AACxC,8DAAyD;AAGzD,MAAa,gBAAgB;IAC3B,YACU,OAKP;QALO,YAAO,GAAP,OAAO,CAKd;IACA,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,SAAG,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACnD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;QAC7F,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAA;QACnD,MAAM,SAAS,GAAG,IAAA,qBAAa,EAAC,oBAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAE1D,uCAAuC;QACvC,MAAM,cAAc,GAAQ;YAC1B,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE;YACnB,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE;YAC3B,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;SACtC,CAAA;QAED,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACjC,
|
|
1
|
+
{"version":3,"file":"kpi-value-provider.js","sourceRoot":"","sources":["../../server/controllers/kpi-value-provider.ts"],"names":[],"mappings":";;;AAAA,iDAAqD;AACrD,4CAAwC;AACxC,8DAAyD;AAGzD,MAAa,gBAAgB;IAC3B,YACU,OAKP;QALO,YAAO,GAAP,OAAO,CAKd;IACA,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,SAAG,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACnD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;QAC7F,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAA;QACnD,MAAM,SAAS,GAAG,IAAA,qBAAa,EAAC,oBAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAE1D,uCAAuC;QACvC,MAAM,cAAc,GAAQ;YAC1B,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE;YACnB,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE;YAC3B,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;SACtC,CAAA;QAED,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACjC,2DAA2D;YAC3D,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC;gBACpC,KAAK,EAAE,cAAc;gBACrB,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE;aAC9C,CAAC,CAAA;YACF,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,kBAAkB,CAAC,CAAA;YACvG,CAAC;YACD,OAAO,KAAK,CAAC,KAAK,CAAA;QACpB,CAAC;aAAM,CAAC;YACN,uDAAuD;YACvD,6BAA6B;YAC7B,cAAc,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAA;YACjD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC;gBACpC,KAAK,EAAE,cAAc;gBACrB,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;aAC3B,CAAC,CAAA;YACF,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CACb,6BAA6B,IAAI,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,iBAAiB,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAC7G,CAAA;YACH,CAAC;YACD,OAAO,KAAK,CAAC,KAAK,CAAA;QACpB,CAAC;IACH,CAAC;CACF;AAjDD,4CAiDC","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { Kpi } from '../service/kpi/kpi'\nimport { KpiValue } from '../service/kpi-value/kpi-value'\nimport { ValueProvider } from '../calculator/provider'\n\nexport class KpiValueProvider implements ValueProvider {\n constructor(\n private options: {\n valueDate: string\n org?: string\n domainId: string\n tx?: any\n }\n ) {}\n\n async get(name: string) {\n const kpiRepo = getRepository(Kpi, this.options.tx)\n const kpi = await kpiRepo.findOne({ where: { name, domain: { id: this.options.domainId } } })\n if (!kpi) throw new Error(`KPI not found: ${name}`)\n const valueRepo = getRepository(KpiValue, this.options.tx)\n\n // SINGLE 타입인 경우 valueDate 무시하고 최신 값 조회\n const whereCondition: any = {\n kpi: { id: kpi.id },\n org: this.options.org ?? '',\n domain: { id: this.options.domainId }\n }\n\n if (kpi.periodType === 'ALLTIME') {\n // ALLTIME 타입은 valueDate 무시하고 최신 version → createdAt 순으로 1건\n const value = await valueRepo.findOne({\n where: whereCondition,\n order: { version: 'DESC', createdAt: 'DESC' }\n })\n if (!value) {\n throw new Error(`KPI value not found: kpi='${name}', org='${this.options.org ?? ''}' (ALLTIME type)`)\n }\n return value.value\n } else {\n // 같은 (kpi, valueDate, org, domain) 좌표에 여러 version 가능 →\n // version DESC 으로 latest 보장.\n whereCondition.valueDate = this.options.valueDate\n const value = await valueRepo.findOne({\n where: whereCondition,\n order: { version: 'DESC' }\n })\n if (!value) {\n throw new Error(\n `KPI value not found: kpi='${name}', org='${this.options.org ?? ''}', valueDate='${this.options.valueDate}'`\n )\n }\n return value.value\n }\n }\n}\n"]}
|
|
@@ -6,6 +6,15 @@ import { Kpi } from '../kpi/kpi';
|
|
|
6
6
|
export declare class KpiValueQuery {
|
|
7
7
|
kpiValue(id: string, context: ResolverContext): Promise<KpiValue>;
|
|
8
8
|
kpiValues(params: ListParam, context: ResolverContext): Promise<KpiValueList>;
|
|
9
|
+
/**
|
|
10
|
+
* 같은 (domain, kpi, valueDate, kpiOrgScope) 좌표에서 version 이 가장 큰 row 만 반환.
|
|
11
|
+
* 차트·최종 KPI 계산 등 "최신 상태" 만 필요한 사용처용.
|
|
12
|
+
*
|
|
13
|
+
* 구현: subquery 로 (domain, kpi, valueDate, kpiOrgScope) 그룹별 MAX(version) 를 구한 뒤
|
|
14
|
+
* 본 테이블과 매칭. 기존 kpiValues 와 동일하게 ListParam (filters/sortings/pagination)
|
|
15
|
+
* 지원, 도메인 inheritance 적용.
|
|
16
|
+
*/
|
|
17
|
+
latestKpiValues(params: ListParam, context: ResolverContext): Promise<KpiValueList>;
|
|
9
18
|
domain(kpiValue: KpiValue): Promise<Domain>;
|
|
10
19
|
updater(kpiValue: KpiValue): Promise<User>;
|
|
11
20
|
creator(kpiValue: KpiValue): Promise<User>;
|
|
@@ -33,6 +33,43 @@ let KpiValueQuery = class KpiValueQuery {
|
|
|
33
33
|
const [items, total] = await queryBuilder.getManyAndCount();
|
|
34
34
|
return { items, total };
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 같은 (domain, kpi, valueDate, kpiOrgScope) 좌표에서 version 이 가장 큰 row 만 반환.
|
|
38
|
+
* 차트·최종 KPI 계산 등 "최신 상태" 만 필요한 사용처용.
|
|
39
|
+
*
|
|
40
|
+
* 구현: subquery 로 (domain, kpi, valueDate, kpiOrgScope) 그룹별 MAX(version) 를 구한 뒤
|
|
41
|
+
* 본 테이블과 매칭. 기존 kpiValues 와 동일하게 ListParam (filters/sortings/pagination)
|
|
42
|
+
* 지원, 도메인 inheritance 적용.
|
|
43
|
+
*/
|
|
44
|
+
async latestKpiValues(params, context) {
|
|
45
|
+
const { domain } = context.state;
|
|
46
|
+
const domainIds = await (0, domain_inheritance_1.getDomainIdsWithAncestors)(domain);
|
|
47
|
+
const queryBuilder = (0, shell_1.getQueryBuilderFromListParams)({
|
|
48
|
+
domain: undefined,
|
|
49
|
+
params,
|
|
50
|
+
repository: (0, shell_1.getRepository)(kpi_value_1.KpiValue),
|
|
51
|
+
searchables: ['kpi', 'group', 'valueDate']
|
|
52
|
+
});
|
|
53
|
+
queryBuilder.andWhere(`${queryBuilder.alias}.domain IN (:...ancestorDomainIds)`, {
|
|
54
|
+
ancestorDomainIds: domainIds
|
|
55
|
+
});
|
|
56
|
+
// 같은 (domain, kpi, valueDate, kpiOrgScope) 좌표에서 가장 높은 version 만 통과.
|
|
57
|
+
// 운영 DB 가 PostgreSQL 이라 DISTINCT ON 활용 — TypeORM QueryBuilder 의 정식 메서드.
|
|
58
|
+
queryBuilder
|
|
59
|
+
.distinctOn([
|
|
60
|
+
`${queryBuilder.alias}.domainId`,
|
|
61
|
+
`${queryBuilder.alias}.kpiId`,
|
|
62
|
+
`${queryBuilder.alias}.valueDate`,
|
|
63
|
+
`${queryBuilder.alias}.kpiOrgScope`
|
|
64
|
+
])
|
|
65
|
+
.addOrderBy(`${queryBuilder.alias}.domainId`, 'ASC')
|
|
66
|
+
.addOrderBy(`${queryBuilder.alias}.kpiId`, 'ASC')
|
|
67
|
+
.addOrderBy(`${queryBuilder.alias}.valueDate`, 'ASC')
|
|
68
|
+
.addOrderBy(`${queryBuilder.alias}.kpiOrgScope`, 'ASC')
|
|
69
|
+
.addOrderBy(`${queryBuilder.alias}.version`, 'DESC');
|
|
70
|
+
const items = await queryBuilder.getMany();
|
|
71
|
+
return { items, total: items.length };
|
|
72
|
+
}
|
|
36
73
|
async domain(kpiValue) {
|
|
37
74
|
return kpiValue.domainId && (await (0, shell_1.getRepository)(shell_1.Domain).findOneBy({ id: kpiValue.domainId }));
|
|
38
75
|
}
|
|
@@ -67,6 +104,17 @@ tslib_1.__decorate([
|
|
|
67
104
|
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
68
105
|
tslib_1.__metadata("design:returntype", Promise)
|
|
69
106
|
], KpiValueQuery.prototype, "kpiValues", null);
|
|
107
|
+
tslib_1.__decorate([
|
|
108
|
+
(0, type_graphql_1.Directive)('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)'),
|
|
109
|
+
(0, type_graphql_1.Query)(returns => kpi_value_type_1.KpiValueList, {
|
|
110
|
+
description: 'Fetch latest-version KpiValues only — one row per (domain, kpi, valueDate, kpiOrgScope).'
|
|
111
|
+
}),
|
|
112
|
+
tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
|
|
113
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
114
|
+
tslib_1.__metadata("design:type", Function),
|
|
115
|
+
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
116
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
117
|
+
], KpiValueQuery.prototype, "latestKpiValues", null);
|
|
70
118
|
tslib_1.__decorate([
|
|
71
119
|
(0, type_graphql_1.FieldResolver)(type => shell_1.Domain),
|
|
72
120
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-value-query.js","sourceRoot":"","sources":["../../../server/service/kpi-value/kpi-value-query.ts"],"names":[],"mappings":";;;;AAAA,qCAA4B;AAC5B,+CAAqG;AACrG,iDAAuG;AACvG,yDAAgD;AAChD,2CAAsC;AACtC,qDAA+C;AAC/C,oCAAgC;AAEhC,oEAAuE;AAGhE,IAAM,aAAa,GAAnB,MAAM,aAAa;IAGlB,AAAN,KAAK,CAAC,QAAQ,CACgE,EAAU,EAC/E,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,SAAS,GAAG,MAAM,IAAA,8CAAyB,EAAC,MAAM,CAAC,CAAA;QAEzD,OAAO,MAAM,IAAA,qBAAa,EAAC,oBAAQ,CAAC,CAAC,OAAO,CAAC;YAC3C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE;SAC7C,CAAC,CAAA;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,SAAS,CAA0B,MAAiB,EAAS,OAAwB;QACzF,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,SAAS,GAAG,MAAM,IAAA,8CAAyB,EAAC,MAAM,CAAC,CAAA;QAEzD,MAAM,YAAY,GAAG,IAAA,qCAA6B,EAAC;YACjD,MAAM,EAAE,SAAS;YACjB,MAAM;YACN,UAAU,EAAE,IAAA,qBAAa,EAAC,oBAAQ,CAAC;YACnC,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC;SAC3C,CAAC,CAAA;QAEF,YAAY,CAAC,QAAQ,CAAC,GAAG,YAAY,CAAC,KAAK,oCAAoC,EAAE;YAC/E,iBAAiB,EAAE,SAAS;SAC7B,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAA;QAE3D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzB,CAAC;IAGK,AAAN,KAAK,CAAC,MAAM,CAAS,QAAkB;QACrC,OAAO,QAAQ,CAAC,QAAQ,IAAI,CAAC,MAAM,IAAA,qBAAa,EAAC,cAAM,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;IAChG,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAS,QAAkB;QACtC,OAAO,QAAQ,CAAC,SAAS,IAAI,CAAC,MAAM,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;IAChG,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAS,QAAkB;QACtC,OAAO,QAAQ,CAAC,SAAS,IAAI,CAAC,MAAM,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;IAChG,CAAC;IAGK,AAAN,KAAK,CAAC,GAAG,CAAS,QAAkB;QAClC,IAAI,CAAC,QAAQ,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAChC,OAAO,MAAM,IAAA,qBAAa,EAAC,SAAG,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;IACnE,CAAC;CACF,CAAA;
|
|
1
|
+
{"version":3,"file":"kpi-value-query.js","sourceRoot":"","sources":["../../../server/service/kpi-value/kpi-value-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive, Float } from 'type-graphql'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { KpiValue } from './kpi-value'\nimport { KpiValueList } from './kpi-value-type'\nimport { Kpi } from '../kpi/kpi'\nimport { KpiValueScoreService } from './kpi-value-score.service'\nimport { getDomainIdsWithAncestors } from '../utils/domain-inheritance'\n\n@Resolver(KpiValue)\nexport class KpiValueQuery {\n @Directive('@privilege(category: \"kpi\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => KpiValue!, { nullable: true, description: 'Fetch a single KPI value by its unique identifier.' })\n async kpiValue(\n @Arg('id', { description: 'Unique identifier of the KPI value to fetch.' }) id: string,\n @Ctx() context: ResolverContext\n ): Promise<KpiValue> {\n const { domain } = context.state\n const domainIds = await getDomainIdsWithAncestors(domain)\n\n return await getRepository(KpiValue).findOne({\n where: { domain: { id: In(domainIds) }, id }\n })\n }\n\n @Directive('@privilege(category: \"kpi\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => KpiValueList, { description: 'To fetch multiple KpiValues' })\n async kpiValues(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<KpiValueList> {\n const { domain } = context.state\n const domainIds = await getDomainIdsWithAncestors(domain)\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain: undefined,\n params,\n repository: getRepository(KpiValue),\n searchables: ['kpi', 'group', 'valueDate']\n })\n\n queryBuilder.andWhere(`${queryBuilder.alias}.domain IN (:...ancestorDomainIds)`, {\n ancestorDomainIds: domainIds\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n /**\n * 같은 (domain, kpi, valueDate, kpiOrgScope) 좌표에서 version 이 가장 큰 row 만 반환.\n * 차트·최종 KPI 계산 등 \"최신 상태\" 만 필요한 사용처용.\n *\n * 구현: subquery 로 (domain, kpi, valueDate, kpiOrgScope) 그룹별 MAX(version) 를 구한 뒤\n * 본 테이블과 매칭. 기존 kpiValues 와 동일하게 ListParam (filters/sortings/pagination)\n * 지원, 도메인 inheritance 적용.\n */\n @Directive('@privilege(category: \"kpi\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => KpiValueList, {\n description: 'Fetch latest-version KpiValues only — one row per (domain, kpi, valueDate, kpiOrgScope).'\n })\n async latestKpiValues(\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<KpiValueList> {\n const { domain } = context.state\n const domainIds = await getDomainIdsWithAncestors(domain)\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain: undefined,\n params,\n repository: getRepository(KpiValue),\n searchables: ['kpi', 'group', 'valueDate']\n })\n\n queryBuilder.andWhere(`${queryBuilder.alias}.domain IN (:...ancestorDomainIds)`, {\n ancestorDomainIds: domainIds\n })\n\n // 같은 (domain, kpi, valueDate, kpiOrgScope) 좌표에서 가장 높은 version 만 통과.\n // 운영 DB 가 PostgreSQL 이라 DISTINCT ON 활용 — TypeORM QueryBuilder 의 정식 메서드.\n queryBuilder\n .distinctOn([\n `${queryBuilder.alias}.domainId`,\n `${queryBuilder.alias}.kpiId`,\n `${queryBuilder.alias}.valueDate`,\n `${queryBuilder.alias}.kpiOrgScope`\n ])\n .addOrderBy(`${queryBuilder.alias}.domainId`, 'ASC')\n .addOrderBy(`${queryBuilder.alias}.kpiId`, 'ASC')\n .addOrderBy(`${queryBuilder.alias}.valueDate`, 'ASC')\n .addOrderBy(`${queryBuilder.alias}.kpiOrgScope`, 'ASC')\n .addOrderBy(`${queryBuilder.alias}.version`, 'DESC')\n\n const items = await queryBuilder.getMany()\n\n return { items, total: items.length }\n }\n\n @FieldResolver(type => Domain)\n async domain(@Root() kpiValue: KpiValue): Promise<Domain> {\n return kpiValue.domainId && (await getRepository(Domain).findOneBy({ id: kpiValue.domainId }))\n }\n\n @FieldResolver(type => User)\n async updater(@Root() kpiValue: KpiValue): Promise<User> {\n return kpiValue.updaterId && (await getRepository(User).findOneBy({ id: kpiValue.updaterId }))\n }\n\n @FieldResolver(type => User)\n async creator(@Root() kpiValue: KpiValue): Promise<User> {\n return kpiValue.creatorId && (await getRepository(User).findOneBy({ id: kpiValue.creatorId }))\n }\n\n @FieldResolver(type => Kpi)\n async kpi(@Root() kpiValue: KpiValue): Promise<Kpi | null> {\n if (!kpiValue.kpiId) return null\n return await getRepository(Kpi).findOneBy({ id: kpiValue.kpiId })\n }\n}\n"]}
|