feat: 모바일 신고 시스템 구축 + tkqc 연동 + tkuser 이슈유형 관리

- tkreport 모바일 신고 페이지 (5단계 위자드: 유형→위치→프로젝트→항목→사진)
- 프로젝트 DB 연동 (아코디언 UI: TBM등록/활성프로젝트/모름)
- 클라이언트 이미지 리사이징 (1280px, JPEG 80%)
- nginx client_max_body_size 50m, /api/projects/ 프록시 추가
- 부적합 신고 → tkqc 자동 연동 (사진 base64 전달, SSO 토큰 유지)
- work_issue_reports에 project_id 컬럼 추가
- imageUploadService 경로 수정 (public/uploads → uploads, Docker 볼륨 일치)
- tkuser 이슈유형 탭, 휴가관리, nginx 프록시 업데이트
- tkqc 대시보드/수신함/관리함/폐기함 UI 업데이트
- system1 랜딩페이지 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-02-12 15:52:45 +09:00
parent 733bb0cb35
commit 234a6252c0
18 changed files with 1308 additions and 1208 deletions

View File

@@ -208,6 +208,7 @@ exports.createReport = async (req, res) => {
issue_category_id,
issue_item_id,
custom_item_name, // 직접 입력한 항목명
project_id,
additional_description,
photos = []
} = req.body;
@@ -275,6 +276,7 @@ exports.createReport = async (req, res) => {
reporter_id,
factory_category_id: factory_category_id || null,
workplace_id: workplace_id || null,
project_id: project_id || null,
custom_location: custom_location || null,
tbm_session_id: tbm_session_id || null,
visit_request_id: visit_request_id || null,
@@ -306,23 +308,35 @@ exports.createReport = async (req, res) => {
});
if (categoryInfo && categoryInfo.category_type === 'nonconformity') {
// 사진은 System 2에만 저장, URL 참조만 전달
const baseUrl = process.env.SYSTEM2_PUBLIC_URL || 'https://tkreport.technicalkorea.net';
const photoUrls = Object.values(photoPaths).filter(Boolean)
.map(p => `${baseUrl}/api/uploads/${p}`);
const descParts = [additional_description || categoryInfo.category_name];
if (photoUrls.length > 0) {
descParts.push('', '[첨부 사진]');
photoUrls.forEach((url, i) => descParts.push(`${i + 1}. ${url}`));
// 저장된 사진 파일을 base64로 읽어서 System 3에 전달
const fs = require('fs').promises;
const path = require('path');
const photoBase64List = [];
for (const p of Object.values(photoPaths)) {
if (!p) continue;
try {
const filePath = path.join(__dirname, '..', p);
const buf = await fs.readFile(filePath);
const b64 = `data:image/jpeg;base64,${buf.toString('base64')}`;
photoBase64List.push(b64);
} catch (readErr) {
console.error('사진 파일 읽기 실패:', p, readErr.message);
}
}
const descText = additional_description || categoryInfo.category_name;
// 원래 신고자의 SSO 토큰 추출
const originalToken = (req.headers['authorization'] || '').replace('Bearer ', '');
const result = await mProjectService.sendToMProject({
category: categoryInfo.category_name,
description: descParts.join('\n'),
description: descText,
reporter_name: req.user.name || req.user.username,
tk_issue_id: reportId,
photos: [] // 사진 복사 안 함 (URL 참조만)
project_id: project_id || null,
photos: photoBase64List,
ssoToken: originalToken
});
if (result.success && result.mProjectId) {
workIssueModel.updateMProjectId(reportId, result.mProjectId, () => {});