@dssp/project 1.0.0 → 1.0.2
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/bootstrap.d.ts +0 -0
- package/dist-client/bootstrap.js +2 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/lib/chatbot-widget.d.ts +53 -0
- package/dist-client/pages/lib/chatbot-widget.js +631 -0
- package/dist-client/pages/lib/chatbot-widget.js.map +1 -0
- package/dist-client/pages/lib/select2-component.d.ts +23 -0
- package/dist-client/pages/lib/select2-component.js +181 -0
- package/dist-client/pages/lib/select2-component.js.map +1 -0
- package/dist-client/pages/lib/waether.d.ts +7 -0
- package/dist-client/pages/lib/waether.js +156 -0
- package/dist-client/pages/lib/waether.js.map +1 -0
- package/dist-client/pages/project/component/pagenation.d.ts +18 -0
- package/dist-client/pages/project/component/pagenation.js +142 -0
- package/dist-client/pages/project/component/pagenation.js.map +1 -0
- package/dist-client/pages/project/component/project-update-header.d.ts +1 -0
- package/dist-client/pages/project/component/project-update-header.js +121 -0
- package/dist-client/pages/project/component/project-update-header.js.map +1 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-attachment-list-popup.d.ts +1 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-attachment-list-popup.js +308 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-attachment-list-popup.js.map +1 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-comment-list-popup.d.ts +1 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-comment-list-popup.js +356 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-comment-list-popup.js.map +1 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-create-popup.d.ts +1 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-create-popup.js +682 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-create-popup.js.map +1 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-view.d.ts +33 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-view.js +676 -0
- package/dist-client/pages/project/popup/checklist/task-checklist-view.js.map +1 -0
- package/dist-client/pages/project/popup/popup-plan-export.d.ts +10 -0
- package/dist-client/pages/project/popup/popup-plan-export.js +242 -0
- package/dist-client/pages/project/popup/popup-plan-export.js.map +1 -0
- package/dist-client/pages/project/popup/popup-plan-upload.d.ts +13 -0
- package/dist-client/pages/project/popup/popup-plan-upload.js +265 -0
- package/dist-client/pages/project/popup/popup-plan-upload.js.map +1 -0
- package/dist-client/pages/project/popup/popup-project-create.d.ts +12 -0
- package/dist-client/pages/project/popup/popup-project-create.js +152 -0
- package/dist-client/pages/project/popup/popup-project-create.js.map +1 -0
- package/dist-client/pages/project/popup/popup-task-upload.d.ts +9 -0
- package/dist-client/pages/project/popup/popup-task-upload.js +105 -0
- package/dist-client/pages/project/popup/popup-task-upload.js.map +1 -0
- package/dist-client/pages/project/project-completed-list.d.ts +23 -0
- package/dist-client/pages/project/project-completed-list.js +322 -0
- package/dist-client/pages/project/project-completed-list.js.map +1 -0
- package/dist-client/pages/project/project-detail.d.ts +47 -0
- package/dist-client/pages/project/project-detail.js +1065 -0
- package/dist-client/pages/project/project-detail.js.map +1 -0
- package/dist-client/pages/project/project-list.d.ts +201 -0
- package/dist-client/pages/project/project-list.js +410 -0
- package/dist-client/pages/project/project-list.js.map +1 -0
- package/dist-client/pages/project/project-plan-management.d.ts +33 -0
- package/dist-client/pages/project/project-plan-management.js +492 -0
- package/dist-client/pages/project/project-plan-management.js.map +1 -0
- package/dist-client/pages/project/project-setting-list.d.ts +31 -0
- package/dist-client/pages/project/project-setting-list.js +456 -0
- package/dist-client/pages/project/project-setting-list.js.map +1 -0
- package/dist-client/pages/project/project-task-list.d.ts +18 -0
- package/dist-client/pages/project/project-task-list.js +306 -0
- package/dist-client/pages/project/project-task-list.js.map +1 -0
- package/dist-client/pages/project/project-task.d.ts +51 -0
- package/dist-client/pages/project/project-task.js +692 -0
- package/dist-client/pages/project/project-task.js.map +1 -0
- package/dist-client/pages/project/project-update.d.ts +81 -0
- package/dist-client/pages/project/project-update.js +1477 -0
- package/dist-client/pages/project/project-update.js.map +1 -0
- package/dist-client/pages/resource/construction-detail-type-popup.d.ts +2 -0
- package/dist-client/pages/resource/construction-detail-type-popup.js +197 -0
- package/dist-client/pages/resource/construction-detail-type-popup.js.map +1 -0
- package/dist-client/pages/resource/construction-type-management.d.ts +34 -0
- package/dist-client/pages/resource/construction-type-management.js +214 -0
- package/dist-client/pages/resource/construction-type-management.js.map +1 -0
- package/dist-client/pages/resource/inspection-drawing-type-management.d.ts +48 -0
- package/dist-client/pages/resource/inspection-drawing-type-management.js +234 -0
- package/dist-client/pages/resource/inspection-drawing-type-management.js.map +1 -0
- package/dist-client/pages/resource/inspection-part-popup.d.ts +2 -0
- package/dist-client/pages/resource/inspection-part-popup.js +197 -0
- package/dist-client/pages/resource/inspection-part-popup.js.map +1 -0
- package/dist-client/pages/resource/resource-importer.d.ts +10 -0
- package/dist-client/pages/resource/resource-importer.js +101 -0
- package/dist-client/pages/resource/resource-importer.js.map +1 -0
- package/dist-client/pages/resource/resource-list-page.d.ts +62 -0
- package/dist-client/pages/resource/resource-list-page.js +331 -0
- package/dist-client/pages/resource/resource-list-page.js.map +1 -0
- package/dist-client/pages/resource/worker-type-management.d.ts +34 -0
- package/dist-client/pages/resource/worker-type-management.js +180 -0
- package/dist-client/pages/resource/worker-type-management.js.map +1 -0
- package/dist-client/pages/task/task-importer.d.ts +10 -0
- package/dist-client/pages/task/task-importer.js +101 -0
- package/dist-client/pages/task/task-importer.js.map +1 -0
- package/dist-client/pages/task/task-list-page.d.ts +62 -0
- package/dist-client/pages/task/task-list-page.js +322 -0
- package/dist-client/pages/task/task-list-page.js.map +1 -0
- package/dist-client/pages/task-resource/task-resource-importer.d.ts +10 -0
- package/dist-client/pages/task-resource/task-resource-importer.js +101 -0
- package/dist-client/pages/task-resource/task-resource-importer.js.map +1 -0
- package/dist-client/pages/task-resource/task-resource-list-page.d.ts +62 -0
- package/dist-client/pages/task-resource/task-resource-list-page.js +331 -0
- package/dist-client/pages/task-resource/task-resource-list-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +44 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/shared/domain-context.d.ts +18 -0
- package/dist-client/shared/domain-context.js +28 -0
- package/dist-client/shared/domain-context.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/viewparts/domain-switch.d.ts +19 -0
- package/dist-client/viewparts/domain-switch.js +274 -0
- package/dist-client/viewparts/domain-switch.js.map +1 -0
- package/dist-server/controllers/export-tasks.d.ts +7 -0
- package/dist-server/controllers/export-tasks.js +29 -0
- package/dist-server/controllers/export-tasks.js.map +1 -0
- package/dist-server/controllers/import-task.d.ts +3 -0
- package/dist-server/controllers/import-task.js +113 -0
- package/dist-server/controllers/import-task.js.map +1 -0
- package/dist-server/controllers/index.d.ts +0 -0
- package/dist-server/controllers/index.js +1 -0
- package/dist-server/controllers/index.js.map +1 -0
- package/dist-server/controllers/parse-excel.d.ts +2 -0
- package/dist-server/controllers/parse-excel.js +74 -0
- package/dist-server/controllers/parse-excel.js.map +1 -0
- package/dist-server/controllers/types.d.ts +18 -0
- package/dist-server/controllers/types.js +3 -0
- package/dist-server/controllers/types.js.map +1 -0
- package/dist-server/index.d.ts +3 -0
- package/dist-server/index.js +7 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/middlewares/index.d.ts +1 -0
- package/dist-server/middlewares/index.js +7 -0
- package/dist-server/middlewares/index.js.map +1 -0
- package/dist-server/migrations/1723861466413-seed-roles.d.ts +5 -0
- package/dist-server/migrations/1723861466413-seed-roles.js +117 -0
- package/dist-server/migrations/1723861466413-seed-roles.js.map +1 -0
- package/dist-server/migrations/1723861466414-seed-codes.d.ts +5 -0
- package/dist-server/migrations/1723861466414-seed-codes.js +150 -0
- package/dist-server/migrations/1723861466414-seed-codes.js.map +1 -0
- package/dist-server/migrations/1723861476419-seed-resources.d.ts +5 -0
- package/dist-server/migrations/1723861476419-seed-resources.js +57 -0
- package/dist-server/migrations/1723861476419-seed-resources.js.map +1 -0
- package/dist-server/migrations/1723861478420-seed-/bsample-project.d.ts +5 -0
- package/dist-server/migrations/1723861478420-seed-/bsample-project.js +68 -0
- package/dist-server/migrations/1723861478420-seed-/bsample-project.js.map +1 -0
- package/dist-server/migrations/1723861478421-seed-/bsample-tasks.d.ts +5 -0
- package/dist-server/migrations/1723861478421-seed-/bsample-tasks.js +189 -0
- package/dist-server/migrations/1723861478421-seed-/bsample-tasks.js.map +1 -0
- package/dist-server/migrations/index.d.ts +1 -0
- package/dist-server/migrations/index.js +12 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/routes.d.ts +1 -0
- package/dist-server/routes.js +105 -0
- package/dist-server/routes.js.map +1 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-mutation.d.ts +6 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-mutation.js +56 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-mutation.js.map +1 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-query.d.ts +9 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-query.js +53 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-query.js.map +1 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-type.d.ts +11 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-type.js +43 -0
- package/dist-server/service/construction-detail-type/construction-detail-type-type.js.map +1 -0
- package/dist-server/service/construction-detail-type/construction-detail-type.d.ts +15 -0
- package/dist-server/service/construction-detail-type/construction-detail-type.js +69 -0
- package/dist-server/service/construction-detail-type/construction-detail-type.js.map +1 -0
- package/dist-server/service/construction-detail-type/index.d.ts +5 -0
- package/dist-server/service/construction-detail-type/index.js +9 -0
- package/dist-server/service/construction-detail-type/index.js.map +1 -0
- package/dist-server/service/construction-type/construction-type-mutation.d.ts +6 -0
- package/dist-server/service/construction-type/construction-type-mutation.js +64 -0
- package/dist-server/service/construction-type/construction-type-mutation.js.map +1 -0
- package/dist-server/service/construction-type/construction-type-query.d.ts +13 -0
- package/dist-server/service/construction-type/construction-type-query.js +87 -0
- package/dist-server/service/construction-type/construction-type-query.js.map +1 -0
- package/dist-server/service/construction-type/construction-type-type.d.ts +12 -0
- package/dist-server/service/construction-type/construction-type-type.js +47 -0
- package/dist-server/service/construction-type/construction-type-type.js.map +1 -0
- package/dist-server/service/construction-type/construction-type.d.ts +19 -0
- package/dist-server/service/construction-type/construction-type.js +88 -0
- package/dist-server/service/construction-type/construction-type.js.map +1 -0
- package/dist-server/service/construction-type/index.d.ts +5 -0
- package/dist-server/service/construction-type/index.js +9 -0
- package/dist-server/service/construction-type/index.js.map +1 -0
- package/dist-server/service/index.d.ts +15 -0
- package/dist-server/service/index.js +59 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/inspection-drawing-type/index.d.ts +5 -0
- package/dist-server/service/inspection-drawing-type/index.js +9 -0
- package/dist-server/service/inspection-drawing-type/index.js.map +1 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-mutation.d.ts +6 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-mutation.js +64 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-mutation.js.map +1 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-query.d.ts +12 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-query.js +78 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-query.js.map +1 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-type.d.ts +10 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-type.js +39 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type-type.js.map +1 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type.d.ts +16 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type.js +72 -0
- package/dist-server/service/inspection-drawing-type/inspection-drawing-type.js.map +1 -0
- package/dist-server/service/inspection-part/index.d.ts +5 -0
- package/dist-server/service/inspection-part/index.js +9 -0
- package/dist-server/service/inspection-part/index.js.map +1 -0
- package/dist-server/service/inspection-part/inspection-part-mutation.d.ts +6 -0
- package/dist-server/service/inspection-part/inspection-part-mutation.js +55 -0
- package/dist-server/service/inspection-part/inspection-part-mutation.js.map +1 -0
- package/dist-server/service/inspection-part/inspection-part-query.d.ts +10 -0
- package/dist-server/service/inspection-part/inspection-part-query.js +66 -0
- package/dist-server/service/inspection-part/inspection-part-query.js.map +1 -0
- package/dist-server/service/inspection-part/inspection-part-type.d.ts +11 -0
- package/dist-server/service/inspection-part/inspection-part-type.js +43 -0
- package/dist-server/service/inspection-part/inspection-part-type.js.map +1 -0
- package/dist-server/service/inspection-part/inspection-part.d.ts +15 -0
- package/dist-server/service/inspection-part/inspection-part.js +69 -0
- package/dist-server/service/inspection-part/inspection-part.js.map +1 -0
- package/dist-server/service/manager/index.d.ts +7 -0
- package/dist-server/service/manager/index.js +11 -0
- package/dist-server/service/manager/index.js.map +1 -0
- package/dist-server/service/manager/manager-mutation.d.ts +5 -0
- package/dist-server/service/manager/manager-mutation.js +39 -0
- package/dist-server/service/manager/manager-mutation.js.map +1 -0
- package/dist-server/service/manager/manager-query.d.ts +4 -0
- package/dist-server/service/manager/manager-query.js +35 -0
- package/dist-server/service/manager/manager-query.js.map +1 -0
- package/dist-server/service/manager/manager-type.d.ts +15 -0
- package/dist-server/service/manager/manager-type.js +62 -0
- package/dist-server/service/manager/manager-type.js.map +1 -0
- package/dist-server/service/manager/manager.d.ts +8 -0
- package/dist-server/service/manager/manager.js +41 -0
- package/dist-server/service/manager/manager.js.map +1 -0
- package/dist-server/service/project/index.d.ts +5 -0
- package/dist-server/service/project/index.js +9 -0
- package/dist-server/service/project/index.js.map +1 -0
- package/dist-server/service/project/issue-project-code.d.ts +7 -0
- package/dist-server/service/project/issue-project-code.js +27 -0
- package/dist-server/service/project/issue-project-code.js.map +1 -0
- package/dist-server/service/project/project-mutation.d.ts +28 -0
- package/dist-server/service/project/project-mutation.js +499 -0
- package/dist-server/service/project/project-mutation.js.map +1 -0
- package/dist-server/service/project/project-query.d.ts +31 -0
- package/dist-server/service/project/project-query.js +307 -0
- package/dist-server/service/project/project-query.js.map +1 -0
- package/dist-server/service/project/project-type.d.ts +42 -0
- package/dist-server/service/project/project-type.js +152 -0
- package/dist-server/service/project/project-type.js.map +1 -0
- package/dist-server/service/project/project.d.ts +66 -0
- package/dist-server/service/project/project.js +247 -0
- package/dist-server/service/project/project.js.map +1 -0
- package/dist-server/service/resource/index.d.ts +6 -0
- package/dist-server/service/resource/index.js +10 -0
- package/dist-server/service/resource/index.js.map +1 -0
- package/dist-server/service/resource/resource-mutation.d.ts +10 -0
- package/dist-server/service/resource/resource-mutation.js +131 -0
- package/dist-server/service/resource/resource-mutation.js.map +1 -0
- package/dist-server/service/resource/resource-query.d.ts +11 -0
- package/dist-server/service/resource/resource-query.js +76 -0
- package/dist-server/service/resource/resource-query.js.map +1 -0
- package/dist-server/service/resource/resource-type.d.ts +17 -0
- package/dist-server/service/resource/resource-type.js +65 -0
- package/dist-server/service/resource/resource-type.js.map +1 -0
- package/dist-server/service/resource/resource.d.ts +24 -0
- package/dist-server/service/resource/resource.js +93 -0
- package/dist-server/service/resource/resource.js.map +1 -0
- package/dist-server/service/task/index.d.ts +5 -0
- package/dist-server/service/task/index.js +9 -0
- package/dist-server/service/task/index.js.map +1 -0
- package/dist-server/service/task/task-mutation.d.ts +10 -0
- package/dist-server/service/task/task-mutation.js +135 -0
- package/dist-server/service/task/task-mutation.js.map +1 -0
- package/dist-server/service/task/task-query.d.ts +21 -0
- package/dist-server/service/task/task-query.js +219 -0
- package/dist-server/service/task/task-query.js.map +1 -0
- package/dist-server/service/task/task-type.d.ts +31 -0
- package/dist-server/service/task/task-type.js +114 -0
- package/dist-server/service/task/task-type.js.map +1 -0
- package/dist-server/service/task/task.d.ts +35 -0
- package/dist-server/service/task/task.js +151 -0
- package/dist-server/service/task/task.js.map +1 -0
- package/dist-server/service/task-checklist-binding/index.d.ts +5 -0
- package/dist-server/service/task-checklist-binding/index.js +9 -0
- package/dist-server/service/task-checklist-binding/index.js.map +1 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-mutation.d.ts +5 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-mutation.js +188 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-mutation.js.map +1 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-query.d.ts +8 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-query.js +61 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-query.js.map +1 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-type.d.ts +15 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-type.js +57 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding-type.js.map +1 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding.d.ts +22 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding.js +106 -0
- package/dist-server/service/task-checklist-binding/task-checklist-binding.js.map +1 -0
- package/dist-server/service/task-resource/index.d.ts +6 -0
- package/dist-server/service/task-resource/index.js +10 -0
- package/dist-server/service/task-resource/index.js.map +1 -0
- package/dist-server/service/task-resource/task-resource-mutation.d.ts +10 -0
- package/dist-server/service/task-resource/task-resource-mutation.js +132 -0
- package/dist-server/service/task-resource/task-resource-mutation.js.map +1 -0
- package/dist-server/service/task-resource/task-resource-query.d.ts +7 -0
- package/dist-server/service/task-resource/task-resource-query.js +45 -0
- package/dist-server/service/task-resource/task-resource-query.js.map +1 -0
- package/dist-server/service/task-resource/task-resource-type.d.ts +17 -0
- package/dist-server/service/task-resource/task-resource-type.js +65 -0
- package/dist-server/service/task-resource/task-resource-type.js.map +1 -0
- package/dist-server/service/task-resource/task-resource.d.ts +13 -0
- package/dist-server/service/task-resource/task-resource.js +59 -0
- package/dist-server/service/task-resource/task-resource.js.map +1 -0
- package/dist-server/service/worker-type/index.d.ts +5 -0
- package/dist-server/service/worker-type/index.js +9 -0
- package/dist-server/service/worker-type/index.js.map +1 -0
- package/dist-server/service/worker-type/worker-type-mutation.d.ts +6 -0
- package/dist-server/service/worker-type/worker-type-mutation.js +64 -0
- package/dist-server/service/worker-type/worker-type-mutation.js.map +1 -0
- package/dist-server/service/worker-type/worker-type-query.d.ts +11 -0
- package/dist-server/service/worker-type/worker-type-query.js +73 -0
- package/dist-server/service/worker-type/worker-type-query.js.map +1 -0
- package/dist-server/service/worker-type/worker-type-type.d.ts +11 -0
- package/dist-server/service/worker-type/worker-type-type.js +43 -0
- package/dist-server/service/worker-type/worker-type-type.js.map +1 -0
- package/dist-server/service/worker-type/worker-type.d.ts +16 -0
- package/dist-server/service/worker-type/worker-type.js +77 -0
- package/dist-server/service/worker-type/worker-type.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProjectMutation = void 0;
|
|
4
|
+
exports.createAttachmentAfterDelete = createAttachmentAfterDelete;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const type_graphql_1 = require("type-graphql");
|
|
7
|
+
const typeorm_1 = require("typeorm");
|
|
8
|
+
const shell_1 = require("@things-factory/shell");
|
|
9
|
+
const attachment_base_1 = require("@things-factory/attachment-base");
|
|
10
|
+
const project_1 = require("./project");
|
|
11
|
+
const project_type_1 = require("./project-type");
|
|
12
|
+
const issue_project_code_1 = require("./issue-project-code");
|
|
13
|
+
const building_complex_1 = require("@dssp/building-complex");
|
|
14
|
+
const headless_pdf_to_image_1 = require("@things-factory/board-service/dist-server/controllers/headless-pdf-to-image");
|
|
15
|
+
const parse_excel_1 = require("../../controllers/parse-excel");
|
|
16
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
17
|
+
const path = tslib_1.__importStar(require("path"));
|
|
18
|
+
const archiver_1 = tslib_1.__importDefault(require("archiver"));
|
|
19
|
+
const env_1 = require("@things-factory/env");
|
|
20
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
21
|
+
const PROJECT_DOMAIN_EXT_TYPE = 'project';
|
|
22
|
+
const PROJECT_TEMPLATE_SUBDOMAIN = 'project-template';
|
|
23
|
+
const projectType = env_1.config.get('projectType', project_1.ProjectType.DSSP);
|
|
24
|
+
let ProjectMutation = class ProjectMutation {
|
|
25
|
+
async createProject(project, context) {
|
|
26
|
+
const { domain, user, tx } = context.state;
|
|
27
|
+
const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
|
|
28
|
+
const buildingComplexRepo = (0, shell_1.getRepository)(building_complex_1.BuildingComplex, tx);
|
|
29
|
+
const newBuildingComplex = await buildingComplexRepo.save({
|
|
30
|
+
domain,
|
|
31
|
+
creator: user,
|
|
32
|
+
updater: user
|
|
33
|
+
});
|
|
34
|
+
const result = await projectRepo.save({
|
|
35
|
+
name: project.name,
|
|
36
|
+
documentNaming: project.name,
|
|
37
|
+
buildingComplex: newBuildingComplex,
|
|
38
|
+
projectType,
|
|
39
|
+
domain,
|
|
40
|
+
creator: user,
|
|
41
|
+
updater: user
|
|
42
|
+
});
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
async updateProject(project, context) {
|
|
46
|
+
var _a;
|
|
47
|
+
const { user, tx } = context.state;
|
|
48
|
+
const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
|
|
49
|
+
const buildingComplexRepo = (0, shell_1.getRepository)(building_complex_1.BuildingComplex, tx);
|
|
50
|
+
const buildingRepo = (0, shell_1.getRepository)(building_complex_1.Building, tx);
|
|
51
|
+
const buildingLevelRepo = (0, shell_1.getRepository)(building_complex_1.BuildingLevel, tx);
|
|
52
|
+
const buildingComplex = project.buildingComplex;
|
|
53
|
+
const buildings = ((_a = project.buildingComplex) === null || _a === void 0 ? void 0 : _a.buildings) || [];
|
|
54
|
+
// 1. 프로젝트 수정
|
|
55
|
+
const projectResult = await projectRepo.save(Object.assign(Object.assign({}, project), { updater: user }));
|
|
56
|
+
// 1-1. 프로젝트명이 변경되면 연동된 테넌트 Domain 이름도 함께 동기화.
|
|
57
|
+
// 전용 Domain 은 테넌트 승격(promoteProjectToTenant) 시에만 생성되므로
|
|
58
|
+
// code 를 보유한 프로젝트만 대상. Domain 은 subdomain=code, extType='project' 로 식별.
|
|
59
|
+
if (project.name !== undefined) {
|
|
60
|
+
const domainRepo = (0, shell_1.getRepository)(shell_1.Domain, tx);
|
|
61
|
+
const { code } = (await projectRepo.findOne({ where: { id: project.id }, select: ['code'] })) || {};
|
|
62
|
+
if (code) {
|
|
63
|
+
const linkedDomain = await domainRepo.findOne({
|
|
64
|
+
where: { subdomain: code, extType: PROJECT_DOMAIN_EXT_TYPE }
|
|
65
|
+
});
|
|
66
|
+
if (linkedDomain && linkedDomain.name !== project.name) {
|
|
67
|
+
await domainRepo.save(Object.assign(Object.assign({}, linkedDomain), { name: project.name }));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// buildingComplex 가 patch 에 없는 경우 (예: state 만 변경하는 부분 patch) 는 단지 정보·
|
|
72
|
+
// 동/층·첨부파일 관련 후속 단계 모두 skip.
|
|
73
|
+
if (!buildingComplex) {
|
|
74
|
+
return projectResult;
|
|
75
|
+
}
|
|
76
|
+
// 2. 단지 정보 수정
|
|
77
|
+
await buildingComplexRepo.save(Object.assign(Object.assign({}, buildingComplex), { updater: user }));
|
|
78
|
+
// 2-1. 프로젝트 메인 이미지 첨부파일 나머지 삭제 후 저장
|
|
79
|
+
await createAttachmentAfterDelete(context, project === null || project === void 0 ? void 0 : project.mainPhotoUpload, project.id, project_1.Project.name);
|
|
80
|
+
// 2-2. 단지 BIM 이미지 첨부파일 나머지 삭제 후 저장
|
|
81
|
+
if (buildingComplex.id) {
|
|
82
|
+
await createAttachmentAfterDelete(context, buildingComplex === null || buildingComplex === void 0 ? void 0 : buildingComplex.drawingUpload, buildingComplex.id, building_complex_1.BuildingComplex.name + '_bim');
|
|
83
|
+
}
|
|
84
|
+
// 3. 동의 층 정보가 바뀌었으면 층 초기화 후 다시 생성
|
|
85
|
+
const originBuilding = buildingComplex.id
|
|
86
|
+
? await buildingRepo.findBy({ buildingComplex: { id: buildingComplex.id } })
|
|
87
|
+
: [];
|
|
88
|
+
const afterBuilding = buildings.reduce((acc, building) => (Object.assign(Object.assign({}, acc), { [building.name]: building.floorCount })), {}); // 비교용으로 수정된 동 정보 데이터 파싱
|
|
89
|
+
const afterBasement = buildings.reduce((acc, building) => (Object.assign(Object.assign({}, acc), { [building.name]: building.basementFloorCount || 0 })), {}); // 비교용으로 수정된 지하 정보 데이터 파싱
|
|
90
|
+
const isBuidlingChanged = originBuilding.some(building => afterBuilding[building.name] !== building.floorCount || afterBasement[building.name] !== building.basementFloorCount); // 층 개수가 달라진 동이 있는지 확인
|
|
91
|
+
// 동의 층 개수가 달라지면 모든 층의 데이터 제거 후 생성
|
|
92
|
+
if (isBuidlingChanged || originBuilding.length !== buildings.length) {
|
|
93
|
+
// 3-1. 기존 동/층 첨부파일 및 데이터 제거
|
|
94
|
+
const buildingIds = originBuilding.map((building) => building.id);
|
|
95
|
+
const buildingLevels = await buildingLevelRepo.findBy({ building: { id: (0, typeorm_1.In)(buildingIds) } });
|
|
96
|
+
const buildingLevelIds = buildingLevels.map((buildingLevel) => buildingLevel.id);
|
|
97
|
+
await buildingLevelRepo.softDelete({ building: { id: (0, typeorm_1.In)(buildingIds) } });
|
|
98
|
+
await buildingRepo.softDelete({ id: (0, typeorm_1.In)(buildingIds) });
|
|
99
|
+
await (0, attachment_base_1.deleteAttachmentsByRef)(null, { refBys: [...buildingIds, ...buildingLevelIds] }, context);
|
|
100
|
+
// 3-2. 단지 내 동 정보들 생성
|
|
101
|
+
for (let buildingKey in buildings) {
|
|
102
|
+
const building = buildings[buildingKey];
|
|
103
|
+
const newBuilding = await buildingRepo.save({
|
|
104
|
+
buildingComplex: buildingComplex,
|
|
105
|
+
name: building.name,
|
|
106
|
+
floorCount: building.floorCount,
|
|
107
|
+
hasBasement: building.hasBasement,
|
|
108
|
+
basementFloorCount: building.hasBasement ? building.basementFloorCount : 0,
|
|
109
|
+
updater: user,
|
|
110
|
+
creator: user
|
|
111
|
+
});
|
|
112
|
+
// 3-3. 동별로 for문 돌면서 층 데이터 개수대로 생성
|
|
113
|
+
for (let i = 1; i <= building.floorCount; i++) {
|
|
114
|
+
await buildingLevelRepo.save({ building: newBuilding, floor: i, creator: user });
|
|
115
|
+
}
|
|
116
|
+
// 3-4. 지하층 층 데이터 생성
|
|
117
|
+
if (building.hasBasement) {
|
|
118
|
+
for (let i = 1; i <= building.basementFloorCount; i++) {
|
|
119
|
+
await buildingLevelRepo.save({ building: newBuilding, floor: -i, creator: user });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return projectResult;
|
|
125
|
+
}
|
|
126
|
+
async updateProjectPlan(project, context) {
|
|
127
|
+
var _a, _b;
|
|
128
|
+
const { user, tx, domain } = context.state;
|
|
129
|
+
const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
|
|
130
|
+
const buildingComplexRepo = (0, shell_1.getRepository)(building_complex_1.BuildingComplex, tx);
|
|
131
|
+
const buildingRepo = (0, shell_1.getRepository)(building_complex_1.Building, tx);
|
|
132
|
+
const buildingLevelRepo = (0, shell_1.getRepository)(building_complex_1.BuildingLevel, tx);
|
|
133
|
+
const attachmentRepo = (0, shell_1.getRepository)(attachment_base_1.Attachment, tx);
|
|
134
|
+
const buildingComplex = project.buildingComplex;
|
|
135
|
+
const buildings = ((_a = project.buildingComplex) === null || _a === void 0 ? void 0 : _a.buildings) || [];
|
|
136
|
+
// 1. 프로젝트 수정 시간 업데이트
|
|
137
|
+
const projectResult = await projectRepo.save(Object.assign(Object.assign({}, project), { updater: user }));
|
|
138
|
+
// 2. 단지 축척 정보 수정
|
|
139
|
+
await buildingComplexRepo.save(Object.assign(Object.assign({}, buildingComplex), { updater: user }));
|
|
140
|
+
for (let buildingKey in buildings) {
|
|
141
|
+
const building = buildings[buildingKey];
|
|
142
|
+
for (let buildingLevelKey in building.buildingLevels) {
|
|
143
|
+
const buildingLevel = building.buildingLevels[buildingLevelKey];
|
|
144
|
+
// 3. 층별 도면 이미지 저장
|
|
145
|
+
const mainDrawingAttatchment = await createAttachmentAfterDelete(context, buildingLevel.mainDrawingUpload, buildingLevel.id, building_complex_1.BuildingLevel.name + '_mainDrawing');
|
|
146
|
+
// 첨부된 PDF가 있으면 PDF 파일대로 썸네일 생성
|
|
147
|
+
if (mainDrawingAttatchment) {
|
|
148
|
+
const mainDrawingUpload = await buildingLevel.mainDrawingUpload;
|
|
149
|
+
const pdfPath = `/${attachment_base_1.ATTACHMENT_PATH}/${mainDrawingAttatchment.path}`; // TODO ATTACHMENT_PATH 제거, mainDrawingAttachment.fullpath 로 해도 될 것 같은데...
|
|
150
|
+
const fileName = mainDrawingUpload.filename.replace('.pdf', '');
|
|
151
|
+
const pngFile = await (0, headless_pdf_to_image_1.pdfToImage)({ pdfPath, fileName });
|
|
152
|
+
await createAttachmentAfterDelete(context, pngFile, buildingLevel.id, building_complex_1.BuildingLevel.name + '_mainDrawing_image');
|
|
153
|
+
const pngThumbnailFile = await (0, headless_pdf_to_image_1.pdfToImage)({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } });
|
|
154
|
+
await createAttachmentAfterDelete(context, pngThumbnailFile, buildingLevel.id, building_complex_1.BuildingLevel.name + '_mainDrawing_thumbnail');
|
|
155
|
+
}
|
|
156
|
+
// 3-1. 입면도 파일 저장
|
|
157
|
+
const elevationDrawingAttatchment = await createAttachmentAfterDelete(context, buildingLevel.elevationDrawingUpload, buildingLevel.id, building_complex_1.BuildingLevel.name + '_elevationDrawing');
|
|
158
|
+
if (elevationDrawingAttatchment) {
|
|
159
|
+
const elevationDrawingUpload = await buildingLevel.elevationDrawingUpload;
|
|
160
|
+
const pdfPath = `/${attachment_base_1.ATTACHMENT_PATH}/${elevationDrawingAttatchment.path}`;
|
|
161
|
+
const fileName = elevationDrawingUpload.filename.replace('.pdf', '');
|
|
162
|
+
const pngThumbnailFile = await (0, headless_pdf_to_image_1.pdfToImage)({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } });
|
|
163
|
+
await createAttachmentAfterDelete(context, pngThumbnailFile, buildingLevel.id, building_complex_1.BuildingLevel.name + '_elevationDrawing_thumbnail');
|
|
164
|
+
}
|
|
165
|
+
// 3-2. 철근배분도 파일 저장
|
|
166
|
+
const rebarDistributionDrawingAttatchment = await createAttachmentAfterDelete(context, buildingLevel.rebarDistributionDrawingUpload, buildingLevel.id, building_complex_1.BuildingLevel.name + '_rebarDistributionDrawing');
|
|
167
|
+
if (rebarDistributionDrawingAttatchment) {
|
|
168
|
+
const rebarDistributionDrawingUpload = await buildingLevel.rebarDistributionDrawingUpload;
|
|
169
|
+
const pdfPath = `/${attachment_base_1.ATTACHMENT_PATH}/${rebarDistributionDrawingAttatchment.path}`;
|
|
170
|
+
const fileName = rebarDistributionDrawingUpload.filename.replace('.pdf', '');
|
|
171
|
+
const pngThumbnailFile = await (0, headless_pdf_to_image_1.pdfToImage)({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } });
|
|
172
|
+
await createAttachmentAfterDelete(context, pngThumbnailFile, buildingLevel.id, building_complex_1.BuildingLevel.name + '_rebarDistributionDrawing_thumbnail');
|
|
173
|
+
}
|
|
174
|
+
// 3-3. 기타 도면 파일 저장
|
|
175
|
+
const etcDrawingRefType = building_complex_1.BuildingLevel.name + '_etcDrawings';
|
|
176
|
+
// 기존 첨부 파일 중 제거된 것들 삭제
|
|
177
|
+
const etcDrawings = await attachmentRepo.find({ where: { refType: etcDrawingRefType, refBy: buildingLevel.id } });
|
|
178
|
+
for (let etcDrawing of etcDrawings) {
|
|
179
|
+
if (!buildingLevel.etcDrawingIds.includes(etcDrawing.id)) {
|
|
180
|
+
await (0, attachment_base_1.deleteAttachment)(null, { id: etcDrawing.id }, context);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// 업로드
|
|
184
|
+
if (((_b = buildingLevel.etcDrawingsUpload) === null || _b === void 0 ? void 0 : _b.length) > 0) {
|
|
185
|
+
for (let etcDrawing of buildingLevel.etcDrawingsUpload) {
|
|
186
|
+
await (0, attachment_base_1.createAttachment)(null, { attachment: { file: etcDrawing, refType: etcDrawingRefType, refBy: buildingLevel.id } }, context);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// 3-4. 층 업데이트 시간 갱신
|
|
190
|
+
await buildingLevelRepo.save(Object.assign(Object.assign({}, buildingLevel), { updater: user }));
|
|
191
|
+
}
|
|
192
|
+
// 4. 동별 도면 이미지 저장
|
|
193
|
+
await createAttachmentAfterDelete(context, building === null || building === void 0 ? void 0 : building.drawingUpload, building.id, building_complex_1.Building.name);
|
|
194
|
+
// 4-1. 동 업데이트 시간 갱신
|
|
195
|
+
await buildingRepo.save(Object.assign(Object.assign({}, building), { updater: user }));
|
|
196
|
+
}
|
|
197
|
+
return projectResult;
|
|
198
|
+
}
|
|
199
|
+
async uploadProjectScheduleTable(param, context) {
|
|
200
|
+
var _a, e_1, _b, _c;
|
|
201
|
+
const { user, tx } = context.state;
|
|
202
|
+
const { projectId, scheduleTable } = param;
|
|
203
|
+
const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
|
|
204
|
+
const project = await projectRepo.findOne({
|
|
205
|
+
where: { id: projectId }
|
|
206
|
+
});
|
|
207
|
+
const { createReadStream, filename, mimetype } = await scheduleTable;
|
|
208
|
+
const stream = createReadStream();
|
|
209
|
+
const chunks = [];
|
|
210
|
+
try {
|
|
211
|
+
for (var _d = true, stream_1 = tslib_1.__asyncValues(stream), stream_1_1; stream_1_1 = await stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
|
|
212
|
+
_c = stream_1_1.value;
|
|
213
|
+
_d = false;
|
|
214
|
+
const chunk = _c;
|
|
215
|
+
chunks.push(chunk);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
219
|
+
finally {
|
|
220
|
+
try {
|
|
221
|
+
if (!_d && !_a && (_b = stream_1.return)) await _b.call(stream_1);
|
|
222
|
+
}
|
|
223
|
+
finally { if (e_1) throw e_1.error; }
|
|
224
|
+
}
|
|
225
|
+
const buffer = Buffer.concat(chunks);
|
|
226
|
+
await (0, parse_excel_1.parseExcelAndImportTasks)(buffer, project, context);
|
|
227
|
+
// await parseExcelAndImportTasks(attachment.fullpath, project, context)
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
async deleteProject(id, context) {
|
|
231
|
+
const { tx } = context.state;
|
|
232
|
+
await (0, shell_1.getRepository)(project_1.Project, tx).delete({ id });
|
|
233
|
+
await (0, attachment_base_1.deleteAttachmentsByRef)(null, { refBys: [id] }, context);
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
async downloadPlanFiles(fileIds, context) {
|
|
237
|
+
const { tx } = context.state;
|
|
238
|
+
if (!fileIds || fileIds.length === 0) {
|
|
239
|
+
throw new Error('다운로드할 파일을 선택해주세요.');
|
|
240
|
+
}
|
|
241
|
+
// 첨부파일 정보 조회
|
|
242
|
+
const attachments = await tx.getRepository(attachment_base_1.Attachment).find({
|
|
243
|
+
where: { id: (0, typeorm_1.In)(fileIds) }
|
|
244
|
+
});
|
|
245
|
+
if (attachments.length === 0) {
|
|
246
|
+
throw new Error('선택된 파일을 찾을 수 없습니다.');
|
|
247
|
+
}
|
|
248
|
+
// ZIP 파일 생성 - 동과 층 정보를 포함한 파일명
|
|
249
|
+
const zipFileName = `download.zip`;
|
|
250
|
+
const tempDir = path.join(process.cwd(), 'temp');
|
|
251
|
+
const zipFilePath = path.join(tempDir, zipFileName);
|
|
252
|
+
// temp 디렉토리 생성
|
|
253
|
+
if (!fs.existsSync(tempDir)) {
|
|
254
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
255
|
+
}
|
|
256
|
+
const output = fs.createWriteStream(zipFilePath);
|
|
257
|
+
const archive = (0, archiver_1.default)('zip', {
|
|
258
|
+
zlib: { level: 9 } // 최대 압축
|
|
259
|
+
});
|
|
260
|
+
return new Promise(async (resolve, reject) => {
|
|
261
|
+
output.on('close', () => {
|
|
262
|
+
// ZIP 파일을 Base64로 인코딩하여 반환
|
|
263
|
+
const zipBuffer = fs.readFileSync(zipFilePath);
|
|
264
|
+
const base64Data = zipBuffer.toString('base64');
|
|
265
|
+
const dataUrl = `data:application/zip;base64,${base64Data}`;
|
|
266
|
+
// 임시 파일 삭제
|
|
267
|
+
fs.unlinkSync(zipFilePath);
|
|
268
|
+
resolve(dataUrl);
|
|
269
|
+
});
|
|
270
|
+
archive.on('error', err => {
|
|
271
|
+
reject(err);
|
|
272
|
+
});
|
|
273
|
+
archive.pipe(output);
|
|
274
|
+
try {
|
|
275
|
+
// 각 첨부파일을 ZIP에 추가
|
|
276
|
+
for (const attachment of attachments) {
|
|
277
|
+
try {
|
|
278
|
+
const fileUrl = `http://localhost:3000${attachment.fullpath}`;
|
|
279
|
+
const response = await fetch(fileUrl);
|
|
280
|
+
if (response.ok) {
|
|
281
|
+
const buffer = await response.arrayBuffer();
|
|
282
|
+
const fileName = attachment.name || path.basename(attachment.fullpath);
|
|
283
|
+
archive.append(Buffer.from(buffer), { name: fileName });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
console.error('Error processing attachment:', attachment.fullpath, error);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
archive.finalize();
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
reject(error);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async promoteProjectToTenant(projectId, context) {
|
|
298
|
+
var _a;
|
|
299
|
+
const { user, tx } = context.state;
|
|
300
|
+
const projectRepo = tx.getRepository(project_1.Project);
|
|
301
|
+
const domainRepo = tx.getRepository(shell_1.Domain);
|
|
302
|
+
const project = await projectRepo.findOne({
|
|
303
|
+
where: { id: projectId }
|
|
304
|
+
});
|
|
305
|
+
if (!project) {
|
|
306
|
+
throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`);
|
|
307
|
+
}
|
|
308
|
+
// 이미 활성 테넌트인 경우 차단 (강등 상태는 재승격 허용)
|
|
309
|
+
if (project.code) {
|
|
310
|
+
const existing = await domainRepo.findOne({
|
|
311
|
+
where: { subdomain: project.code, extType: PROJECT_DOMAIN_EXT_TYPE }
|
|
312
|
+
});
|
|
313
|
+
if (existing) {
|
|
314
|
+
throw new Error(`이미 테넌트로 승격된 프로젝트입니다: ${project.code}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// 템플릿 Domain 조회 — 모든 프로젝트 도메인의 parent. Role 상속의 단일 진실.
|
|
318
|
+
// subdomain 만으로 식별 — 어드민에서 수동 생성된 도메인의 extType 이
|
|
319
|
+
// NULL/대소문자 차이로 누락되어 false-negative 가 자주 발생하므로.
|
|
320
|
+
const templateDomain = await domainRepo.findOne({
|
|
321
|
+
where: { subdomain: PROJECT_TEMPLATE_SUBDOMAIN }
|
|
322
|
+
});
|
|
323
|
+
if (!templateDomain) {
|
|
324
|
+
throw new Error(`프로젝트 템플릿 Domain (subdomain='${PROJECT_TEMPLATE_SUBDOMAIN}') 이 존재하지 않습니다. 어드민에서 먼저 생성하세요.`);
|
|
325
|
+
}
|
|
326
|
+
// 코드가 없으면 발번 (재승격 시에는 기존 코드 재사용)
|
|
327
|
+
const code = (_a = project.code) !== null && _a !== void 0 ? _a : (await (0, issue_project_code_1.issueProjectCode)(tx));
|
|
328
|
+
await domainRepo.save({
|
|
329
|
+
name: project.name,
|
|
330
|
+
subdomain: code,
|
|
331
|
+
extType: PROJECT_DOMAIN_EXT_TYPE,
|
|
332
|
+
parent: templateDomain,
|
|
333
|
+
owner: user.id
|
|
334
|
+
});
|
|
335
|
+
if (!project.code) {
|
|
336
|
+
await projectRepo.update({ id: projectId }, { code });
|
|
337
|
+
project.code = code;
|
|
338
|
+
}
|
|
339
|
+
return project;
|
|
340
|
+
}
|
|
341
|
+
async demoteProjectTenant(projectId, context) {
|
|
342
|
+
const { tx } = context.state;
|
|
343
|
+
const project = await tx.getRepository(project_1.Project).findOne({
|
|
344
|
+
where: { id: projectId }
|
|
345
|
+
});
|
|
346
|
+
if (!project) {
|
|
347
|
+
throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`);
|
|
348
|
+
}
|
|
349
|
+
if (!project.code) {
|
|
350
|
+
throw new Error('승격된 적이 없는 프로젝트입니다.');
|
|
351
|
+
}
|
|
352
|
+
const domainRepo = tx.getRepository(shell_1.Domain);
|
|
353
|
+
const tenantDomain = await domainRepo.findOne({
|
|
354
|
+
where: { subdomain: project.code, extType: PROJECT_DOMAIN_EXT_TYPE }
|
|
355
|
+
});
|
|
356
|
+
if (!tenantDomain) {
|
|
357
|
+
throw new Error('이미 강등된 프로젝트입니다.');
|
|
358
|
+
}
|
|
359
|
+
await domainRepo.softDelete({ id: tenantDomain.id });
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* 비밀번호 확인 후 프로젝트 삭제 — **수퍼유저 전용**(@privilege superUserGranted).
|
|
364
|
+
*
|
|
365
|
+
* 안전장치 3중:
|
|
366
|
+
* 1) @privilege(superUserGranted: true) — 수퍼유저만 호출 가능 (UI 노출 + API 양쪽 방어).
|
|
367
|
+
* 2) 호출자 본인 비밀번호 재확인 — 실수/방치 세션 오작동 방지.
|
|
368
|
+
* 3) **활성 테넌트 상태에서는 삭제 불가** — 먼저 강등해야 함. 활성 테넌트는 라이브
|
|
369
|
+
* 프로젝트 워크스페이스(Domain)를 갖고 있어, 강등으로 그 도메인을 숨긴 뒤에만 삭제.
|
|
370
|
+
*
|
|
371
|
+
* 테넌트 Domain 자체는 지우지 않는다 — domains 는 100+ 테이블이 FK 로 참조하므로 hard-delete
|
|
372
|
+
* 가 불가(프레임워크의 demote 도 softDelete 만 수행). 강등으로 soft-delete 된 상태 그대로 두면
|
|
373
|
+
* 어디에도 노출되지 않고, Project.code 는 순차 발번이라 재사용 충돌도 없다.
|
|
374
|
+
*/
|
|
375
|
+
async deleteProjectWithPassword(projectId, password, context) {
|
|
376
|
+
const { tx, user } = context.state;
|
|
377
|
+
// 1) 호출자 본인 비밀번호 재확인
|
|
378
|
+
const dbUser = await (0, shell_1.getRepository)(auth_base_1.User, tx).findOne({ where: { id: user.id } });
|
|
379
|
+
if (!dbUser || !auth_base_1.User.verify(dbUser.password, password, dbUser.salt)) {
|
|
380
|
+
throw new Error('비밀번호가 일치하지 않습니다.');
|
|
381
|
+
}
|
|
382
|
+
const project = await (0, shell_1.getRepository)(project_1.Project, tx).findOne({ where: { id: projectId } });
|
|
383
|
+
if (!project) {
|
|
384
|
+
throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`);
|
|
385
|
+
}
|
|
386
|
+
// 2) 활성 테넌트 상태면 삭제 불가 (강등 상태에서만)
|
|
387
|
+
if (project.code) {
|
|
388
|
+
const activeTenant = await (0, shell_1.getRepository)(shell_1.Domain, tx).findOne({
|
|
389
|
+
where: { subdomain: project.code, extType: PROJECT_DOMAIN_EXT_TYPE }
|
|
390
|
+
});
|
|
391
|
+
if (activeTenant) {
|
|
392
|
+
throw new Error('활성 테넌트 상태에서는 삭제할 수 없습니다. 먼저 테넌트를 강등하세요.');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// 3) 프로젝트 soft-delete (Project.deletedAt). 프레임워크의 도메인 강등과 동일하게
|
|
396
|
+
// 복구 가능한 soft-delete 를 쓴다 — 첨부/KPI 데이터/테넌트 Domain 은 그대로 보존되어
|
|
397
|
+
// 필요 시 복구 가능. (soft-delete 시 목록/조회에서는 deletedAt IS NULL 필터로 자동 제외)
|
|
398
|
+
await (0, shell_1.getRepository)(project_1.Project, tx).softDelete({ id: projectId });
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
exports.ProjectMutation = ProjectMutation;
|
|
403
|
+
tslib_1.__decorate([
|
|
404
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
405
|
+
(0, type_graphql_1.Mutation)(returns => project_1.Project, { description: '프로젝트 생성' }),
|
|
406
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('project')),
|
|
407
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
408
|
+
tslib_1.__metadata("design:type", Function),
|
|
409
|
+
tslib_1.__metadata("design:paramtypes", [project_type_1.NewProject, Object]),
|
|
410
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
411
|
+
], ProjectMutation.prototype, "createProject", null);
|
|
412
|
+
tslib_1.__decorate([
|
|
413
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
414
|
+
(0, type_graphql_1.Mutation)(returns => project_1.Project, { description: '프로젝트 업데이트 (중복 사용 금지)' }),
|
|
415
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('project')),
|
|
416
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
417
|
+
tslib_1.__metadata("design:type", Function),
|
|
418
|
+
tslib_1.__metadata("design:paramtypes", [project_type_1.ProjectPatch, Object]),
|
|
419
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
420
|
+
], ProjectMutation.prototype, "updateProject", null);
|
|
421
|
+
tslib_1.__decorate([
|
|
422
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
423
|
+
(0, type_graphql_1.Mutation)(returns => project_1.Project, { description: '프로젝트 도면 업데이트' }),
|
|
424
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('project')),
|
|
425
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
426
|
+
tslib_1.__metadata("design:type", Function),
|
|
427
|
+
tslib_1.__metadata("design:paramtypes", [project_type_1.ProjectPatch, Object]),
|
|
428
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
429
|
+
], ProjectMutation.prototype, "updateProjectPlan", null);
|
|
430
|
+
tslib_1.__decorate([
|
|
431
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
432
|
+
(0, type_graphql_1.Mutation)(returns => Boolean, { description: '프로젝트 공정표 업로드' }),
|
|
433
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('param')),
|
|
434
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
435
|
+
tslib_1.__metadata("design:type", Function),
|
|
436
|
+
tslib_1.__metadata("design:paramtypes", [project_type_1.UploadProjectScheduleTable, Object]),
|
|
437
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
438
|
+
], ProjectMutation.prototype, "uploadProjectScheduleTable", null);
|
|
439
|
+
tslib_1.__decorate([
|
|
440
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
441
|
+
(0, type_graphql_1.Mutation)(returns => Boolean, { description: 'To delete Project' }),
|
|
442
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
443
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
444
|
+
tslib_1.__metadata("design:type", Function),
|
|
445
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
446
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
447
|
+
], ProjectMutation.prototype, "deleteProject", null);
|
|
448
|
+
tslib_1.__decorate([
|
|
449
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
450
|
+
(0, type_graphql_1.Mutation)(returns => String, { description: '선택된 도면 파일들을 ZIP으로 압축하여 다운로드 URL 반환' }),
|
|
451
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('fileIds', type => [String])),
|
|
452
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
453
|
+
tslib_1.__metadata("design:type", Function),
|
|
454
|
+
tslib_1.__metadata("design:paramtypes", [Array, Object]),
|
|
455
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
456
|
+
], ProjectMutation.prototype, "downloadPlanFiles", null);
|
|
457
|
+
tslib_1.__decorate([
|
|
458
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
459
|
+
(0, type_graphql_1.Mutation)(returns => project_1.Project, { description: '프로젝트를 테넌트로 승격 (관리번호 발번 + project 카테고리 Domain 생성)' }),
|
|
460
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('projectId')),
|
|
461
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
462
|
+
tslib_1.__metadata("design:type", Function),
|
|
463
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
464
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
465
|
+
], ProjectMutation.prototype, "promoteProjectToTenant", null);
|
|
466
|
+
tslib_1.__decorate([
|
|
467
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
468
|
+
(0, type_graphql_1.Mutation)(returns => Boolean, { description: '프로젝트 테넌트 강등 (Domain soft-delete, Project.code 보존)' }),
|
|
469
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('projectId')),
|
|
470
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
471
|
+
tslib_1.__metadata("design:type", Function),
|
|
472
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
473
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
474
|
+
], ProjectMutation.prototype, "demoteProjectTenant", null);
|
|
475
|
+
tslib_1.__decorate([
|
|
476
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
477
|
+
(0, type_graphql_1.Directive)('@privilege(superUserGranted: true)'),
|
|
478
|
+
(0, type_graphql_1.Mutation)(returns => Boolean, { description: '비밀번호 확인 후 프로젝트 삭제 (수퍼유저 전용, 테넌트 강등 상태에서만)' }),
|
|
479
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('projectId')),
|
|
480
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('password')),
|
|
481
|
+
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
482
|
+
tslib_1.__metadata("design:type", Function),
|
|
483
|
+
tslib_1.__metadata("design:paramtypes", [String, String, Object]),
|
|
484
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
485
|
+
], ProjectMutation.prototype, "deleteProjectWithPassword", null);
|
|
486
|
+
exports.ProjectMutation = ProjectMutation = tslib_1.__decorate([
|
|
487
|
+
(0, type_graphql_1.Resolver)(project_1.Project)
|
|
488
|
+
], ProjectMutation);
|
|
489
|
+
async function createAttachmentAfterDelete(context, file, refBy, refType) {
|
|
490
|
+
if (file === undefined) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
const { tx } = context.state;
|
|
494
|
+
// 기존 첨부 파일이 있으면 삭제
|
|
495
|
+
await (0, attachment_base_1.deleteAttachmentsByRef)(null, { refBys: [refBy], refType }, context);
|
|
496
|
+
let result = await (0, attachment_base_1.createAttachment)(null, { attachment: { file, refType, refBy } }, context);
|
|
497
|
+
return await (0, shell_1.getRepository)(attachment_base_1.Attachment, tx).findOne({ where: { id: result.id } });
|
|
498
|
+
}
|
|
499
|
+
//# sourceMappingURL=project-mutation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-mutation.js","sourceRoot":"","sources":["../../../server/service/project/project-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'\nimport { In } from 'typeorm'\nimport { Domain, getRepository } from '@things-factory/shell'\nimport {\n Attachment,\n createAttachment,\n deleteAttachmentsByRef,\n ATTACHMENT_PATH,\n deleteAttachment\n} from '@things-factory/attachment-base'\nimport { Project, ProjectType } from './project'\nimport { NewProject, ProjectPatch, UploadProjectScheduleTable } from './project-type'\nimport { issueProjectCode } from './issue-project-code'\nimport { BuildingComplex, Building, BuildingLevel } from '@dssp/building-complex'\nimport { pdfToImage } from '@things-factory/board-service/dist-server/controllers/headless-pdf-to-image'\nimport { parseExcelAndImportTasks } from '../../controllers/parse-excel'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport archiver from 'archiver'\nimport { config } from '@things-factory/env'\nimport { User } from '@things-factory/auth-base'\n\nconst PROJECT_DOMAIN_EXT_TYPE = 'project'\nconst PROJECT_TEMPLATE_SUBDOMAIN = 'project-template'\n\nconst projectType = config.get('projectType', ProjectType.DSSP)\n\n@Resolver(Project)\nexport class ProjectMutation {\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 생성' })\n async createProject(@Arg('project') project: NewProject, @Ctx() context: ResolverContext): Promise<Project> {\n const { domain, user, tx } = context.state\n const projectRepo = getRepository(Project, tx)\n const buildingComplexRepo = getRepository(BuildingComplex, tx)\n\n const newBuildingComplex = await buildingComplexRepo.save({\n domain,\n creator: user,\n updater: user\n })\n\n const result = await projectRepo.save({\n name: project.name,\n documentNaming: project.name,\n buildingComplex: newBuildingComplex,\n projectType,\n domain,\n creator: user,\n updater: user\n })\n\n return result\n }\n\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 업데이트 (중복 사용 금지)' })\n async updateProject(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {\n const { user, tx } = context.state\n const projectRepo = getRepository(Project, tx)\n const buildingComplexRepo = getRepository(BuildingComplex, tx)\n const buildingRepo = getRepository(Building, tx)\n const buildingLevelRepo = getRepository(BuildingLevel, tx)\n\n const buildingComplex = project.buildingComplex\n const buildings = project.buildingComplex?.buildings || []\n\n // 1. 프로젝트 수정\n const projectResult = await projectRepo.save({ ...project, updater: user })\n\n // 1-1. 프로젝트명이 변경되면 연동된 테넌트 Domain 이름도 함께 동기화.\n // 전용 Domain 은 테넌트 승격(promoteProjectToTenant) 시에만 생성되므로\n // code 를 보유한 프로젝트만 대상. Domain 은 subdomain=code, extType='project' 로 식별.\n if (project.name !== undefined) {\n const domainRepo = getRepository(Domain, tx)\n const { code } = (await projectRepo.findOne({ where: { id: project.id }, select: ['code'] })) || {}\n if (code) {\n const linkedDomain = await domainRepo.findOne({\n where: { subdomain: code, extType: PROJECT_DOMAIN_EXT_TYPE }\n })\n if (linkedDomain && linkedDomain.name !== project.name) {\n await domainRepo.save({ ...linkedDomain, name: project.name })\n }\n }\n }\n\n // buildingComplex 가 patch 에 없는 경우 (예: state 만 변경하는 부분 patch) 는 단지 정보·\n // 동/층·첨부파일 관련 후속 단계 모두 skip.\n if (!buildingComplex) {\n return projectResult\n }\n\n // 2. 단지 정보 수정\n await buildingComplexRepo.save({ ...buildingComplex, updater: user })\n\n // 2-1. 프로젝트 메인 이미지 첨부파일 나머지 삭제 후 저장\n await createAttachmentAfterDelete(context, project?.mainPhotoUpload, project.id, Project.name)\n\n // 2-2. 단지 BIM 이미지 첨부파일 나머지 삭제 후 저장\n if (buildingComplex.id) {\n await createAttachmentAfterDelete(\n context,\n buildingComplex?.drawingUpload,\n buildingComplex.id,\n BuildingComplex.name + '_bim'\n )\n }\n\n // 3. 동의 층 정보가 바뀌었으면 층 초기화 후 다시 생성\n const originBuilding = buildingComplex.id\n ? await buildingRepo.findBy({ buildingComplex: { id: buildingComplex.id } })\n : []\n const afterBuilding = buildings.reduce((acc, building) => ({ ...acc, [building.name]: building.floorCount }), {}) // 비교용으로 수정된 동 정보 데이터 파싱\n const afterBasement = buildings.reduce((acc, building) => ({ ...acc, [building.name]: building.basementFloorCount || 0 }), {}) // 비교용으로 수정된 지하 정보 데이터 파싱\n const isBuidlingChanged = originBuilding.some(\n building =>\n afterBuilding[building.name] !== building.floorCount || afterBasement[building.name] !== building.basementFloorCount\n ) // 층 개수가 달라진 동이 있는지 확인\n\n // 동의 층 개수가 달라지면 모든 층의 데이터 제거 후 생성\n if (isBuidlingChanged || originBuilding.length !== buildings.length) {\n // 3-1. 기존 동/층 첨부파일 및 데이터 제거\n const buildingIds = originBuilding.map((building: Building) => building.id)\n const buildingLevels = await buildingLevelRepo.findBy({ building: { id: In(buildingIds) } })\n const buildingLevelIds = buildingLevels.map((buildingLevel: BuildingLevel) => buildingLevel.id)\n\n await buildingLevelRepo.softDelete({ building: { id: In(buildingIds) } })\n await buildingRepo.softDelete({ id: In(buildingIds) })\n await deleteAttachmentsByRef(null, { refBys: [...buildingIds, ...buildingLevelIds] }, context)\n\n // 3-2. 단지 내 동 정보들 생성\n for (let buildingKey in buildings) {\n const building = buildings[buildingKey]\n const newBuilding = await buildingRepo.save({\n buildingComplex: buildingComplex,\n name: building.name,\n floorCount: building.floorCount,\n hasBasement: building.hasBasement,\n basementFloorCount: building.hasBasement ? building.basementFloorCount : 0,\n updater: user,\n creator: user\n })\n\n // 3-3. 동별로 for문 돌면서 층 데이터 개수대로 생성\n for (let i = 1; i <= building.floorCount; i++) {\n await buildingLevelRepo.save({ building: newBuilding, floor: i, creator: user })\n }\n\n // 3-4. 지하층 층 데이터 생성\n if (building.hasBasement) {\n for (let i = 1; i <= building.basementFloorCount; i++) {\n await buildingLevelRepo.save({ building: newBuilding, floor: -i, creator: user })\n }\n }\n }\n }\n\n return projectResult\n }\n\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 도면 업데이트' })\n async updateProjectPlan(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {\n const { user, tx, domain } = context.state\n const projectRepo = getRepository(Project, tx)\n const buildingComplexRepo = getRepository(BuildingComplex, tx)\n const buildingRepo = getRepository(Building, tx)\n const buildingLevelRepo = getRepository(BuildingLevel, tx)\n const attachmentRepo = getRepository(Attachment, tx)\n const buildingComplex = project.buildingComplex\n const buildings = project.buildingComplex?.buildings || []\n\n // 1. 프로젝트 수정 시간 업데이트\n const projectResult = await projectRepo.save({ ...project, updater: user })\n\n // 2. 단지 축척 정보 수정\n await buildingComplexRepo.save({ ...buildingComplex, updater: user })\n\n for (let buildingKey in buildings) {\n const building = buildings[buildingKey]\n\n for (let buildingLevelKey in building.buildingLevels) {\n const buildingLevel = building.buildingLevels[buildingLevelKey]\n\n // 3. 층별 도면 이미지 저장\n const mainDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.mainDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_mainDrawing'\n )\n // 첨부된 PDF가 있으면 PDF 파일대로 썸네일 생성\n if (mainDrawingAttatchment) {\n const mainDrawingUpload = await buildingLevel.mainDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${mainDrawingAttatchment.path}` // TODO ATTACHMENT_PATH 제거, mainDrawingAttachment.fullpath 로 해도 될 것 같은데...\n const fileName = mainDrawingUpload.filename.replace('.pdf', '')\n const pngFile = await pdfToImage({ pdfPath, fileName })\n await createAttachmentAfterDelete(context, pngFile, buildingLevel.id, BuildingLevel.name + '_mainDrawing_image')\n\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_mainDrawing_thumbnail'\n )\n }\n\n // 3-1. 입면도 파일 저장\n const elevationDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.elevationDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_elevationDrawing'\n )\n if (elevationDrawingAttatchment) {\n const elevationDrawingUpload = await buildingLevel.elevationDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${elevationDrawingAttatchment.path}`\n const fileName = elevationDrawingUpload.filename.replace('.pdf', '')\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_elevationDrawing_thumbnail'\n )\n }\n\n // 3-2. 철근배분도 파일 저장\n const rebarDistributionDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.rebarDistributionDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_rebarDistributionDrawing'\n )\n if (rebarDistributionDrawingAttatchment) {\n const rebarDistributionDrawingUpload = await buildingLevel.rebarDistributionDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${rebarDistributionDrawingAttatchment.path}`\n const fileName = rebarDistributionDrawingUpload.filename.replace('.pdf', '')\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_rebarDistributionDrawing_thumbnail'\n )\n }\n\n // 3-3. 기타 도면 파일 저장\n const etcDrawingRefType = BuildingLevel.name + '_etcDrawings'\n\n // 기존 첨부 파일 중 제거된 것들 삭제\n const etcDrawings = await attachmentRepo.find({ where: { refType: etcDrawingRefType, refBy: buildingLevel.id } })\n for (let etcDrawing of etcDrawings) {\n if (!buildingLevel.etcDrawingIds.includes(etcDrawing.id)) {\n await deleteAttachment(null, { id: etcDrawing.id }, context)\n }\n }\n\n // 업로드\n if (buildingLevel.etcDrawingsUpload?.length > 0) {\n for (let etcDrawing of buildingLevel.etcDrawingsUpload) {\n await createAttachment(\n null,\n { attachment: { file: etcDrawing, refType: etcDrawingRefType, refBy: buildingLevel.id } },\n context\n )\n }\n }\n\n // 3-4. 층 업데이트 시간 갱신\n await buildingLevelRepo.save({ ...buildingLevel, updater: user })\n }\n\n // 4. 동별 도면 이미지 저장\n await createAttachmentAfterDelete(context, building?.drawingUpload, building.id, Building.name)\n\n // 4-1. 동 업데이트 시간 갱신\n await buildingRepo.save({ ...building, updater: user })\n }\n\n return projectResult\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: '프로젝트 공정표 업로드' })\n async uploadProjectScheduleTable(\n @Arg('param') param: UploadProjectScheduleTable,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { user, tx } = context.state\n const { projectId, scheduleTable } = param\n\n const projectRepo = getRepository(Project, tx)\n const project = await projectRepo.findOne({\n where: { id: projectId }\n })\n\n const { createReadStream, filename, mimetype } = await scheduleTable\n\n const stream = createReadStream()\n\n const chunks = []\n for await (const chunk of stream) {\n chunks.push(chunk)\n }\n\n const buffer = Buffer.concat(chunks)\n\n await parseExcelAndImportTasks(buffer, project, context)\n // await parseExcelAndImportTasks(attachment.fullpath, project, context)\n\n return true\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete Project' })\n async deleteProject(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { tx } = context.state\n\n await getRepository(Project, tx).delete({ id })\n await deleteAttachmentsByRef(null, { refBys: [id] }, context)\n\n return true\n }\n\n @Directive('@transaction')\n @Mutation(returns => String, { description: '선택된 도면 파일들을 ZIP으로 압축하여 다운로드 URL 반환' })\n async downloadPlanFiles(@Arg('fileIds', type => [String]) fileIds: string[], @Ctx() context: ResolverContext): Promise<string> {\n const { tx } = context.state\n\n if (!fileIds || fileIds.length === 0) {\n throw new Error('다운로드할 파일을 선택해주세요.')\n }\n\n // 첨부파일 정보 조회\n const attachments = await tx.getRepository(Attachment).find({\n where: { id: In(fileIds) }\n })\n\n if (attachments.length === 0) {\n throw new Error('선택된 파일을 찾을 수 없습니다.')\n }\n\n // ZIP 파일 생성 - 동과 층 정보를 포함한 파일명\n const zipFileName = `download.zip`\n const tempDir = path.join(process.cwd(), 'temp')\n const zipFilePath = path.join(tempDir, zipFileName)\n\n // temp 디렉토리 생성\n if (!fs.existsSync(tempDir)) {\n fs.mkdirSync(tempDir, { recursive: true })\n }\n\n const output = fs.createWriteStream(zipFilePath)\n const archive = archiver('zip', {\n zlib: { level: 9 } // 최대 압축\n })\n\n return new Promise(async (resolve, reject) => {\n output.on('close', () => {\n // ZIP 파일을 Base64로 인코딩하여 반환\n const zipBuffer = fs.readFileSync(zipFilePath)\n const base64Data = zipBuffer.toString('base64')\n const dataUrl = `data:application/zip;base64,${base64Data}`\n\n // 임시 파일 삭제\n fs.unlinkSync(zipFilePath)\n resolve(dataUrl)\n })\n\n archive.on('error', err => {\n reject(err)\n })\n archive.pipe(output)\n\n try {\n // 각 첨부파일을 ZIP에 추가\n for (const attachment of attachments) {\n try {\n const fileUrl = `http://localhost:3000${attachment.fullpath}`\n const response = await fetch(fileUrl)\n if (response.ok) {\n const buffer = await response.arrayBuffer()\n const fileName = attachment.name || path.basename(attachment.fullpath)\n archive.append(Buffer.from(buffer), { name: fileName })\n }\n } catch (error) {\n console.error('Error processing attachment:', attachment.fullpath, error)\n }\n }\n\n archive.finalize()\n } catch (error) {\n reject(error)\n }\n })\n }\n\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트를 테넌트로 승격 (관리번호 발번 + project 카테고리 Domain 생성)' })\n async promoteProjectToTenant(@Arg('projectId') projectId: string, @Ctx() context: ResolverContext): Promise<Project> {\n const { user, tx } = context.state\n\n const projectRepo = tx.getRepository(Project)\n const domainRepo = tx.getRepository(Domain)\n\n const project = await projectRepo.findOne({\n where: { id: projectId }\n })\n\n if (!project) {\n throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`)\n }\n\n // 이미 활성 테넌트인 경우 차단 (강등 상태는 재승격 허용)\n if (project.code) {\n const existing = await domainRepo.findOne({\n where: { subdomain: project.code, extType: PROJECT_DOMAIN_EXT_TYPE }\n })\n if (existing) {\n throw new Error(`이미 테넌트로 승격된 프로젝트입니다: ${project.code}`)\n }\n }\n\n // 템플릿 Domain 조회 — 모든 프로젝트 도메인의 parent. Role 상속의 단일 진실.\n // subdomain 만으로 식별 — 어드민에서 수동 생성된 도메인의 extType 이\n // NULL/대소문자 차이로 누락되어 false-negative 가 자주 발생하므로.\n const templateDomain = await domainRepo.findOne({\n where: { subdomain: PROJECT_TEMPLATE_SUBDOMAIN }\n })\n if (!templateDomain) {\n throw new Error(\n `프로젝트 템플릿 Domain (subdomain='${PROJECT_TEMPLATE_SUBDOMAIN}') 이 존재하지 않습니다. 어드민에서 먼저 생성하세요.`\n )\n }\n\n // 코드가 없으면 발번 (재승격 시에는 기존 코드 재사용)\n const code = project.code ?? (await issueProjectCode(tx))\n\n await domainRepo.save({\n name: project.name,\n subdomain: code,\n extType: PROJECT_DOMAIN_EXT_TYPE,\n parent: templateDomain,\n owner: user.id\n })\n\n if (!project.code) {\n await projectRepo.update({ id: projectId }, { code })\n project.code = code\n }\n\n return project\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: '프로젝트 테넌트 강등 (Domain soft-delete, Project.code 보존)' })\n async demoteProjectTenant(@Arg('projectId') projectId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { tx } = context.state\n\n const project = await tx.getRepository(Project).findOne({\n where: { id: projectId }\n })\n\n if (!project) {\n throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`)\n }\n\n if (!project.code) {\n throw new Error('승격된 적이 없는 프로젝트입니다.')\n }\n\n const domainRepo = tx.getRepository(Domain)\n const tenantDomain = await domainRepo.findOne({\n where: { subdomain: project.code, extType: PROJECT_DOMAIN_EXT_TYPE }\n })\n\n if (!tenantDomain) {\n throw new Error('이미 강등된 프로젝트입니다.')\n }\n\n await domainRepo.softDelete({ id: tenantDomain.id })\n\n return true\n }\n\n /**\n * 비밀번호 확인 후 프로젝트 삭제 — **수퍼유저 전용**(@privilege superUserGranted).\n *\n * 안전장치 3중:\n * 1) @privilege(superUserGranted: true) — 수퍼유저만 호출 가능 (UI 노출 + API 양쪽 방어).\n * 2) 호출자 본인 비밀번호 재확인 — 실수/방치 세션 오작동 방지.\n * 3) **활성 테넌트 상태에서는 삭제 불가** — 먼저 강등해야 함. 활성 테넌트는 라이브\n * 프로젝트 워크스페이스(Domain)를 갖고 있어, 강등으로 그 도메인을 숨긴 뒤에만 삭제.\n *\n * 테넌트 Domain 자체는 지우지 않는다 — domains 는 100+ 테이블이 FK 로 참조하므로 hard-delete\n * 가 불가(프레임워크의 demote 도 softDelete 만 수행). 강등으로 soft-delete 된 상태 그대로 두면\n * 어디에도 노출되지 않고, Project.code 는 순차 발번이라 재사용 충돌도 없다.\n */\n @Directive('@transaction')\n @Directive('@privilege(superUserGranted: true)')\n @Mutation(returns => Boolean, { description: '비밀번호 확인 후 프로젝트 삭제 (수퍼유저 전용, 테넌트 강등 상태에서만)' })\n async deleteProjectWithPassword(\n @Arg('projectId') projectId: string,\n @Arg('password') password: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, user } = context.state\n\n // 1) 호출자 본인 비밀번호 재확인\n const dbUser = await getRepository(User, tx).findOne({ where: { id: user.id } })\n if (!dbUser || !User.verify(dbUser.password, password, dbUser.salt)) {\n throw new Error('비밀번호가 일치하지 않습니다.')\n }\n\n const project = await getRepository(Project, tx).findOne({ where: { id: projectId } })\n if (!project) {\n throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`)\n }\n\n // 2) 활성 테넌트 상태면 삭제 불가 (강등 상태에서만)\n if (project.code) {\n const activeTenant = await getRepository(Domain, tx).findOne({\n where: { subdomain: project.code, extType: PROJECT_DOMAIN_EXT_TYPE }\n })\n if (activeTenant) {\n throw new Error('활성 테넌트 상태에서는 삭제할 수 없습니다. 먼저 테넌트를 강등하세요.')\n }\n }\n\n // 3) 프로젝트 soft-delete (Project.deletedAt). 프레임워크의 도메인 강등과 동일하게\n // 복구 가능한 soft-delete 를 쓴다 — 첨부/KPI 데이터/테넌트 Domain 은 그대로 보존되어\n // 필요 시 복구 가능. (soft-delete 시 목록/조회에서는 deletedAt IS NULL 필터로 자동 제외)\n await getRepository(Project, tx).softDelete({ id: projectId })\n\n return true\n }\n}\n\nexport async function createAttachmentAfterDelete(context: ResolverContext, file: any, refBy: any, refType: any) {\n if (file === undefined) {\n return null\n }\n\n const { tx } = context.state\n\n // 기존 첨부 파일이 있으면 삭제\n await deleteAttachmentsByRef(null, { refBys: [refBy], refType }, context)\n\n let result = await createAttachment(null, { attachment: { file, refType, refBy } }, context)\n\n return await getRepository(Attachment, tx).findOne({ where: { id: result.id } })\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Domain, ListParam } from '@things-factory/shell';
|
|
2
|
+
import { User } from '@things-factory/auth-base';
|
|
3
|
+
import { Project } from './project';
|
|
4
|
+
import { Task } from '../task/task';
|
|
5
|
+
import { ProjectList } from './project-type';
|
|
6
|
+
import { Attachment } from '@things-factory/attachment-base';
|
|
7
|
+
import { BuildingComplex } from '@dssp/building-complex';
|
|
8
|
+
export declare class ProjectQuery {
|
|
9
|
+
project(id: string, context: ResolverContext): Promise<Project>;
|
|
10
|
+
currentProject(context: ResolverContext): Promise<Project>;
|
|
11
|
+
projects(params: ListParam): Promise<ProjectList>;
|
|
12
|
+
projectByBuildingComplexId(buildingComplexId: string): Promise<Project>;
|
|
13
|
+
projectByBuildingLevelId(buildingLevelId: string, context: ResolverContext): Promise<Project>;
|
|
14
|
+
currentProjectType(): Promise<string>;
|
|
15
|
+
rootTasks(project: Project): Promise<Task[]>;
|
|
16
|
+
mainPhoto(project: Project): Promise<string | Attachment>;
|
|
17
|
+
scheduleTable(project: Project): Promise<Attachment | undefined>;
|
|
18
|
+
completeReport(project: Project): Promise<Attachment | undefined>;
|
|
19
|
+
buildingComplex(project: Project): Promise<BuildingComplex>;
|
|
20
|
+
domain(project: Project): Promise<Domain>;
|
|
21
|
+
tenantDomain(project: Project): Promise<Domain | null>;
|
|
22
|
+
updater(project: Project): Promise<User>;
|
|
23
|
+
creator(project: Project): Promise<User>;
|
|
24
|
+
projectType(project: Project): Promise<string>;
|
|
25
|
+
kpi(project: Project): Promise<number>;
|
|
26
|
+
kpiValues(project: Project): Promise<KpiValuesObject[]>;
|
|
27
|
+
}
|
|
28
|
+
export declare class KpiValuesObject {
|
|
29
|
+
kpiName: string;
|
|
30
|
+
value: number;
|
|
31
|
+
}
|