@things-factory/kpi 10.0.0-beta.95 → 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-metric-value-provider.js +10 -4
- package/dist-server/controllers/kpi-metric-value-provider.js.map +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
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.KpiMetricValueProvider = void 0;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
4
5
|
const shell_1 = require("@things-factory/shell");
|
|
5
6
|
const kpi_metric_1 = require("../service/kpi-metric/kpi-metric");
|
|
6
7
|
const kpi_metric_value_1 = require("../service/kpi-metric-value/kpi-metric-value");
|
|
7
8
|
const value_date_util_1 = require("../service/utils/value-date-util");
|
|
9
|
+
const domain_inheritance_1 = require("../service/utils/domain-inheritance");
|
|
8
10
|
class KpiMetricValueProvider {
|
|
9
11
|
constructor(options) {
|
|
10
12
|
this.options = options;
|
|
11
13
|
}
|
|
12
14
|
async get(name) {
|
|
13
15
|
const metricRepo = (0, shell_1.getRepository)(kpi_metric_1.KpiMetric, this.options.tx);
|
|
14
|
-
|
|
16
|
+
// 현재 도메인 + 모든 조상 도메인 ID. KpiMetric 정의는 보통 부모(admin)에,
|
|
17
|
+
// KpiMetricValue 는 자식(tenant)에 저장되는 패턴 대응.
|
|
18
|
+
const domain = await (0, shell_1.getRepository)(shell_1.Domain, this.options.tx).findOne({ where: { id: this.options.domainId } });
|
|
19
|
+
const domainIds = domain ? await (0, domain_inheritance_1.getDomainIdsWithAncestors)(domain) : [this.options.domainId];
|
|
20
|
+
const metric = await metricRepo.findOne({ where: { name, domain: { id: (0, typeorm_1.In)(domainIds) } } });
|
|
15
21
|
if (!metric)
|
|
16
22
|
throw new Error(`Metric not found: ${name}`);
|
|
17
23
|
// ALLTIME 타입인 경우 valueDate 무시하고 최신 값 조회
|
|
@@ -21,7 +27,7 @@ class KpiMetricValueProvider {
|
|
|
21
27
|
where: {
|
|
22
28
|
metric: { id: metric.id },
|
|
23
29
|
org: this.options.org ?? '',
|
|
24
|
-
domain: { id:
|
|
30
|
+
domain: { id: (0, typeorm_1.In)(domainIds) }
|
|
25
31
|
},
|
|
26
32
|
order: { createdAt: 'DESC' }
|
|
27
33
|
});
|
|
@@ -50,7 +56,7 @@ class KpiMetricValueProvider {
|
|
|
50
56
|
metric: { id: metric.id },
|
|
51
57
|
valueDate,
|
|
52
58
|
org: this.options.org ?? '',
|
|
53
|
-
domain: { id:
|
|
59
|
+
domain: { id: (0, typeorm_1.In)(domainIds) }
|
|
54
60
|
}
|
|
55
61
|
});
|
|
56
62
|
if (!value) {
|
|
@@ -59,7 +65,7 @@ class KpiMetricValueProvider {
|
|
|
59
65
|
where: {
|
|
60
66
|
metric: { id: metric.id },
|
|
61
67
|
org: this.options.org ?? '',
|
|
62
|
-
domain: { id:
|
|
68
|
+
domain: { id: (0, typeorm_1.In)(domainIds) }
|
|
63
69
|
},
|
|
64
70
|
order: { valueDate: 'DESC' }
|
|
65
71
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-metric-value-provider.js","sourceRoot":"","sources":["../../server/controllers/kpi-metric-value-provider.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"kpi-metric-value-provider.js","sourceRoot":"","sources":["../../server/controllers/kpi-metric-value-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Domain, getRepository } from '@things-factory/shell'\nimport { KpiMetric } from '../service/kpi-metric/kpi-metric'\nimport { KpiMetricValue } from '../service/kpi-metric-value/kpi-metric-value'\nimport { ValueProvider } from '../calculator/provider'\nimport { getDefaultValueDate } from '../service/utils/value-date-util'\nimport { getDomainIdsWithAncestors } from '../service/utils/domain-inheritance'\n\nexport class KpiMetricValueProvider 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 metricRepo = getRepository(KpiMetric, this.options.tx)\n\n // 현재 도메인 + 모든 조상 도메인 ID. KpiMetric 정의는 보통 부모(admin)에,\n // KpiMetricValue 는 자식(tenant)에 저장되는 패턴 대응.\n const domain = await getRepository(Domain, this.options.tx).findOne({ where: { id: this.options.domainId } })\n const domainIds = domain ? await getDomainIdsWithAncestors(domain) : [this.options.domainId]\n\n const metric = await metricRepo.findOne({ where: { name, domain: { id: In(domainIds) } } })\n if (!metric) throw new Error(`Metric not found: ${name}`)\n\n // ALLTIME 타입인 경우 valueDate 무시하고 최신 값 조회\n if (metric.periodType === 'ALLTIME') {\n const valueRepo = getRepository(KpiMetricValue, this.options.tx)\n const value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n org: this.options.org ?? '',\n domain: { id: In(domainIds) }\n },\n order: { createdAt: 'DESC' }\n })\n if (!value) {\n throw new Error(`Metric value not found: metric='${name}', org='${this.options.org ?? ''}' (ALLTIME type)`)\n }\n return value.value\n }\n\n // metric의 periodType에 맞게 valueDate 보정\n let valueDate = this.options.valueDate\n if (!valueDate) {\n valueDate = getDefaultValueDate(metric.periodType, 'current')\n } else {\n // valueDate가 metric.periodType에 맞는 포맷인지 검사, 아니면 보정\n // (간단히: 길이로 구분, 실제는 정규식 등으로 더 정교하게 가능)\n if (metric.periodType === 'MONTH' && valueDate.length > 7) valueDate = valueDate.slice(0, 7)\n if (metric.periodType === 'DAY' && valueDate.length > 10) valueDate = valueDate.slice(0, 10)\n // 기타 periodType별 보정 추가 가능\n }\n const valueRepo = getRepository(KpiMetricValue, this.options.tx)\n var value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n valueDate,\n org: this.options.org ?? '',\n domain: { id: In(domainIds) }\n }\n })\n if (!value) {\n // 임시로 최신의 데이타 하나를 찾아오는 것으로 하자.\n value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n org: this.options.org ?? '',\n domain: { id: In(domainIds) }\n },\n order: { valueDate: 'DESC' }\n })\n\n if (!value) {\n throw new Error(\n `Metric value not found: metric='${name}', org='${this.options.org ?? ''}', valueDate='${valueDate}', periodType='${metric.periodType}'`\n )\n }\n }\n\n return value.value\n }\n}\n"]}
|
|
@@ -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"]}
|