@dssp/supervision 1.0.0-alpha.92 → 1.0.0-alpha.93
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/building-inspection/building-inspection-management.d.ts +1 -0
- package/dist-client/pages/building-inspection/building-inspection-management.js +19 -4
- package/dist-client/pages/building-inspection/building-inspection-management.js.map +1 -1
- package/dist-client/pages/building-inspection/component/field-document/field-document-list-panel.js +9 -8
- package/dist-client/pages/building-inspection/component/field-document/field-document-list-panel.js.map +1 -1
- package/dist-client/pages/building-inspection/component/field-document/field-document-popup.d.ts +3 -0
- package/dist-client/pages/building-inspection/component/field-document/field-document-popup.js +86 -23
- package/dist-client/pages/building-inspection/component/field-document/field-document-popup.js.map +1 -1
- package/dist-client/pages/checklist/checklist-view.d.ts +1 -0
- package/dist-client/pages/checklist/checklist-view.js +12 -68
- package/dist-client/pages/checklist/checklist-view.js.map +1 -1
- package/dist-client/pages/checklist/kcs-criteria-popup.d.ts +15 -0
- package/dist-client/pages/checklist/kcs-criteria-popup.js +174 -0
- package/dist-client/pages/checklist/kcs-criteria-popup.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/field-document/field-document-mutation.js +8 -5
- package/dist-server/service/field-document/field-document-mutation.js.map +1 -1
- package/dist-server/service/field-document/field-document-summary-service.d.ts +8 -0
- package/dist-server/service/field-document/field-document-summary-service.js +44 -20
- package/dist-server/service/field-document/field-document-summary-service.js.map +1 -1
- package/dist-server/service/field-document/field-document-type.d.ts +1 -0
- package/dist-server/service/field-document/field-document-type.js +4 -0
- package/dist-server/service/field-document/field-document-type.js.map +1 -1
- package/dist-server/service/project-checklist/spec-matching-service.js +9 -3
- package/dist-server/service/project-checklist/spec-matching-service.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -11,7 +11,8 @@ const field_document_summary_service_1 = require("./field-document-summary-servi
|
|
|
11
11
|
let FieldDocumentMutation = class FieldDocumentMutation {
|
|
12
12
|
async createFieldDocument(patch, context) {
|
|
13
13
|
const { domain, user, tx } = context.state;
|
|
14
|
-
const
|
|
14
|
+
const repo = tx.getRepository(field_document_1.FieldDocument);
|
|
15
|
+
const result = await repo.save({
|
|
15
16
|
projectId: patch.projectId,
|
|
16
17
|
code: patch.code,
|
|
17
18
|
name: patch.name,
|
|
@@ -23,15 +24,16 @@ let FieldDocumentMutation = class FieldDocumentMutation {
|
|
|
23
24
|
updater: user
|
|
24
25
|
});
|
|
25
26
|
if (patch.file) {
|
|
26
|
-
await (0, attachment_base_1.createAttachment)(null, {
|
|
27
|
+
const attachment = await (0, attachment_base_1.createAttachment)(null, {
|
|
27
28
|
attachment: {
|
|
28
29
|
file: patch.file,
|
|
29
30
|
refType: field_document_1.FieldDocument.name,
|
|
30
31
|
refBy: result.id
|
|
31
32
|
}
|
|
32
33
|
}, context);
|
|
34
|
+
// 업로드 시점에 AI 브리핑 동기 생성 (실패해도 업로드는 유지)
|
|
35
|
+
await (0, field_document_summary_service_1.generateDocumentSummarySync)(repo, result, (attachment === null || attachment === void 0 ? void 0 : attachment.path) || '', patch.category);
|
|
33
36
|
}
|
|
34
|
-
(0, field_document_summary_service_1.queueDocumentSummary)(result.id);
|
|
35
37
|
return result;
|
|
36
38
|
}
|
|
37
39
|
async createFieldDocuments(patches, context) {
|
|
@@ -51,15 +53,16 @@ let FieldDocumentMutation = class FieldDocumentMutation {
|
|
|
51
53
|
updater: user
|
|
52
54
|
});
|
|
53
55
|
if (patch.file) {
|
|
54
|
-
await (0, attachment_base_1.createAttachment)(null, {
|
|
56
|
+
const attachment = await (0, attachment_base_1.createAttachment)(null, {
|
|
55
57
|
attachment: {
|
|
56
58
|
file: patch.file,
|
|
57
59
|
refType: field_document_1.FieldDocument.name,
|
|
58
60
|
refBy: result.id
|
|
59
61
|
}
|
|
60
62
|
}, context);
|
|
63
|
+
// 업로드 시점에 AI 브리핑 동기 생성 (실패해도 업로드는 유지)
|
|
64
|
+
await (0, field_document_summary_service_1.generateDocumentSummarySync)(repo, result, (attachment === null || attachment === void 0 ? void 0 : attachment.path) || '', patch.category);
|
|
61
65
|
}
|
|
62
|
-
(0, field_document_summary_service_1.queueDocumentSummary)(result.id);
|
|
63
66
|
results.push(result);
|
|
64
67
|
}
|
|
65
68
|
return results;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-document-mutation.js","sourceRoot":"","sources":["../../../server/service/field-document/field-document-mutation.ts"],"names":[],"mappings":";;;;AAAA,+CAAsE;AACtE,qCAA4B;AAE5B,qEAA0F;AAE1F,qDAA4E;AAC5E,+DAAwD;AACxD,
|
|
1
|
+
{"version":3,"file":"field-document-mutation.js","sourceRoot":"","sources":["../../../server/service/field-document/field-document-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'\nimport { In } from 'typeorm'\n\nimport { createAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'\n\nimport { FieldDocument, FieldDocumentSummaryStatus } from './field-document'\nimport { NewFieldDocument } from './field-document-type'\nimport { generateDocumentSummarySync } from './field-document-summary-service'\n\n@Resolver(FieldDocument)\nexport class FieldDocumentMutation {\n @Directive('@transaction')\n @Mutation(returns => FieldDocument, { description: '현장 문서 생성' })\n async createFieldDocument(\n @Arg('patch') patch: NewFieldDocument,\n @Ctx() context: ResolverContext\n ): Promise<FieldDocument> {\n const { domain, user, tx } = context.state\n\n const repo = tx.getRepository(FieldDocument)\n\n const result = await repo.save({\n projectId: patch.projectId,\n code: patch.code,\n name: patch.name,\n sourceType: patch.sourceType,\n sourceRefId: patch.sourceRefId,\n aiSummaryStatus: FieldDocumentSummaryStatus.REQUEST,\n domain,\n creator: user,\n updater: user\n })\n\n if (patch.file) {\n const attachment = await createAttachment(\n null,\n {\n attachment: {\n file: patch.file,\n refType: FieldDocument.name,\n refBy: result.id\n }\n },\n context\n )\n\n // 업로드 시점에 AI 브리핑 동기 생성 (실패해도 업로드는 유지)\n await generateDocumentSummarySync(repo, result, attachment?.path || '', patch.category)\n }\n\n return result\n }\n\n @Directive('@transaction')\n @Mutation(returns => [FieldDocument], { description: '현장 문서 다건 생성' })\n async createFieldDocuments(\n @Arg('patches', type => [NewFieldDocument]) patches: NewFieldDocument[],\n @Ctx() context: ResolverContext\n ): Promise<FieldDocument[]> {\n const { domain, user, tx } = context.state\n const repo = tx.getRepository(FieldDocument)\n const results: FieldDocument[] = []\n\n for (const patch of patches) {\n const result = await repo.save({\n projectId: patch.projectId,\n code: patch.code,\n name: patch.name,\n sourceType: patch.sourceType,\n sourceRefId: patch.sourceRefId,\n aiSummaryStatus: FieldDocumentSummaryStatus.REQUEST,\n domain,\n creator: user,\n updater: user\n })\n\n if (patch.file) {\n const attachment = await createAttachment(\n null,\n {\n attachment: {\n file: patch.file,\n refType: FieldDocument.name,\n refBy: result.id\n }\n },\n context\n )\n\n // 업로드 시점에 AI 브리핑 동기 생성 (실패해도 업로드는 유지)\n await generateDocumentSummarySync(repo, result, attachment?.path || '', patch.category)\n }\n\n results.push(result)\n }\n\n return results\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: '문서 폴더 이동' })\n async moveFieldDocuments(\n @Arg('ids', type => [String]) ids: string[],\n @Arg('targetCode') targetCode: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { user, tx } = context.state\n const repo = tx.getRepository(FieldDocument)\n\n await repo.update({ id: In(ids) }, { code: targetCode, updater: user })\n\n return true\n }\n\n @Directive('@transaction')\n @Mutation(returns => FieldDocument, { description: '즐겨찾기 토글' })\n async toggleFieldDocumentFavorite(\n @Arg('id') id: string,\n @Ctx() context: ResolverContext\n ): Promise<FieldDocument> {\n const { user, tx } = context.state\n const repo = tx.getRepository(FieldDocument)\n const doc = await repo.findOneBy({ id })\n\n return await repo.save({\n ...doc,\n isFavorite: !doc.isFavorite,\n updater: user\n })\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: '현장 문서 삭제' })\n async deleteFieldDocuments(\n @Arg('ids', type => [String]) ids: string[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx } = context.state\n\n await tx.getRepository(FieldDocument).softDelete({ id: In(ids) })\n await deleteAttachmentsByRef(null, { refBys: ids }, context)\n\n return true\n }\n}\n"]}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
import { Repository } from 'typeorm';
|
|
2
|
+
import { FieldDocument } from './field-document';
|
|
3
|
+
export declare function fetchDocumentBriefing({ filename, filePath, category }: {
|
|
4
|
+
filename: string;
|
|
5
|
+
filePath: string;
|
|
6
|
+
category?: string;
|
|
7
|
+
}): Promise<string | null>;
|
|
8
|
+
export declare function generateDocumentSummarySync(repository: Repository<FieldDocument>, doc: FieldDocument, filePath: string, category?: string): Promise<void>;
|
|
1
9
|
export declare function generateDocumentSummary(fieldDocumentId: string): Promise<void>;
|
|
2
10
|
export declare function queueDocumentSummary(fieldDocumentId: string): void;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchDocumentBriefing = fetchDocumentBriefing;
|
|
4
|
+
exports.generateDocumentSummarySync = generateDocumentSummarySync;
|
|
3
5
|
exports.generateDocumentSummary = generateDocumentSummary;
|
|
4
6
|
exports.queueDocumentSummary = queueDocumentSummary;
|
|
5
7
|
const shell_1 = require("@things-factory/shell");
|
|
@@ -8,8 +10,48 @@ const field_document_1 = require("./field-document");
|
|
|
8
10
|
const SUMMARY_API_BASE_URL = 'https://hatiolab-korea-uni.ettisoft.com';
|
|
9
11
|
const API_USERNAME = 'admin';
|
|
10
12
|
const API_PASSWORD = 'admin1234';
|
|
13
|
+
/* 엔진(doc_summary_engine)이 문서 파일을 내려받을 때 사용할 플랫폼 공개 주소 */
|
|
14
|
+
const FILE_BASE_URL = 'https://dcsp.hatiolab.com';
|
|
15
|
+
/* doc_summary_engine 호출 → 한 줄 브리핑 반환. 읽을 수 없는 문서(스캔·도면 등)는 null (정상) */
|
|
16
|
+
async function fetchDocumentBriefing({ filename, filePath, category }) {
|
|
17
|
+
var _a;
|
|
18
|
+
const url = `${SUMMARY_API_BASE_URL}/api/run/doc-summary`;
|
|
19
|
+
const headers = {
|
|
20
|
+
Authorization: `Basic ${Buffer.from(`${API_USERNAME}:${API_PASSWORD}`).toString('base64')}`
|
|
21
|
+
};
|
|
22
|
+
const inputPayload = {
|
|
23
|
+
file_url: `${FILE_BASE_URL}/attachment/${filePath}`,
|
|
24
|
+
filename,
|
|
25
|
+
category: category || ''
|
|
26
|
+
};
|
|
27
|
+
console.log('[doc-summary] request:', url, JSON.stringify(inputPayload));
|
|
28
|
+
const form = new FormData();
|
|
29
|
+
form.append('input', JSON.stringify(inputPayload));
|
|
30
|
+
const response = await fetch(url, { method: 'POST', headers, body: form });
|
|
31
|
+
const raw = await (response === null || response === void 0 ? void 0 : response.json());
|
|
32
|
+
console.log('[doc-summary] response:', response === null || response === void 0 ? void 0 : response.status, JSON.stringify(raw));
|
|
33
|
+
const data = (raw === null || raw === void 0 ? void 0 : raw.result) || {};
|
|
34
|
+
if ((data === null || data === void 0 ? void 0 : data.status) !== 'success') {
|
|
35
|
+
throw new Error((data === null || data === void 0 ? void 0 : data.error) || 'AI 브리핑 생성 실패');
|
|
36
|
+
}
|
|
37
|
+
return ((_a = data.result) === null || _a === void 0 ? void 0 : _a.briefing) || null;
|
|
38
|
+
}
|
|
39
|
+
/* 문서 업로드 시점에 동기로 브리핑을 생성/저장. 엔진 실패는 FAIL 상태로만 남기고 업로드는 유지 */
|
|
40
|
+
async function generateDocumentSummarySync(repository, doc, filePath, category) {
|
|
41
|
+
try {
|
|
42
|
+
const briefing = await fetchDocumentBriefing({ filename: doc.name, filePath, category });
|
|
43
|
+
await repository.update({ id: doc.id }, { aiSummary: briefing, aiSummaryStatus: field_document_1.FieldDocumentSummaryStatus.SUCCESS });
|
|
44
|
+
doc.aiSummary = briefing;
|
|
45
|
+
doc.aiSummaryStatus = field_document_1.FieldDocumentSummaryStatus.SUCCESS;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error('Document briefing generation failed:', error);
|
|
49
|
+
await repository.update({ id: doc.id }, { aiSummaryStatus: field_document_1.FieldDocumentSummaryStatus.FAIL });
|
|
50
|
+
doc.aiSummaryStatus = field_document_1.FieldDocumentSummaryStatus.FAIL;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/* 문서 ID로 브리핑 생성 (백그라운드/자동등록 경로용) */
|
|
11
54
|
async function generateDocumentSummary(fieldDocumentId) {
|
|
12
|
-
var _a, _b, _c;
|
|
13
55
|
const repository = (0, shell_1.getRepository)(field_document_1.FieldDocument);
|
|
14
56
|
try {
|
|
15
57
|
const doc = await repository.findOneBy({ id: fieldDocumentId });
|
|
@@ -18,25 +60,7 @@ async function generateDocumentSummary(fieldDocumentId) {
|
|
|
18
60
|
const attachment = await (0, shell_1.getRepository)(attachment_base_1.Attachment).findOne({
|
|
19
61
|
where: { refBy: doc.id, refType: field_document_1.FieldDocument.name }
|
|
20
62
|
});
|
|
21
|
-
|
|
22
|
-
const headers = {
|
|
23
|
-
Authorization: `Basic ${Buffer.from(`${API_USERNAME}:${API_PASSWORD}`).toString('base64')}`
|
|
24
|
-
};
|
|
25
|
-
const form = new FormData();
|
|
26
|
-
form.append('input', JSON.stringify({ documentName: doc.name, documentPath: (attachment === null || attachment === void 0 ? void 0 : attachment.path) || '' }));
|
|
27
|
-
const response = await fetch(url, {
|
|
28
|
-
method: 'POST',
|
|
29
|
-
headers,
|
|
30
|
-
body: form
|
|
31
|
-
});
|
|
32
|
-
const data = ((_a = (await (response === null || response === void 0 ? void 0 : response.json()))) === null || _a === void 0 ? void 0 : _a.result) || {};
|
|
33
|
-
if ((data === null || data === void 0 ? void 0 : data.status) === 'success') {
|
|
34
|
-
const summary = ((_b = data.result) === null || _b === void 0 ? void 0 : _b['요약']) || ((_c = data.result) === null || _c === void 0 ? void 0 : _c['summary']) || '';
|
|
35
|
-
await repository.update({ id: fieldDocumentId }, { aiSummary: summary, aiSummaryStatus: field_document_1.FieldDocumentSummaryStatus.SUCCESS });
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
await repository.update({ id: fieldDocumentId }, { aiSummaryStatus: field_document_1.FieldDocumentSummaryStatus.FAIL });
|
|
39
|
-
}
|
|
63
|
+
await generateDocumentSummarySync(repository, doc, (attachment === null || attachment === void 0 ? void 0 : attachment.path) || '');
|
|
40
64
|
}
|
|
41
65
|
catch (error) {
|
|
42
66
|
console.error('Document summary generation failed:', error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-document-summary-service.js","sourceRoot":"","sources":["../../../server/service/field-document/field-document-summary-service.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"field-document-summary-service.js","sourceRoot":"","sources":["../../../server/service/field-document/field-document-summary-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Repository } from 'typeorm'\nimport { getRepository } from '@things-factory/shell'\nimport { Attachment } from '@things-factory/attachment-base'\nimport { FieldDocument, FieldDocumentSummaryStatus } from './field-document'\n\nconst SUMMARY_API_BASE_URL: string = 'https://hatiolab-korea-uni.ettisoft.com'\nconst API_USERNAME: string = 'admin'\nconst API_PASSWORD: string = 'admin1234'\n/* 엔진(doc_summary_engine)이 문서 파일을 내려받을 때 사용할 플랫폼 공개 주소 */\nconst FILE_BASE_URL: string = 'https://dcsp.hatiolab.com'\n\n/* doc_summary_engine 호출 → 한 줄 브리핑 반환. 읽을 수 없는 문서(스캔·도면 등)는 null (정상) */\nexport async function fetchDocumentBriefing({\n filename,\n filePath,\n category\n}: {\n filename: string\n filePath: string\n category?: string\n}): Promise<string | null> {\n const url = `${SUMMARY_API_BASE_URL}/api/run/doc-summary`\n const headers: any = {\n Authorization: `Basic ${Buffer.from(`${API_USERNAME}:${API_PASSWORD}`).toString('base64')}`\n }\n\n const inputPayload = {\n file_url: `${FILE_BASE_URL}/attachment/${filePath}`,\n filename,\n category: category || ''\n }\n console.log('[doc-summary] request:', url, JSON.stringify(inputPayload))\n\n const form = new FormData()\n form.append('input', JSON.stringify(inputPayload))\n\n const response = await fetch(url, { method: 'POST', headers, body: form })\n const raw = await response?.json()\n console.log('[doc-summary] response:', response?.status, JSON.stringify(raw))\n\n const data = raw?.result || {}\n\n if (data?.status !== 'success') {\n throw new Error(data?.error || 'AI 브리핑 생성 실패')\n }\n\n return data.result?.briefing || null\n}\n\n/* 문서 업로드 시점에 동기로 브리핑을 생성/저장. 엔진 실패는 FAIL 상태로만 남기고 업로드는 유지 */\nexport async function generateDocumentSummarySync(\n repository: Repository<FieldDocument>,\n doc: FieldDocument,\n filePath: string,\n category?: string\n): Promise<void> {\n try {\n const briefing = await fetchDocumentBriefing({ filename: doc.name, filePath, category })\n await repository.update(\n { id: doc.id },\n { aiSummary: briefing, aiSummaryStatus: FieldDocumentSummaryStatus.SUCCESS }\n )\n doc.aiSummary = briefing\n doc.aiSummaryStatus = FieldDocumentSummaryStatus.SUCCESS\n } catch (error) {\n console.error('Document briefing generation failed:', error)\n await repository.update({ id: doc.id }, { aiSummaryStatus: FieldDocumentSummaryStatus.FAIL })\n doc.aiSummaryStatus = FieldDocumentSummaryStatus.FAIL\n }\n}\n\n/* 문서 ID로 브리핑 생성 (백그라운드/자동등록 경로용) */\nexport async function generateDocumentSummary(fieldDocumentId: string): Promise<void> {\n const repository = getRepository(FieldDocument)\n\n try {\n const doc = await repository.findOneBy({ id: fieldDocumentId })\n if (!doc) return\n\n const attachment = await getRepository(Attachment).findOne({\n where: { refBy: doc.id, refType: FieldDocument.name }\n })\n\n await generateDocumentSummarySync(repository, doc, attachment?.path || '')\n } catch (error) {\n console.error('Document summary generation failed:', error)\n await repository.update({ id: fieldDocumentId }, { aiSummaryStatus: FieldDocumentSummaryStatus.FAIL })\n }\n}\n\nexport function queueDocumentSummary(fieldDocumentId: string): void {\n if (!fieldDocumentId) return\n\n setTimeout(() => {\n generateDocumentSummary(fieldDocumentId).catch(err => {\n console.error('Background document summary update failed:', err)\n })\n }, 0)\n}\n"]}
|
|
@@ -28,6 +28,10 @@ tslib_1.__decorate([
|
|
|
28
28
|
(0, type_graphql_1.Field)({ nullable: true }),
|
|
29
29
|
tslib_1.__metadata("design:type", String)
|
|
30
30
|
], NewFieldDocument.prototype, "sourceRefId", void 0);
|
|
31
|
+
tslib_1.__decorate([
|
|
32
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'AI 브리핑 힌트용 분류 문자열 (대 · 중 · 소분류)' }),
|
|
33
|
+
tslib_1.__metadata("design:type", String)
|
|
34
|
+
], NewFieldDocument.prototype, "category", void 0);
|
|
31
35
|
tslib_1.__decorate([
|
|
32
36
|
(0, type_graphql_1.Field)(() => GraphQLUpload_js_1.default, { nullable: true }),
|
|
33
37
|
tslib_1.__metadata("design:type", Object)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-document-type.js","sourceRoot":"","sources":["../../../server/service/field-document/field-document-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAoE;AACpE,+FAA2D;AAE3D,qDAAgD;AAGzC,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;
|
|
1
|
+
{"version":3,"file":"field-document-type.js","sourceRoot":"","sources":["../../../server/service/field-document/field-document-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAoE;AACpE,+FAA2D;AAE3D,qDAAgD;AAGzC,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAqB5B,CAAA;AArBY,4CAAgB;AAE3B;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;;mDACT;AAGlB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACP;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACN;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;kDACzD;AAGjB;IADC,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,0BAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CAC9B;2BApBN,gBAAgB;IAD5B,IAAA,wBAAS,GAAE;GACC,gBAAgB,CAqB5B;AAGM,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;CAS9B,CAAA;AATY,gDAAkB;AAE7B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;;8CAC5B;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDACb;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDACb;6BARF,kBAAkB;IAD9B,IAAA,wBAAS,GAAE;GACC,kBAAkB,CAS9B;AAGM,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAM7B,CAAA;AANY,8CAAiB;AAE5B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,8BAAa,CAAC,CAAC;;gDACT;AAGtB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,CAAC;;gDACN;4BALF,iBAAiB;IAD7B,IAAA,yBAAU,GAAE;GACA,iBAAiB,CAM7B;AAGM,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;CAMlC,CAAA;AANY,wDAAsB;AAEjC;IADC,IAAA,oBAAK,GAAE;;oDACI;AAGZ;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,CAAC;;qDACN;iCALF,sBAAsB;IADlC,IAAA,yBAAU,GAAE;GACA,sBAAsB,CAMlC","sourcesContent":["import { ObjectType, Field, InputType, Int, ID } from 'type-graphql'\nimport GraphQLUpload from 'graphql-upload/GraphQLUpload.js'\nimport type { FileUpload } from 'graphql-upload/GraphQLUpload.js'\nimport { FieldDocument } from './field-document'\n\n@InputType()\nexport class NewFieldDocument {\n @Field({ nullable: false })\n projectId!: string\n\n @Field({ nullable: false })\n code!: string\n\n @Field({ nullable: false })\n name!: string\n\n @Field({ nullable: true })\n sourceType?: string\n\n @Field({ nullable: true })\n sourceRefId?: string\n\n @Field({ nullable: true, description: 'AI 브리핑 힌트용 분류 문자열 (대 · 중 · 소분류)' })\n category?: string\n\n @Field(() => GraphQLUpload, { nullable: true })\n file?: FileUpload\n}\n\n@InputType()\nexport class FieldDocumentPatch {\n @Field(type => ID, { nullable: false })\n id!: string\n\n @Field({ nullable: true })\n code?: string\n\n @Field({ nullable: true })\n name?: string\n}\n\n@ObjectType()\nexport class FieldDocumentList {\n @Field(type => [FieldDocument])\n items: FieldDocument[]\n\n @Field(type => Int)\n total: number\n}\n\n@ObjectType()\nexport class FieldDocumentCodeCount {\n @Field()\n code: string\n\n @Field(type => Int)\n count: number\n}\n"]}
|
|
@@ -8,6 +8,10 @@ const project_checklist_item_1 = require("../project-checklist-item/project-chec
|
|
|
8
8
|
const SPEC_MATCHING_API_URL = 'https://hatiolab-korea-uni.ettisoft.com/api/run/spec-matching';
|
|
9
9
|
const API_USERNAME = 'admin';
|
|
10
10
|
const API_PASSWORD = 'admin1234';
|
|
11
|
+
// 조항 번호 마커 (1) (2) ... 앞에서 줄바꿈
|
|
12
|
+
function formatClauses(text) {
|
|
13
|
+
return (text || '').replace(/\s*(?=\(\d+\))/g, '\n').trim();
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* 검사항목(query) 1건에 대해 KCS 표준 기준과 현장 시방서 기준을 매칭 API로 조회한다.
|
|
13
17
|
* - KCS 표준은 API 서버에 파일(kcs.csv)이 이미 존재한다.
|
|
@@ -15,7 +19,7 @@ const API_PASSWORD = 'admin1234';
|
|
|
15
19
|
* - 첨부파일이 없으면 site_spec_pdf_url 파라미터는 생략한다.
|
|
16
20
|
*/
|
|
17
21
|
async function matchInspectionCriteria(query, siteSpecPdfUrl) {
|
|
18
|
-
var _a, _b;
|
|
22
|
+
var _a, _b, _c;
|
|
19
23
|
// operato-runner /api/run/{module} 은 multipart 폼필드 'input'(JSON 문자열)으로 입력을 받는다
|
|
20
24
|
const params = { query, top_k: 1 };
|
|
21
25
|
// 첨부파일(현장 시방서 PDF)이 없으면 site_spec_pdf_url 은 생략
|
|
@@ -41,10 +45,12 @@ async function matchInspectionCriteria(query, siteSpecPdfUrl) {
|
|
|
41
45
|
if ((moduleReturn === null || moduleReturn === void 0 ? void 0 : moduleReturn.status) === 'success') {
|
|
42
46
|
const result = moduleReturn.result || {};
|
|
43
47
|
const kcsMatch = ((_a = result.kcs_matches) === null || _a === void 0 ? void 0 : _a[0]) || null;
|
|
44
|
-
|
|
48
|
+
// 조항 번호 마커 (1) (2) ... 앞에서 줄바꿈 (저장 시 1회 파싱)
|
|
49
|
+
if (kcsMatch === null || kcsMatch === void 0 ? void 0 : kcsMatch.text)
|
|
50
|
+
kcsMatch.text = formatClauses(kcsMatch.text);
|
|
45
51
|
return {
|
|
46
52
|
kcsCriteria: kcsMatch ? JSON.stringify(kcsMatch) : null,
|
|
47
|
-
siteSpecCriteria: (
|
|
53
|
+
siteSpecCriteria: formatClauses((_c = (_b = result.site_spec_matches) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.text) || null
|
|
48
54
|
};
|
|
49
55
|
}
|
|
50
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-matching-service.js","sourceRoot":"","sources":["../../../server/service/project-checklist/spec-matching-service.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"spec-matching-service.js","sourceRoot":"","sources":["../../../server/service/project-checklist/spec-matching-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { ProjectChecklistItem } from '../project-checklist-item/project-checklist-item'\n\nconst SPEC_MATCHING_API_URL: string = 'https://hatiolab-korea-uni.ettisoft.com/api/run/spec-matching'\nconst API_USERNAME: string = 'admin'\nconst API_PASSWORD: string = 'admin1234'\n\n// 조항 번호 마커 (1) (2) ... 앞에서 줄바꿈\nfunction formatClauses(text?: string): string {\n return (text || '').replace(/\\s*(?=\\(\\d+\\))/g, '\\n').trim()\n}\n\nexport interface MatchedCriteria {\n // KCS 시방서 기준: kcs_matches[0] 오브젝트 전체를 JSON 문자열로 저장 (file_label 등 포함)\n kcsCriteria: string | null\n // 현장 시방서 기준: site_spec_matches[0].text\n siteSpecCriteria: string | null\n}\n\n/**\n * 검사항목(query) 1건에 대해 KCS 표준 기준과 현장 시방서 기준을 매칭 API로 조회한다.\n * - KCS 표준은 API 서버에 파일(kcs.csv)이 이미 존재한다.\n * - 현장 시방서는 업로드한 PDF URL(siteSpecPdfUrl)을 넘길 때만 매칭된다.\n * - 첨부파일이 없으면 site_spec_pdf_url 파라미터는 생략한다.\n */\nexport async function matchInspectionCriteria(query: string, siteSpecPdfUrl?: string): Promise<MatchedCriteria> {\n // operato-runner /api/run/{module} 은 multipart 폼필드 'input'(JSON 문자열)으로 입력을 받는다\n const params: any = { query, top_k: 1 }\n // 첨부파일(현장 시방서 PDF)이 없으면 site_spec_pdf_url 은 생략\n if (siteSpecPdfUrl) {\n params.site_spec_pdf_url = siteSpecPdfUrl\n }\n\n const form = new FormData()\n form.append('input', JSON.stringify(params))\n\n // Content-Type은 FormData가 boundary와 함께 자동 설정하므로 지정하지 않음\n const headers: any = {\n Authorization: `Basic ${Buffer.from(`${API_USERNAME}:${API_PASSWORD}`).toString('base64')}`\n }\n\n try {\n const response = await fetch(SPEC_MATCHING_API_URL, {\n method: 'POST',\n headers,\n body: form\n })\n\n const outer = await response?.json()\n\n console.log('data :', outer)\n // runner 응답: { result: <모듈 main() 반환>, exit_code, ... }, 모듈 반환: { status, result: {...} }\n const moduleReturn = outer?.result\n if (moduleReturn?.status === 'success') {\n const result = moduleReturn.result || {}\n const kcsMatch = result.kcs_matches?.[0] || null\n // 조항 번호 마커 (1) (2) ... 앞에서 줄바꿈 (저장 시 1회 파싱)\n if (kcsMatch?.text) kcsMatch.text = formatClauses(kcsMatch.text)\n\n return {\n kcsCriteria: kcsMatch ? JSON.stringify(kcsMatch) : null,\n siteSpecCriteria: formatClauses(result.site_spec_matches?.[0]?.text) || null\n }\n }\n } catch (error) {\n console.error('spec-matching API 호출 실패:', error)\n }\n\n return { kcsCriteria: null, siteSpecCriteria: null }\n}\n\n/**\n * 검사항목 ID 목록에 대해 KCS/현장 시방서 기준을 매칭하여 저장한다.\n * (트랜잭션 커밋 후 백그라운드에서 실행되므로 전역 repository 사용)\n */\nexport async function updateProjectChecklistCriteria(itemIds: string[], siteSpecPdfUrl?: string): Promise<void> {\n const repository = getRepository(ProjectChecklistItem)\n\n for (const id of itemIds) {\n try {\n const item = await repository.findOneBy({ id })\n if (!item) continue\n const { kcsCriteria, siteSpecCriteria } = await matchInspectionCriteria(item.name, siteSpecPdfUrl)\n await repository.update({ id }, { kcsCriteria, siteSpecCriteria })\n } catch (error) {\n console.error('project-checklist 검사 기준 매칭 실패:', error)\n }\n }\n}\n\n// 검사 기준 매칭을 백그라운드(비동기)로 트리거 — 저장 응답을 막지 않는다.\nexport function queueUpdateProjectChecklistCriteria(itemIds: string[], siteSpecPdfUrl?: string): void {\n if (!itemIds || itemIds.length === 0) return\n\n setTimeout(() => {\n updateProjectChecklistCriteria(itemIds, siteSpecPdfUrl).catch(err => {\n console.error('Background spec-matching update failed (project-checklist):', err)\n })\n }, 0)\n}\n"]}
|