파일 업로드 회사별로 보이도록 수정
This commit is contained in:
parent
958aeb2d53
commit
acaa3414d2
|
|
@ -232,13 +232,20 @@ export const uploadFiles = async (
|
||||||
|
|
||||||
// 자동 연결 로직 - target_objid 자동 생성
|
// 자동 연결 로직 - target_objid 자동 생성
|
||||||
let finalTargetObjid = targetObjid;
|
let finalTargetObjid = targetObjid;
|
||||||
if (autoLink === "true" && linkedTable && recordId) {
|
|
||||||
|
// 🔑 템플릿 파일(screen_files:)이나 temp_ 파일은 autoLink 무시
|
||||||
|
const isTemplateFile = targetObjid && (targetObjid.startsWith('screen_files:') || targetObjid.startsWith('temp_'));
|
||||||
|
|
||||||
|
if (!isTemplateFile && autoLink === "true" && linkedTable && recordId) {
|
||||||
// 가상 파일 컬럼의 경우 컬럼명도 포함한 target_objid 생성
|
// 가상 파일 컬럼의 경우 컬럼명도 포함한 target_objid 생성
|
||||||
if (isVirtualFileColumn === "true" && columnName) {
|
if (isVirtualFileColumn === "true" && columnName) {
|
||||||
finalTargetObjid = `${linkedTable}:${recordId}:${columnName}`;
|
finalTargetObjid = `${linkedTable}:${recordId}:${columnName}`;
|
||||||
} else {
|
} else {
|
||||||
finalTargetObjid = `${linkedTable}:${recordId}`;
|
finalTargetObjid = `${linkedTable}:${recordId}`;
|
||||||
}
|
}
|
||||||
|
console.log("📎 autoLink 적용:", { original: targetObjid, final: finalTargetObjid });
|
||||||
|
} else if (isTemplateFile) {
|
||||||
|
console.log("🎨 템플릿 파일이므로 targetObjid 유지:", targetObjid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedFiles = [];
|
const savedFiles = [];
|
||||||
|
|
@ -363,6 +370,38 @@ export const deleteFile = async (
|
||||||
const { objid } = req.params;
|
const { objid } = req.params;
|
||||||
const { writer = "system" } = req.body;
|
const { writer = "system" } = req.body;
|
||||||
|
|
||||||
|
// 🔒 멀티테넌시: 현재 사용자의 회사 코드
|
||||||
|
const companyCode = req.user?.companyCode;
|
||||||
|
|
||||||
|
// 파일 정보 조회
|
||||||
|
const fileRecord = await queryOne<any>(
|
||||||
|
`SELECT * FROM attach_file_info WHERE objid = $1`,
|
||||||
|
[parseInt(objid)]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fileRecord) {
|
||||||
|
res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: "파일을 찾을 수 없습니다.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔒 멀티테넌시: 회사 코드 일치 여부 확인 (최고 관리자 제외)
|
||||||
|
if (companyCode !== "*" && fileRecord.company_code !== companyCode) {
|
||||||
|
console.warn("⚠️ 다른 회사 파일 삭제 시도:", {
|
||||||
|
userId: req.user?.userId,
|
||||||
|
userCompanyCode: companyCode,
|
||||||
|
fileCompanyCode: fileRecord.company_code,
|
||||||
|
objid,
|
||||||
|
});
|
||||||
|
res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: "접근 권한이 없습니다.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 파일 상태를 DELETED로 변경 (논리적 삭제)
|
// 파일 상태를 DELETED로 변경 (논리적 삭제)
|
||||||
await query<any>(
|
await query<any>(
|
||||||
"UPDATE attach_file_info SET status = $1 WHERE objid = $2",
|
"UPDATE attach_file_info SET status = $1 WHERE objid = $2",
|
||||||
|
|
@ -510,6 +549,9 @@ export const getComponentFiles = async (
|
||||||
const { screenId, componentId, tableName, recordId, columnName } =
|
const { screenId, componentId, tableName, recordId, columnName } =
|
||||||
req.query;
|
req.query;
|
||||||
|
|
||||||
|
// 🔒 멀티테넌시: 현재 사용자의 회사 코드 가져오기
|
||||||
|
const companyCode = req.user?.companyCode;
|
||||||
|
|
||||||
console.log("📂 [getComponentFiles] API 호출:", {
|
console.log("📂 [getComponentFiles] API 호출:", {
|
||||||
screenId,
|
screenId,
|
||||||
componentId,
|
componentId,
|
||||||
|
|
@ -517,6 +559,7 @@ export const getComponentFiles = async (
|
||||||
recordId,
|
recordId,
|
||||||
columnName,
|
columnName,
|
||||||
user: req.user?.userId,
|
user: req.user?.userId,
|
||||||
|
companyCode, // 🔒 멀티테넌시 로그
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!screenId || !componentId) {
|
if (!screenId || !componentId) {
|
||||||
|
|
@ -534,32 +577,16 @@ export const getComponentFiles = async (
|
||||||
templateTargetObjid,
|
templateTargetObjid,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 모든 파일 조회해서 실제 저장된 target_objid 패턴 확인
|
// 🔒 멀티테넌시: 회사별 필터링 추가
|
||||||
const allFiles = await query<any>(
|
|
||||||
`SELECT target_objid, real_file_name, regdate
|
|
||||||
FROM attach_file_info
|
|
||||||
WHERE status = $1
|
|
||||||
ORDER BY regdate DESC
|
|
||||||
LIMIT 10`,
|
|
||||||
["ACTIVE"]
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
"🗂️ [getComponentFiles] 최근 저장된 파일들의 target_objid:",
|
|
||||||
allFiles.map((f) => ({
|
|
||||||
target_objid: f.target_objid,
|
|
||||||
name: f.real_file_name,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
const templateFiles = await query<any>(
|
const templateFiles = await query<any>(
|
||||||
`SELECT * FROM attach_file_info
|
`SELECT * FROM attach_file_info
|
||||||
WHERE target_objid = $1 AND status = $2
|
WHERE target_objid = $1 AND status = $2 AND company_code = $3
|
||||||
ORDER BY regdate DESC`,
|
ORDER BY regdate DESC`,
|
||||||
[templateTargetObjid, "ACTIVE"]
|
[templateTargetObjid, "ACTIVE", companyCode]
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"📁 [getComponentFiles] 템플릿 파일 결과:",
|
"📁 [getComponentFiles] 템플릿 파일 결과 (회사별 필터링):",
|
||||||
templateFiles.length
|
templateFiles.length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -567,11 +594,12 @@ export const getComponentFiles = async (
|
||||||
let dataFiles: any[] = [];
|
let dataFiles: any[] = [];
|
||||||
if (tableName && recordId && columnName) {
|
if (tableName && recordId && columnName) {
|
||||||
const dataTargetObjid = `${tableName}:${recordId}:${columnName}`;
|
const dataTargetObjid = `${tableName}:${recordId}:${columnName}`;
|
||||||
|
// 🔒 멀티테넌시: 회사별 필터링 추가
|
||||||
dataFiles = await query<any>(
|
dataFiles = await query<any>(
|
||||||
`SELECT * FROM attach_file_info
|
`SELECT * FROM attach_file_info
|
||||||
WHERE target_objid = $1 AND status = $2
|
WHERE target_objid = $1 AND status = $2 AND company_code = $3
|
||||||
ORDER BY regdate DESC`,
|
ORDER BY regdate DESC`,
|
||||||
[dataTargetObjid, "ACTIVE"]
|
[dataTargetObjid, "ACTIVE", companyCode]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -643,6 +671,9 @@ export const previewFile = async (
|
||||||
const { objid } = req.params;
|
const { objid } = req.params;
|
||||||
const { serverFilename } = req.query;
|
const { serverFilename } = req.query;
|
||||||
|
|
||||||
|
// 🔒 멀티테넌시: 현재 사용자의 회사 코드
|
||||||
|
const companyCode = req.user?.companyCode;
|
||||||
|
|
||||||
const fileRecord = await queryOne<any>(
|
const fileRecord = await queryOne<any>(
|
||||||
"SELECT * FROM attach_file_info WHERE objid = $1 LIMIT 1",
|
"SELECT * FROM attach_file_info WHERE objid = $1 LIMIT 1",
|
||||||
[parseInt(objid)]
|
[parseInt(objid)]
|
||||||
|
|
@ -656,13 +687,28 @@ export const previewFile = async (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔒 멀티테넌시: 회사 코드 일치 여부 확인 (최고 관리자 제외)
|
||||||
|
if (companyCode !== "*" && fileRecord.company_code !== companyCode) {
|
||||||
|
console.warn("⚠️ 다른 회사 파일 접근 시도:", {
|
||||||
|
userId: req.user?.userId,
|
||||||
|
userCompanyCode: companyCode,
|
||||||
|
fileCompanyCode: fileRecord.company_code,
|
||||||
|
objid,
|
||||||
|
});
|
||||||
|
res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: "접근 권한이 없습니다.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 파일 경로에서 회사코드와 날짜 폴더 추출
|
// 파일 경로에서 회사코드와 날짜 폴더 추출
|
||||||
const filePathParts = fileRecord.file_path!.split("/");
|
const filePathParts = fileRecord.file_path!.split("/");
|
||||||
let companyCode = filePathParts[2] || "DEFAULT";
|
let fileCompanyCode = filePathParts[2] || "DEFAULT";
|
||||||
|
|
||||||
// company_* 처리 (실제 회사 코드로 변환)
|
// company_* 처리 (실제 회사 코드로 변환)
|
||||||
if (companyCode === "company_*") {
|
if (fileCompanyCode === "company_*") {
|
||||||
companyCode = "company_*"; // 실제 디렉토리명 유지
|
fileCompanyCode = "company_*"; // 실제 디렉토리명 유지
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = fileRecord.saved_file_name!;
|
const fileName = fileRecord.saved_file_name!;
|
||||||
|
|
@ -674,7 +720,7 @@ export const previewFile = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
const companyUploadDir = getCompanyUploadDir(
|
const companyUploadDir = getCompanyUploadDir(
|
||||||
companyCode,
|
fileCompanyCode,
|
||||||
dateFolder || undefined
|
dateFolder || undefined
|
||||||
);
|
);
|
||||||
const filePath = path.join(companyUploadDir, fileName);
|
const filePath = path.join(companyUploadDir, fileName);
|
||||||
|
|
@ -762,6 +808,9 @@ export const downloadFile = async (
|
||||||
try {
|
try {
|
||||||
const { objid } = req.params;
|
const { objid } = req.params;
|
||||||
|
|
||||||
|
// 🔒 멀티테넌시: 현재 사용자의 회사 코드
|
||||||
|
const companyCode = req.user?.companyCode;
|
||||||
|
|
||||||
const fileRecord = await queryOne<any>(
|
const fileRecord = await queryOne<any>(
|
||||||
`SELECT * FROM attach_file_info WHERE objid = $1`,
|
`SELECT * FROM attach_file_info WHERE objid = $1`,
|
||||||
[parseInt(objid)]
|
[parseInt(objid)]
|
||||||
|
|
@ -775,13 +824,28 @@ export const downloadFile = async (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔒 멀티테넌시: 회사 코드 일치 여부 확인 (최고 관리자 제외)
|
||||||
|
if (companyCode !== "*" && fileRecord.company_code !== companyCode) {
|
||||||
|
console.warn("⚠️ 다른 회사 파일 다운로드 시도:", {
|
||||||
|
userId: req.user?.userId,
|
||||||
|
userCompanyCode: companyCode,
|
||||||
|
fileCompanyCode: fileRecord.company_code,
|
||||||
|
objid,
|
||||||
|
});
|
||||||
|
res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: "접근 권한이 없습니다.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 파일 경로에서 회사코드와 날짜 폴더 추출 (예: /uploads/company_*/2025/09/05/timestamp_filename.ext)
|
// 파일 경로에서 회사코드와 날짜 폴더 추출 (예: /uploads/company_*/2025/09/05/timestamp_filename.ext)
|
||||||
const filePathParts = fileRecord.file_path!.split("/");
|
const filePathParts = fileRecord.file_path!.split("/");
|
||||||
let companyCode = filePathParts[2] || "DEFAULT"; // /uploads/company_*/2025/09/05/filename.ext에서 company_* 추출
|
let fileCompanyCode = filePathParts[2] || "DEFAULT"; // /uploads/company_*/2025/09/05/filename.ext에서 company_* 추출
|
||||||
|
|
||||||
// company_* 처리 (실제 회사 코드로 변환)
|
// company_* 처리 (실제 회사 코드로 변환)
|
||||||
if (companyCode === "company_*") {
|
if (fileCompanyCode === "company_*") {
|
||||||
companyCode = "company_*"; // 실제 디렉토리명 유지
|
fileCompanyCode = "company_*"; // 실제 디렉토리명 유지
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = fileRecord.saved_file_name!;
|
const fileName = fileRecord.saved_file_name!;
|
||||||
|
|
@ -794,7 +858,7 @@ export const downloadFile = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
const companyUploadDir = getCompanyUploadDir(
|
const companyUploadDir = getCompanyUploadDir(
|
||||||
companyCode,
|
fileCompanyCode,
|
||||||
dateFolder || undefined
|
dateFolder || undefined
|
||||||
);
|
);
|
||||||
const filePath = path.join(companyUploadDir, fileName);
|
const filePath = path.join(companyUploadDir, fileName);
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ export const uploadFiles = async (params: {
|
||||||
autoLink?: boolean;
|
autoLink?: boolean;
|
||||||
columnName?: string;
|
columnName?: string;
|
||||||
isVirtualFileColumn?: boolean;
|
isVirtualFileColumn?: boolean;
|
||||||
|
companyCode?: string; // 🔒 멀티테넌시: 회사 코드
|
||||||
}): Promise<FileUploadResponse> => {
|
}): Promise<FileUploadResponse> => {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
|
|
@ -64,6 +65,7 @@ export const uploadFiles = async (params: {
|
||||||
if (params.autoLink !== undefined) formData.append("autoLink", params.autoLink.toString());
|
if (params.autoLink !== undefined) formData.append("autoLink", params.autoLink.toString());
|
||||||
if (params.columnName) formData.append("columnName", params.columnName);
|
if (params.columnName) formData.append("columnName", params.columnName);
|
||||||
if (params.isVirtualFileColumn !== undefined) formData.append("isVirtualFileColumn", params.isVirtualFileColumn.toString());
|
if (params.isVirtualFileColumn !== undefined) formData.append("isVirtualFileColumn", params.isVirtualFileColumn.toString());
|
||||||
|
if (params.companyCode) formData.append("companyCode", params.companyCode); // 🔒 멀티테넌시
|
||||||
|
|
||||||
const response = await apiClient.post("/files/upload", formData, {
|
const response = await apiClient.post("/files/upload", formData, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
|
|
@ -204,24 +204,37 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
|
|
||||||
// 템플릿 파일과 데이터 파일을 조회하는 함수
|
// 템플릿 파일과 데이터 파일을 조회하는 함수
|
||||||
const loadComponentFiles = useCallback(async () => {
|
const loadComponentFiles = useCallback(async () => {
|
||||||
if (!component?.id) return;
|
if (!component?.id) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let screenId =
|
// 1. formData에서 screenId 가져오기
|
||||||
formData?.screenId ||
|
let screenId = formData?.screenId;
|
||||||
(typeof window !== "undefined" && window.location.pathname.includes("/screens/")
|
|
||||||
? parseInt(window.location.pathname.split("/screens/")[1])
|
// 2. URL에서 screenId 추출 (/screens/:id 패턴)
|
||||||
: null);
|
if (!screenId && typeof window !== "undefined") {
|
||||||
|
const pathname = window.location.pathname;
|
||||||
// 디자인 모드인 경우 기본 화면 ID 사용
|
const screenMatch = pathname.match(/\/screens\/(\d+)/);
|
||||||
if (!screenId && isDesignMode) {
|
if (screenMatch) {
|
||||||
screenId = 40; // 기본 화면 ID
|
screenId = parseInt(screenMatch[1]);
|
||||||
console.log("📂 디자인 모드: 기본 화면 ID 사용 (40)");
|
console.log("📂 URL에서 화면 ID 추출:", screenId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. 디자인 모드인 경우 임시 화면 ID 사용
|
||||||
|
if (!screenId && isDesignMode) {
|
||||||
|
screenId = 999999; // 디자인 모드 임시 ID
|
||||||
|
console.log("📂 디자인 모드: 임시 화면 ID 사용 (999999)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 화면 ID가 없으면 컴포넌트 ID만으로 조회 시도
|
||||||
if (!screenId) {
|
if (!screenId) {
|
||||||
console.log("📂 화면 ID 없음, 기존 파일 로직 사용");
|
console.warn("⚠️ 화면 ID 없음, 컴포넌트 ID만으로 파일 조회:", {
|
||||||
return false; // 기존 로직 사용
|
componentId: component.id,
|
||||||
|
pathname: window.location.pathname,
|
||||||
|
formData: formData,
|
||||||
|
});
|
||||||
|
// screenId를 0으로 설정하여 컴포넌트 ID로만 조회
|
||||||
|
screenId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
|
|
@ -229,7 +242,7 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
componentId: component.id,
|
componentId: component.id,
|
||||||
tableName: formData?.tableName || component.tableName,
|
tableName: formData?.tableName || component.tableName,
|
||||||
recordId: formData?.id,
|
recordId: formData?.id,
|
||||||
columnName: component.columnName,
|
columnName: component.columnName || component.id, // 🔑 columnName이 없으면 component.id 사용
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("📂 컴포넌트 파일 조회:", params);
|
console.log("📂 컴포넌트 파일 조회:", params);
|
||||||
|
|
@ -319,7 +332,7 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
return false; // 기존 로직 사용
|
return false; // 기존 로직 사용
|
||||||
}, [component.id, component.tableName, component.columnName, formData?.screenId, formData?.tableName, formData?.id]);
|
}, [component.id, component.tableName, component.columnName, formData?.screenId, formData?.tableName, formData?.id]);
|
||||||
|
|
||||||
// 컴포넌트 파일 동기화
|
// 컴포넌트 파일 동기화 (DB 우선, localStorage는 보조)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const componentFiles = (component as any)?.uploadedFiles || [];
|
const componentFiles = (component as any)?.uploadedFiles || [];
|
||||||
const lastUpdate = (component as any)?.lastFileUpdate;
|
const lastUpdate = (component as any)?.lastFileUpdate;
|
||||||
|
|
@ -332,15 +345,15 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
currentUploadedFiles: uploadedFiles.length,
|
currentUploadedFiles: uploadedFiles.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 먼저 새로운 템플릿 파일 조회 시도
|
// 🔒 항상 DB에서 최신 파일 목록을 조회 (멀티테넌시 격리)
|
||||||
loadComponentFiles().then((useNewLogic) => {
|
loadComponentFiles().then((dbLoadSuccess) => {
|
||||||
if (useNewLogic) {
|
if (dbLoadSuccess) {
|
||||||
console.log("✅ 새로운 템플릿 파일 로직 사용");
|
console.log("✅ DB에서 파일 로드 성공 (멀티테넌시 적용)");
|
||||||
return; // 새로운 로직이 성공했으면 기존 로직 스킵
|
return; // DB 로드 성공 시 localStorage 무시
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 로직 사용
|
// DB 로드 실패 시에만 기존 로직 사용 (하위 호환성)
|
||||||
console.log("📂 기존 파일 로직 사용");
|
console.log("📂 DB 로드 실패, 기존 로직 사용");
|
||||||
|
|
||||||
// 전역 상태에서 최신 파일 정보 가져오기
|
// 전역 상태에서 최신 파일 정보 가져오기
|
||||||
const globalFileState = typeof window !== "undefined" ? (window as any).globalFileState || {} : {};
|
const globalFileState = typeof window !== "undefined" ? (window as any).globalFileState || {} : {};
|
||||||
|
|
@ -358,34 +371,6 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
lastUpdate: lastUpdate,
|
lastUpdate: lastUpdate,
|
||||||
});
|
});
|
||||||
|
|
||||||
// localStorage에서 백업 파일 복원 (새로고침 시 중요!)
|
|
||||||
try {
|
|
||||||
const backupKey = `fileUpload_${component.id}`;
|
|
||||||
const backupFiles = localStorage.getItem(backupKey);
|
|
||||||
if (backupFiles) {
|
|
||||||
const parsedFiles = JSON.parse(backupFiles);
|
|
||||||
if (parsedFiles.length > 0 && currentFiles.length === 0) {
|
|
||||||
console.log("🔄 localStorage에서 파일 복원:", {
|
|
||||||
componentId: component.id,
|
|
||||||
restoredFiles: parsedFiles.length,
|
|
||||||
files: parsedFiles.map((f: any) => ({ objid: f.objid, name: f.realFileName })),
|
|
||||||
});
|
|
||||||
setUploadedFiles(parsedFiles);
|
|
||||||
|
|
||||||
// 전역 상태에도 복원
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
(window as any).globalFileState = {
|
|
||||||
...(window as any).globalFileState,
|
|
||||||
[component.id]: parsedFiles,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("localStorage 백업 복원 실패:", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 최신 파일과 현재 파일 비교
|
// 최신 파일과 현재 파일 비교
|
||||||
if (JSON.stringify(currentFiles) !== JSON.stringify(uploadedFiles)) {
|
if (JSON.stringify(currentFiles) !== JSON.stringify(uploadedFiles)) {
|
||||||
console.log("🔄 useEffect에서 파일 목록 변경 감지:", {
|
console.log("🔄 useEffect에서 파일 목록 변경 감지:", {
|
||||||
|
|
@ -535,24 +520,39 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
// targetObjid 생성 - 템플릿 vs 데이터 파일 구분
|
// targetObjid 생성 - 템플릿 vs 데이터 파일 구분
|
||||||
const tableName = formData?.tableName || component.tableName || "default_table";
|
const tableName = formData?.tableName || component.tableName || "default_table";
|
||||||
const recordId = formData?.id;
|
const recordId = formData?.id;
|
||||||
const screenId = formData?.screenId;
|
|
||||||
const columnName = component.columnName || component.id;
|
const columnName = component.columnName || component.id;
|
||||||
|
|
||||||
|
// screenId 추출 (우선순위: formData > URL)
|
||||||
|
let screenId = formData?.screenId;
|
||||||
|
if (!screenId && typeof window !== "undefined") {
|
||||||
|
const pathname = window.location.pathname;
|
||||||
|
const screenMatch = pathname.match(/\/screens\/(\d+)/);
|
||||||
|
if (screenMatch) {
|
||||||
|
screenId = parseInt(screenMatch[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let targetObjid;
|
let targetObjid;
|
||||||
if (recordId && tableName) {
|
// 우선순위: 1) 실제 데이터 (recordId가 숫자/문자열이고 temp_가 아님) > 2) 템플릿 (screenId) > 3) 기본값
|
||||||
// 실제 데이터 파일
|
const isRealRecord = recordId && typeof recordId !== 'undefined' && !String(recordId).startsWith('temp_');
|
||||||
|
|
||||||
|
if (isRealRecord && tableName) {
|
||||||
|
// 실제 데이터 파일 (진짜 레코드 ID가 있을 때만)
|
||||||
targetObjid = `${tableName}:${recordId}:${columnName}`;
|
targetObjid = `${tableName}:${recordId}:${columnName}`;
|
||||||
console.log("📁 실제 데이터 파일 업로드:", targetObjid);
|
console.log("📁 실제 데이터 파일 업로드:", targetObjid);
|
||||||
} else if (screenId) {
|
} else if (screenId) {
|
||||||
// 템플릿 파일
|
// 🔑 템플릿 파일 (백엔드 조회 형식과 동일하게)
|
||||||
targetObjid = `screen_${screenId}:${component.id}`;
|
targetObjid = `screen_files:${screenId}:${component.id}:${columnName}`;
|
||||||
console.log("🎨 템플릿 파일 업로드:", targetObjid);
|
console.log("🎨 템플릿 파일 업로드:", { targetObjid, screenId, componentId: component.id, columnName });
|
||||||
} else {
|
} else {
|
||||||
// 기본값 (화면관리에서 사용)
|
// 기본값 (화면관리에서 사용)
|
||||||
targetObjid = `temp_${component.id}`;
|
targetObjid = `temp_${component.id}`;
|
||||||
console.log("📝 기본 파일 업로드:", targetObjid);
|
console.log("📝 기본 파일 업로드:", targetObjid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔒 현재 사용자의 회사 코드 가져오기 (멀티테넌시 격리)
|
||||||
|
const userCompanyCode = (window as any).__user__?.companyCode;
|
||||||
|
|
||||||
const uploadData = {
|
const uploadData = {
|
||||||
// 🎯 formData에서 백엔드 API 설정 가져오기
|
// 🎯 formData에서 백엔드 API 설정 가져오기
|
||||||
autoLink: formData?.autoLink || true,
|
autoLink: formData?.autoLink || true,
|
||||||
|
|
@ -562,6 +562,7 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
isVirtualFileColumn: formData?.isVirtualFileColumn || true,
|
isVirtualFileColumn: formData?.isVirtualFileColumn || true,
|
||||||
docType: component.fileConfig?.docType || "DOCUMENT",
|
docType: component.fileConfig?.docType || "DOCUMENT",
|
||||||
docTypeName: component.fileConfig?.docTypeName || "일반 문서",
|
docTypeName: component.fileConfig?.docTypeName || "일반 문서",
|
||||||
|
companyCode: userCompanyCode, // 🔒 멀티테넌시: 회사 코드 명시적 전달
|
||||||
// 호환성을 위한 기존 필드들
|
// 호환성을 위한 기존 필드들
|
||||||
tableName: tableName,
|
tableName: tableName,
|
||||||
fieldName: columnName,
|
fieldName: columnName,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue