diff --git a/backend-node/src/controllers/batchManagementController.ts b/backend-node/src/controllers/batchManagementController.ts index 3b7402ec..bdd9e869 100644 --- a/backend-node/src/controllers/batchManagementController.ts +++ b/backend-node/src/controllers/batchManagementController.ts @@ -438,12 +438,29 @@ export class BatchManagementController { // 토큰 결정: authServiceName이 있으면 DB에서 조회, 없으면 apiKey 사용 let finalApiKey = apiKey || ""; if (authServiceName) { - // DB에서 토큰 조회 + const companyCode = req.user?.companyCode; + + // DB에서 토큰 조회 (멀티테넌시: company_code 필터링) + let tokenQuery: string; + let tokenParams: any[]; + + if (companyCode === "*") { + // 최고 관리자: 모든 회사 토큰 조회 가능 + tokenQuery = `SELECT access_token FROM auth_tokens + WHERE service_name = $1 + ORDER BY created_date DESC LIMIT 1`; + tokenParams = [authServiceName]; + } else { + // 일반 회사: 자신의 회사 토큰만 조회 + tokenQuery = `SELECT access_token FROM auth_tokens + WHERE service_name = $1 AND company_code = $2 + ORDER BY created_date DESC LIMIT 1`; + tokenParams = [authServiceName, companyCode]; + } + const tokenResult = await query<{ access_token: string }>( - `SELECT access_token FROM auth_tokens - WHERE service_name = $1 - ORDER BY created_date DESC LIMIT 1`, - [authServiceName] + tokenQuery, + tokenParams ); if (tokenResult.length > 0 && tokenResult[0].access_token) { finalApiKey = tokenResult[0].access_token; @@ -708,13 +725,33 @@ export class BatchManagementController { /** * 인증 토큰 서비스명 목록 조회 */ - static async getAuthServiceNames(req: Request, res: Response) { + static async getAuthServiceNames(req: AuthenticatedRequest, res: Response) { try { + const companyCode = req.user?.companyCode; + + // 멀티테넌시: company_code 필터링 + let queryText: string; + let queryParams: any[] = []; + + if (companyCode === "*") { + // 최고 관리자: 모든 서비스 조회 + queryText = `SELECT DISTINCT service_name + FROM auth_tokens + WHERE service_name IS NOT NULL + ORDER BY service_name`; + } else { + // 일반 회사: 자신의 회사 서비스만 조회 + queryText = `SELECT DISTINCT service_name + FROM auth_tokens + WHERE service_name IS NOT NULL + AND company_code = $1 + ORDER BY service_name`; + queryParams = [companyCode]; + } + const result = await query<{ service_name: string }>( - `SELECT DISTINCT service_name - FROM auth_tokens - WHERE service_name IS NOT NULL - ORDER BY service_name` + queryText, + queryParams ); const serviceNames = result.map((row) => row.service_name); diff --git a/backend-node/src/services/batchSchedulerService.ts b/backend-node/src/services/batchSchedulerService.ts index c425703b..743c0386 100644 --- a/backend-node/src/services/batchSchedulerService.ts +++ b/backend-node/src/services/batchSchedulerService.ts @@ -260,14 +260,29 @@ export class BatchSchedulerService { "./batchExternalDbService" ); - // auth_service_name이 설정된 경우 auth_tokens에서 토큰 조회 + // auth_service_name이 설정된 경우 auth_tokens에서 토큰 조회 (멀티테넌시 적용) let apiKey = firstMapping.from_api_key || ""; if (config.auth_service_name) { + let tokenQuery: string; + let tokenParams: any[]; + + if (config.company_code === "*") { + // 최고 관리자 배치: 모든 회사 토큰 조회 가능 + tokenQuery = `SELECT access_token FROM auth_tokens + WHERE service_name = $1 + ORDER BY created_date DESC LIMIT 1`; + tokenParams = [config.auth_service_name]; + } else { + // 일반 회사 배치: 자신의 회사 토큰만 조회 + tokenQuery = `SELECT access_token FROM auth_tokens + WHERE service_name = $1 AND company_code = $2 + ORDER BY created_date DESC LIMIT 1`; + tokenParams = [config.auth_service_name, config.company_code]; + } + const tokenResult = await query<{ access_token: string }>( - `SELECT access_token FROM auth_tokens - WHERE service_name = $1 - ORDER BY created_date DESC LIMIT 1`, - [config.auth_service_name] + tokenQuery, + tokenParams ); if (tokenResult.length > 0 && tokenResult[0].access_token) { apiKey = tokenResult[0].access_token; diff --git a/backend-node/src/services/batchService.ts b/backend-node/src/services/batchService.ts index 77e7ac5c..31ee2001 100644 --- a/backend-node/src/services/batchService.ts +++ b/backend-node/src/services/batchService.ts @@ -796,20 +796,29 @@ export class BatchService { const updateColumns = columns.filter( (col) => col !== conflictKey ); - const updateSet = updateColumns - .map((col) => `${col} = EXCLUDED.${col}`) - .join(", "); - // updated_date 컬럼이 있으면 현재 시간으로 업데이트 - const hasUpdatedDate = columns.includes("updated_date"); - const finalUpdateSet = hasUpdatedDate - ? `${updateSet}, updated_date = NOW()` - : updateSet; + // 업데이트할 컬럼이 없으면 DO NOTHING 사용 + if (updateColumns.length === 0) { + queryStr = `INSERT INTO ${tableName} (${columns.join(", ")}) + VALUES (${placeholders}) + ON CONFLICT (${conflictKey}) + DO NOTHING`; + } else { + const updateSet = updateColumns + .map((col) => `${col} = EXCLUDED.${col}`) + .join(", "); - queryStr = `INSERT INTO ${tableName} (${columns.join(", ")}) - VALUES (${placeholders}) - ON CONFLICT (${conflictKey}) - DO UPDATE SET ${finalUpdateSet}`; + // updated_date 컬럼이 있으면 현재 시간으로 업데이트 + const hasUpdatedDate = columns.includes("updated_date"); + const finalUpdateSet = hasUpdatedDate + ? `${updateSet}, updated_date = NOW()` + : updateSet; + + queryStr = `INSERT INTO ${tableName} (${columns.join(", ")}) + VALUES (${placeholders}) + ON CONFLICT (${conflictKey}) + DO UPDATE SET ${finalUpdateSet}`; + } } else { // INSERT 모드: 기존 방식 queryStr = `INSERT INTO ${tableName} (${columns.join(", ")}) VALUES (${placeholders})`;