@dssp/dkpi 1.0.0-alpha.79 → 1.0.0-alpha.80
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/pages/project-complete-tabs/pc-tab1-plan.d.ts +10 -4
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +139 -45
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
- package/dist-client/shared/complete-api.d.ts +28 -0
- package/dist-client/shared/complete-api.js +21 -0
- package/dist-client/shared/complete-api.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +69 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +223 -2
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/schema.graphql +28 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { KpiMetricValue, KpiMetricValuePatch } from '@things-factory/kpi';
|
|
2
2
|
import { Attachment, NewAttachment } from '@things-factory/attachment-base';
|
|
3
|
+
/**
|
|
4
|
+
* 외부 시스템 데이터 수집 결과 — 시스템별 한 row.
|
|
5
|
+
* UI "자동 수집" 버튼이 시나리오 호출 후 시스템별 성공·오류 표시에 사용.
|
|
6
|
+
*/
|
|
7
|
+
declare class ExternalDataCollectionResult {
|
|
8
|
+
source: string;
|
|
9
|
+
label: string;
|
|
10
|
+
ok: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
data?: any;
|
|
13
|
+
}
|
|
3
14
|
export declare class KpiMetricValueMutation {
|
|
4
15
|
updateKpiMetricValuesCumulative(patches: KpiMetricValuePatch[], context: ResolverContext): Promise<KpiMetricValue[]>;
|
|
5
16
|
updateKpiMetricValuesAssessment(patches: KpiMetricValuePatch[], context: ResolverContext): Promise<KpiMetricValue[]>;
|
|
@@ -21,6 +32,63 @@ export declare class KpiMetricValueMutation {
|
|
|
21
32
|
*/
|
|
22
33
|
private saveMetricValue;
|
|
23
34
|
finalizeProjectWithKpiRecalculation(projectId: string, context: ResolverContext): Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* UI "자동 수집" 버튼 전용 mutation. 외부 시스템 3종 시나리오를 순차 호출하고
|
|
37
|
+
* 시스템별 성공/실패 + 메시지를 반환. UI 는 결과를 보고 카드별 상태 (성공·오류) 를 표시.
|
|
38
|
+
*
|
|
39
|
+
* 시나리오는 도메인 EnvVar (주소·자격증명·서비스키 등) 만으로 동작하므로 파라미터 X.
|
|
40
|
+
* 한 시스템 실패가 다른 시스템 수집을 막지 않도록 try/catch 로 격리.
|
|
41
|
+
*
|
|
42
|
+
* 운영 현실: 키스콘·올바로는 시공사 ID/PW 가 자주 미확보 → 오류 흔함. 세움터는 공공
|
|
43
|
+
* 데이터 서비스키만으로 동작 가능. UI 는 시스템별로 명확한 오류 메시지 노출.
|
|
44
|
+
*/
|
|
45
|
+
collectProjectExternalData(projectId: string, context: ResolverContext): Promise<ExternalDataCollectionResult[]>;
|
|
46
|
+
/**
|
|
47
|
+
* 프로젝트 정보 수정 화면의 "정보 연동" 버튼 전용 — 세움터 단일 시나리오만 호출.
|
|
48
|
+
* BuildingComplex 기본 속성 (area / floorAreaRatio / siteType / structureType /
|
|
49
|
+
* coverageRatio / householdCount 등) 자동 채움 목적.
|
|
50
|
+
*
|
|
51
|
+
* address 인자가 있으면 시나리오 실행 전에 EnvVar `Step::건축물관리대장조회::address::query`
|
|
52
|
+
* 를 그 값으로 upsert — 운영자가 폼에서 방금 입력한 주소를 시나리오가 곧바로 사용.
|
|
53
|
+
* (도메인의 기존 EnvVar 와 다를 수 있어 명시적 갱신.)
|
|
54
|
+
*
|
|
55
|
+
* **도메인 스위치**: mutation 호출은 system / 운영자 도메인에서 일어나지만 시나리오·
|
|
56
|
+
* EnvVar 는 **프로젝트가 속한 도메인** 에 있어야 자연. project.domain 으로 컨텍스트
|
|
57
|
+
* 스위치 후 그 도메인에서 EnvVar upsert + 시나리오 실행.
|
|
58
|
+
*/
|
|
59
|
+
collectProjectBasicInfo(projectId: string, address: string, context: ResolverContext): Promise<ExternalDataCollectionResult>;
|
|
60
|
+
/**
|
|
61
|
+
* mutation 호출이 system / 운영자 도메인에서 일어나도 시나리오·EnvVar 는 **프로젝트
|
|
62
|
+
* 테넌트 도메인** 에서 동작해야 자연. project.code 로 자식 (테넌트) 도메인을 찾아
|
|
63
|
+
* 컨텍스트 사본 만들어 반환.
|
|
64
|
+
*
|
|
65
|
+
* 테넌트 도메인 매핑: Domain.subdomain = project.code AND Domain.extType = 'project'
|
|
66
|
+
* (promoteProjectToTenant mutation 이 그렇게 생성). project.domain 자체는 부모
|
|
67
|
+
* 도메인이라 외부 연동 EnvVar 가 거기 있을 일이 없어 사용 부적합.
|
|
68
|
+
*
|
|
69
|
+
* 테넌트 도메인이 없거나 (= 아직 승격 안 됨) project.code 가 비어있으면 원본
|
|
70
|
+
* 컨텍스트 그대로 반환 (방어). project 자체가 없으면 throw.
|
|
71
|
+
*/
|
|
72
|
+
private switchToProjectDomain;
|
|
73
|
+
/**
|
|
74
|
+
* 컨텍스트의 도메인에 EnvVar 1개 upsert. 시나리오가 도메인 EnvVar 를 우선 참조하는
|
|
75
|
+
* 정책이라, 폼에서 입력한 주소를 시나리오 입력으로 흘리는 다리 역할.
|
|
76
|
+
*/
|
|
77
|
+
private upsertEnvVar;
|
|
78
|
+
/**
|
|
79
|
+
* 단일 시나리오 호출 + result unwrap + 실패 판정 — collectProjectExternalData 의
|
|
80
|
+
* loop 본체와 같은 로직을 1건짜리로 분리.
|
|
81
|
+
*/
|
|
82
|
+
private runSingleScenario;
|
|
83
|
+
/**
|
|
84
|
+
* 외부 시스템 3종 시나리오 순차 실행 + 시스템별 결과 수집.
|
|
85
|
+
*/
|
|
86
|
+
private runExternalDataScenariosWithResults;
|
|
87
|
+
/**
|
|
88
|
+
* finalize 흐름용 시나리오 호출. 결과 형태 없이 진행만 — finalize 의 KPI 재계산이 곧
|
|
89
|
+
* 이어지므로 시스템별 결과를 클라이언트에 돌려줄 필요 없음. 실패는 warning 만.
|
|
90
|
+
*/
|
|
91
|
+
private runExternalDataScenarios;
|
|
24
92
|
/**
|
|
25
93
|
* 프로젝트의 모든 KPI Value들을 계층적으로 재계산합니다.
|
|
26
94
|
* 1. 기존 KPI Value 삭제 (중복 방지)
|
|
@@ -72,3 +140,4 @@ export declare class KpiMetricValueMutation {
|
|
|
72
140
|
*/
|
|
73
141
|
private calculateWeightedAverage;
|
|
74
142
|
}
|
|
143
|
+
export {};
|
|
@@ -4,6 +4,7 @@ exports.KpiMetricValueMutation = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
6
|
const shell_1 = require("@things-factory/shell");
|
|
7
|
+
const integration_base_1 = require("@things-factory/integration-base");
|
|
7
8
|
const typeorm_1 = require("typeorm");
|
|
8
9
|
const kpi_1 = require("@things-factory/kpi");
|
|
9
10
|
const attachment_base_1 = require("@things-factory/attachment-base");
|
|
@@ -13,6 +14,44 @@ const form_data_1 = tslib_1.__importDefault(require("form-data"));
|
|
|
13
14
|
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
14
15
|
const https_1 = tslib_1.__importDefault(require("https"));
|
|
15
16
|
const http_1 = tslib_1.__importDefault(require("http"));
|
|
17
|
+
/**
|
|
18
|
+
* 외부 시스템 데이터 수집 결과 — 시스템별 한 row.
|
|
19
|
+
* UI "자동 수집" 버튼이 시나리오 호출 후 시스템별 성공·오류 표시에 사용.
|
|
20
|
+
*/
|
|
21
|
+
let ExternalDataCollectionResult = class ExternalDataCollectionResult {
|
|
22
|
+
};
|
|
23
|
+
tslib_1.__decorate([
|
|
24
|
+
(0, type_graphql_1.Field)({ description: '소스 키: saeumteo | allbaro | kiscon' }),
|
|
25
|
+
tslib_1.__metadata("design:type", String)
|
|
26
|
+
], ExternalDataCollectionResult.prototype, "source", void 0);
|
|
27
|
+
tslib_1.__decorate([
|
|
28
|
+
(0, type_graphql_1.Field)({ description: '표시 라벨 (세움터/올바로/키스콘)' }),
|
|
29
|
+
tslib_1.__metadata("design:type", String)
|
|
30
|
+
], ExternalDataCollectionResult.prototype, "label", void 0);
|
|
31
|
+
tslib_1.__decorate([
|
|
32
|
+
(0, type_graphql_1.Field)({ description: '시나리오 실행 성공 여부' }),
|
|
33
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
34
|
+
], ExternalDataCollectionResult.prototype, "ok", void 0);
|
|
35
|
+
tslib_1.__decorate([
|
|
36
|
+
(0, type_graphql_1.Field)({ description: '성공 시 "완료", 실패 시 에러 메시지' }),
|
|
37
|
+
tslib_1.__metadata("design:type", String)
|
|
38
|
+
], ExternalDataCollectionResult.prototype, "message", void 0);
|
|
39
|
+
tslib_1.__decorate([
|
|
40
|
+
(0, type_graphql_1.Field)(type => shell_1.ScalarObject, {
|
|
41
|
+
nullable: true,
|
|
42
|
+
description: '시나리오 result 객체 — projectKey: value 맵. ' +
|
|
43
|
+
'세움터={area, floorAreaRatio, upperFloorCount, lowerFloorCount, structureType, siteType}, ' +
|
|
44
|
+
'올바로={totalConstructionWasteAmount}, ' +
|
|
45
|
+
'키스콘={constructionPeriod, constructionCost}. ' +
|
|
46
|
+
'siteType (건축물 용도, 예 APARTMENT_COMPLEX) / structureType (구조형태) 는 ' +
|
|
47
|
+
'BuildingComplex 의 시설유형/구조유형 속성 후보. KISCON 의 "공사유형(신설/증축)" 은 ' +
|
|
48
|
+
'DSSP SiteType (용도분류) 와 다른 도메인이라 매핑 대상 아님.'
|
|
49
|
+
}),
|
|
50
|
+
tslib_1.__metadata("design:type", Object)
|
|
51
|
+
], ExternalDataCollectionResult.prototype, "data", void 0);
|
|
52
|
+
ExternalDataCollectionResult = tslib_1.__decorate([
|
|
53
|
+
(0, type_graphql_1.ObjectType)({ description: '외부 시스템(세움터/올바로/키스콘) 데이터 수집 시나리오 호출 결과' })
|
|
54
|
+
], ExternalDataCollectionResult);
|
|
16
55
|
let KpiMetricValueMutation = class KpiMetricValueMutation {
|
|
17
56
|
/* 프로젝트 수행과정 중에 (시간, 비용, 발생 건수 등) 누적된 값을 반영하여 KPI Metric Value 값을 수정 - 예를 들면, 총 설계 변경 건수, 총 건설 폐기물 발생량, 공사비, 공사기간, 재해 건수 등 */
|
|
18
57
|
async updateKpiMetricValuesCumulative(patches, context) {
|
|
@@ -209,11 +248,169 @@ let KpiMetricValueMutation = class KpiMetricValueMutation {
|
|
|
209
248
|
if (!project) {
|
|
210
249
|
throw new Error(`Project not found: ${projectId}`);
|
|
211
250
|
}
|
|
212
|
-
//
|
|
213
|
-
|
|
251
|
+
// 시나리오·EnvVar·KPI value 모두 프로젝트(테넌트) 도메인 기준이라 컨텍스트 스위치
|
|
252
|
+
// 한 번 만든 뒤 이후 작업 모두 그 컨텍스트로 일관 진행.
|
|
253
|
+
const projectContext = await this.switchToProjectDomain(projectId, context);
|
|
254
|
+
// 외부 시스템 데이터 수집은 별도 mutation (collectProjectExternalData) 로 분리.
|
|
255
|
+
// 사용자가 UI "자동 수집" 으로 먼저 명시 수집 후 결과 검토하고 완공 처리하는 흐름.
|
|
256
|
+
// 안전 차원에서 finalize 시점에도 한 번 더 수집을 시도 — 실패는 warning 만.
|
|
257
|
+
await this.runExternalDataScenarios(projectContext);
|
|
258
|
+
// 프로젝트 KPI Value 계산을 다시 할 것이다 — 테넌트 도메인 컨텍스트에서.
|
|
259
|
+
await this.recalculateProjectKpiValues(projectId, projectContext);
|
|
214
260
|
projectRepo.save(Object.assign(Object.assign({}, project), { state: project_1.ProjectState.COMPLETED }));
|
|
215
261
|
return true;
|
|
216
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* UI "자동 수집" 버튼 전용 mutation. 외부 시스템 3종 시나리오를 순차 호출하고
|
|
265
|
+
* 시스템별 성공/실패 + 메시지를 반환. UI 는 결과를 보고 카드별 상태 (성공·오류) 를 표시.
|
|
266
|
+
*
|
|
267
|
+
* 시나리오는 도메인 EnvVar (주소·자격증명·서비스키 등) 만으로 동작하므로 파라미터 X.
|
|
268
|
+
* 한 시스템 실패가 다른 시스템 수집을 막지 않도록 try/catch 로 격리.
|
|
269
|
+
*
|
|
270
|
+
* 운영 현실: 키스콘·올바로는 시공사 ID/PW 가 자주 미확보 → 오류 흔함. 세움터는 공공
|
|
271
|
+
* 데이터 서비스키만으로 동작 가능. UI 는 시스템별로 명확한 오류 메시지 노출.
|
|
272
|
+
*/
|
|
273
|
+
async collectProjectExternalData(projectId, context) {
|
|
274
|
+
const projectContext = await this.switchToProjectDomain(projectId, context);
|
|
275
|
+
return await this.runExternalDataScenariosWithResults(projectContext);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 프로젝트 정보 수정 화면의 "정보 연동" 버튼 전용 — 세움터 단일 시나리오만 호출.
|
|
279
|
+
* BuildingComplex 기본 속성 (area / floorAreaRatio / siteType / structureType /
|
|
280
|
+
* coverageRatio / householdCount 등) 자동 채움 목적.
|
|
281
|
+
*
|
|
282
|
+
* address 인자가 있으면 시나리오 실행 전에 EnvVar `Step::건축물관리대장조회::address::query`
|
|
283
|
+
* 를 그 값으로 upsert — 운영자가 폼에서 방금 입력한 주소를 시나리오가 곧바로 사용.
|
|
284
|
+
* (도메인의 기존 EnvVar 와 다를 수 있어 명시적 갱신.)
|
|
285
|
+
*
|
|
286
|
+
* **도메인 스위치**: mutation 호출은 system / 운영자 도메인에서 일어나지만 시나리오·
|
|
287
|
+
* EnvVar 는 **프로젝트가 속한 도메인** 에 있어야 자연. project.domain 으로 컨텍스트
|
|
288
|
+
* 스위치 후 그 도메인에서 EnvVar upsert + 시나리오 실행.
|
|
289
|
+
*/
|
|
290
|
+
async collectProjectBasicInfo(projectId, address, context) {
|
|
291
|
+
const projectContext = await this.switchToProjectDomain(projectId, context);
|
|
292
|
+
if (address && address.trim()) {
|
|
293
|
+
await this.upsertEnvVar('Step::건축물관리대장조회::address::query', address.trim(), projectContext);
|
|
294
|
+
}
|
|
295
|
+
return await this.runSingleScenario({ source: 'saeumteo', label: '세움터', name: '건축물관리대장조회' }, projectContext);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* mutation 호출이 system / 운영자 도메인에서 일어나도 시나리오·EnvVar 는 **프로젝트
|
|
299
|
+
* 테넌트 도메인** 에서 동작해야 자연. project.code 로 자식 (테넌트) 도메인을 찾아
|
|
300
|
+
* 컨텍스트 사본 만들어 반환.
|
|
301
|
+
*
|
|
302
|
+
* 테넌트 도메인 매핑: Domain.subdomain = project.code AND Domain.extType = 'project'
|
|
303
|
+
* (promoteProjectToTenant mutation 이 그렇게 생성). project.domain 자체는 부모
|
|
304
|
+
* 도메인이라 외부 연동 EnvVar 가 거기 있을 일이 없어 사용 부적합.
|
|
305
|
+
*
|
|
306
|
+
* 테넌트 도메인이 없거나 (= 아직 승격 안 됨) project.code 가 비어있으면 원본
|
|
307
|
+
* 컨텍스트 그대로 반환 (방어). project 자체가 없으면 throw.
|
|
308
|
+
*/
|
|
309
|
+
async switchToProjectDomain(projectId, context) {
|
|
310
|
+
const { tx } = context.state;
|
|
311
|
+
const project = await (0, shell_1.getRepository)(project_1.Project, tx).findOne({ where: { id: projectId } });
|
|
312
|
+
if (!project) {
|
|
313
|
+
throw new Error(`Project not found: ${projectId}`);
|
|
314
|
+
}
|
|
315
|
+
if (!project.code) {
|
|
316
|
+
console.warn(`[collect] project ${projectId} 는 아직 테넌트 승격되지 않음 (no code) — caller domain 그대로 사용`);
|
|
317
|
+
return context;
|
|
318
|
+
}
|
|
319
|
+
const tenantDomain = await (0, shell_1.getRepository)(shell_1.Domain, tx).findOne({
|
|
320
|
+
where: { subdomain: project.code, extType: 'project' }
|
|
321
|
+
});
|
|
322
|
+
if (!tenantDomain) {
|
|
323
|
+
console.warn(`[collect] project ${projectId} (code=${project.code}) 의 테넌트 도메인을 찾지 못함 — caller domain 그대로 사용`);
|
|
324
|
+
return context;
|
|
325
|
+
}
|
|
326
|
+
return Object.assign(Object.assign({}, context), { state: Object.assign(Object.assign({}, context.state), { domain: tenantDomain }) });
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* 컨텍스트의 도메인에 EnvVar 1개 upsert. 시나리오가 도메인 EnvVar 를 우선 참조하는
|
|
330
|
+
* 정책이라, 폼에서 입력한 주소를 시나리오 입력으로 흘리는 다리 역할.
|
|
331
|
+
*/
|
|
332
|
+
async upsertEnvVar(name, value, context) {
|
|
333
|
+
const { domain, user, tx } = context.state;
|
|
334
|
+
const repo = (0, shell_1.getRepository)(shell_1.EnvVar, tx);
|
|
335
|
+
const existing = await repo.findOne({ where: { domain: { id: domain.id }, name } });
|
|
336
|
+
if (existing) {
|
|
337
|
+
existing.value = value;
|
|
338
|
+
existing.active = true;
|
|
339
|
+
existing.updater = user;
|
|
340
|
+
await repo.save(existing);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
await repo.save(repo.create({
|
|
344
|
+
name,
|
|
345
|
+
value,
|
|
346
|
+
active: true,
|
|
347
|
+
domain,
|
|
348
|
+
description: 'Project basic info 자동 연동용 (project-update 정보연동 버튼)',
|
|
349
|
+
creator: user,
|
|
350
|
+
updater: user
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* 단일 시나리오 호출 + result unwrap + 실패 판정 — collectProjectExternalData 의
|
|
356
|
+
* loop 본체와 같은 로직을 1건짜리로 분리.
|
|
357
|
+
*/
|
|
358
|
+
async runSingleScenario(s, context) {
|
|
359
|
+
var _a, _b;
|
|
360
|
+
const { domain } = context.state;
|
|
361
|
+
const instanceName = `collect-${s.name}-${Date.now()}`;
|
|
362
|
+
try {
|
|
363
|
+
const runResult = await (0, integration_base_1.runScenario)(instanceName, s.name, {}, context);
|
|
364
|
+
if (!runResult) {
|
|
365
|
+
console.warn(`[collect] '${s.name}' no result on domain '${domain.subdomain}'`);
|
|
366
|
+
throw new Error('업데이트할 정보가 없습니다');
|
|
367
|
+
}
|
|
368
|
+
if (runResult.state === 'HALTED') {
|
|
369
|
+
console.warn(`[collect] '${s.name}' HALTED on domain '${domain.subdomain}': ${runResult.message || '(no message)'}`);
|
|
370
|
+
throw new Error('업데이트할 정보가 없습니다');
|
|
371
|
+
}
|
|
372
|
+
let data = (_b = (_a = runResult.result) !== null && _a !== void 0 ? _a : runResult.data) !== null && _b !== void 0 ? _b : {};
|
|
373
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
374
|
+
const wrapKeys = Object.keys(data);
|
|
375
|
+
if (wrapKeys.length === 1 &&
|
|
376
|
+
data[wrapKeys[0]] &&
|
|
377
|
+
typeof data[wrapKeys[0]] === 'object' &&
|
|
378
|
+
!Array.isArray(data[wrapKeys[0]])) {
|
|
379
|
+
data = data[wrapKeys[0]];
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
console.log(`[collect] '${s.name}' OK on '${domain.subdomain}' keys=${Object.keys(data || {}).join(',')}`);
|
|
383
|
+
return { source: s.source, label: s.label, ok: true, message: '완료', data };
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
387
|
+
console.warn(`[collect] '${s.name}' failed on '${domain.subdomain}': ${msg}`);
|
|
388
|
+
return { source: s.source, label: s.label, ok: false, message: msg };
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* 외부 시스템 3종 시나리오 순차 실행 + 시스템별 결과 수집.
|
|
393
|
+
*/
|
|
394
|
+
async runExternalDataScenariosWithResults(context) {
|
|
395
|
+
const { domain } = context.state;
|
|
396
|
+
const SCENARIOS = [
|
|
397
|
+
{ source: 'saeumteo', label: '세움터', name: '건축물관리대장조회' },
|
|
398
|
+
{ source: 'allbaro', label: '올바로', name: '건설폐기물정보조회' },
|
|
399
|
+
{ source: 'kiscon', label: '키스콘', name: '건설공사정보조회' }
|
|
400
|
+
];
|
|
401
|
+
const results = [];
|
|
402
|
+
for (const s of SCENARIOS) {
|
|
403
|
+
results.push(await this.runSingleScenario(s, context));
|
|
404
|
+
}
|
|
405
|
+
return results;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* finalize 흐름용 시나리오 호출. 결과 형태 없이 진행만 — finalize 의 KPI 재계산이 곧
|
|
409
|
+
* 이어지므로 시스템별 결과를 클라이언트에 돌려줄 필요 없음. 실패는 warning 만.
|
|
410
|
+
*/
|
|
411
|
+
async runExternalDataScenarios(context) {
|
|
412
|
+
await this.runExternalDataScenariosWithResults(context);
|
|
413
|
+
}
|
|
217
414
|
/**
|
|
218
415
|
* 프로젝트의 모든 KPI Value들을 계층적으로 재계산합니다.
|
|
219
416
|
* 1. 기존 KPI Value 삭제 (중복 방지)
|
|
@@ -680,6 +877,30 @@ tslib_1.__decorate([
|
|
|
680
877
|
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
681
878
|
tslib_1.__metadata("design:returntype", Promise)
|
|
682
879
|
], KpiMetricValueMutation.prototype, "finalizeProjectWithKpiRecalculation", null);
|
|
880
|
+
tslib_1.__decorate([
|
|
881
|
+
(0, type_graphql_1.Directive)('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
|
|
882
|
+
(0, type_graphql_1.Mutation)(returns => [ExternalDataCollectionResult], {
|
|
883
|
+
description: 'Run external data collection scenarios per system, returning per-system result.'
|
|
884
|
+
}),
|
|
885
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('projectId')),
|
|
886
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
887
|
+
tslib_1.__metadata("design:type", Function),
|
|
888
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
889
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
890
|
+
], KpiMetricValueMutation.prototype, "collectProjectExternalData", null);
|
|
891
|
+
tslib_1.__decorate([
|
|
892
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
893
|
+
(0, type_graphql_1.Directive)('@privilege(category: "kpi", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
|
|
894
|
+
(0, type_graphql_1.Mutation)(returns => ExternalDataCollectionResult, {
|
|
895
|
+
description: 'Run only 건축물관리대장조회 scenario for project basic info collection.'
|
|
896
|
+
}),
|
|
897
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('projectId')),
|
|
898
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('address', { nullable: true })),
|
|
899
|
+
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
900
|
+
tslib_1.__metadata("design:type", Function),
|
|
901
|
+
tslib_1.__metadata("design:paramtypes", [String, String, Object]),
|
|
902
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
903
|
+
], KpiMetricValueMutation.prototype, "collectProjectBasicInfo", null);
|
|
683
904
|
exports.KpiMetricValueMutation = KpiMetricValueMutation = tslib_1.__decorate([
|
|
684
905
|
(0, type_graphql_1.Resolver)(kpi_1.KpiMetricValue)
|
|
685
906
|
], KpiMetricValueMutation);
|