[agent-pipeline] pipe-20260305174856-9ogt round-1
This commit is contained in:
parent
f147a7608d
commit
24f5c179d8
|
|
@ -3,6 +3,14 @@ import { AuthenticatedRequest } from "../types/auth";
|
|||
import { query, queryOne, transaction } from "../database/db";
|
||||
import { PoolClient } from "pg";
|
||||
|
||||
// 트랜잭션 내부에서 throw하고 외부에서 instanceof로 구분하기 위한 커스텀 에러
|
||||
class ValidationError extends Error {
|
||||
constructor(public statusCode: number, message: string) {
|
||||
super(message);
|
||||
this.name = "ValidationError";
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 결재 정의 (Approval Definitions) CRUD
|
||||
// ============================================================
|
||||
|
|
@ -18,24 +26,34 @@ export class ApprovalDefinitionController {
|
|||
|
||||
const { is_active, search } = req.query;
|
||||
|
||||
const conditions: string[] = ["company_code = $1"];
|
||||
const params: any[] = [companyCode];
|
||||
let idx = 2;
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
// SUPER_ADMIN은 전체 조회, 일반 회사는 자사 데이터만
|
||||
if (companyCode === "*") {
|
||||
// 전체 조회 (company_code 필터 없음)
|
||||
} else {
|
||||
conditions.push(`company_code = $${idx++}`);
|
||||
params.push(companyCode);
|
||||
}
|
||||
|
||||
if (is_active) {
|
||||
conditions.push(`is_active = $${idx}`);
|
||||
conditions.push(`is_active = $${idx++}`);
|
||||
params.push(is_active);
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (search) {
|
||||
// ILIKE에서 같은 파라미터를 두 조건에서 참조 (파라미터는 1개만 push)
|
||||
conditions.push(`(definition_name ILIKE $${idx} OR definition_name_eng ILIKE $${idx})`);
|
||||
params.push(`%${search}%`);
|
||||
idx++;
|
||||
}
|
||||
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
|
||||
const rows = await query<any>(
|
||||
`SELECT * FROM approval_definitions WHERE ${conditions.join(" AND ")} ORDER BY definition_id ASC`,
|
||||
`SELECT * FROM approval_definitions ${whereClause} ORDER BY company_code, definition_id ASC`,
|
||||
params
|
||||
);
|
||||
|
||||
|
|
@ -239,9 +257,15 @@ export class ApprovalTemplateController {
|
|||
|
||||
const { definition_id, is_active } = req.query;
|
||||
|
||||
const conditions: string[] = ["t.company_code = $1"];
|
||||
const params: any[] = [companyCode];
|
||||
let idx = 2;
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
// SUPER_ADMIN은 전체 조회, 일반 회사는 자사 데이터만
|
||||
if (companyCode !== "*") {
|
||||
conditions.push(`t.company_code = $${idx++}`);
|
||||
params.push(companyCode);
|
||||
}
|
||||
|
||||
if (definition_id) {
|
||||
conditions.push(`t.definition_id = $${idx++}`);
|
||||
|
|
@ -252,12 +276,14 @@ export class ApprovalTemplateController {
|
|||
params.push(is_active);
|
||||
}
|
||||
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
|
||||
const rows = await query<any>(
|
||||
`SELECT t.*, d.definition_name
|
||||
FROM approval_line_templates t
|
||||
LEFT JOIN approval_definitions d ON t.definition_id = d.definition_id AND t.company_code = d.company_code
|
||||
WHERE ${conditions.join(" AND ")}
|
||||
ORDER BY t.template_id ASC`,
|
||||
${whereClause}
|
||||
ORDER BY t.company_code, t.template_id ASC`,
|
||||
params
|
||||
);
|
||||
|
||||
|
|
@ -576,9 +602,15 @@ export class ApprovalRequestController {
|
|||
|
||||
const { status, target_table, target_record_id, requester_id, my_approvals, page = "1", limit = "20" } = req.query;
|
||||
|
||||
const conditions: string[] = ["r.company_code = $1"];
|
||||
const params: any[] = [companyCode];
|
||||
let idx = 2;
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
// SUPER_ADMIN은 전체 조회, 일반 회사는 자사 데이터만
|
||||
if (companyCode !== "*") {
|
||||
conditions.push(`r.company_code = $${idx++}`);
|
||||
params.push(companyCode);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
conditions.push(`r.status = $${idx++}`);
|
||||
|
|
@ -606,12 +638,12 @@ export class ApprovalRequestController {
|
|||
}
|
||||
|
||||
const offset = (parseInt(page as string) - 1) * parseInt(limit as string);
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
|
||||
// 카운트 쿼리는 LIMIT/OFFSET 파라미터 없이 실행
|
||||
const countParams = [...params];
|
||||
const [countRow] = await query<any>(
|
||||
`SELECT COUNT(*) as total FROM approval_requests r
|
||||
WHERE ${conditions.join(" AND ")}`,
|
||||
`SELECT COUNT(*) as total FROM approval_requests r ${whereClause}`,
|
||||
countParams
|
||||
);
|
||||
|
||||
|
|
@ -624,7 +656,7 @@ export class ApprovalRequestController {
|
|||
`SELECT r.*, d.definition_name
|
||||
FROM approval_requests r
|
||||
LEFT JOIN approval_definitions d ON r.definition_id = d.definition_id AND r.company_code = d.company_code
|
||||
WHERE ${conditions.join(" AND ")}
|
||||
${whereClause}
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT $${limitIdx} OFFSET $${offsetIdx}`,
|
||||
params
|
||||
|
|
@ -1057,14 +1089,6 @@ export class ApprovalLineController {
|
|||
return res.status(400).json({ success: false, message: "액션은 approved 또는 rejected여야 합니다." });
|
||||
}
|
||||
|
||||
// 검증 에러를 트랜잭션 바깥으로 전달하기 위한 커스텀 에러 클래스
|
||||
class ValidationError extends Error {
|
||||
constructor(public statusCode: number, message: string) {
|
||||
super(message);
|
||||
this.name = "ValidationError";
|
||||
}
|
||||
}
|
||||
|
||||
await transaction(async (client) => {
|
||||
// FOR UPDATE로 결재 라인 잠금 (동시성 방어)
|
||||
const { rows: [line] } = await client.query(
|
||||
|
|
|
|||
Loading…
Reference in New Issue