- 기록을 `common_util_audit.record_audit()` 한 곳으로 모음 — 여섯 자리에 흩어져 있던 raw INSERT 제거 - 대상 식별자 칸 신설(`017_audit_log_detail.sql`, 적용 완료) — 기존 `resource_id` 는 INT 라 프로젝트 UUID 를 못 담아 늘 NULL 이었음. 프로젝트 생성·수정·삭제·회사 생성이 대상을 남김 - 접속 주소·브라우저 기록 — 라우터가 요청을 넘겨 주고, 프록시 뒤에서는 X-Forwarded-For 우선 - 보관 기간 `AUDIT_LOG_RETENTION_DAYS` 기본 365일, 임시 보관함 정리 루프에 얹어 함께 정리 - 화면: 시스템 로그 표에 대상·접속 주소 열 추가(대상은 UUID 앞 8자만) 자체검증 — 프로젝트 생성·하드삭제를 실화면에서 돌려 두 줄 모두 `프로젝트 e1040640… · 127.0.0.1 · 2026-09-06 09:54/09:55` 로 남는 것 확인. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
72 lines
2.6 KiB
Python
72 lines
2.6 KiB
Python
"""B02_ProjRegister FastAPI 라우터."""
|
|
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
|
|
from B01_Dashboard.B01_Dashboard_Repository_Members import check_project_refs
|
|
from common_util.common_util_auth import require_company
|
|
|
|
from .B02_ProjRegister_Repository import create_project
|
|
from .B02_ProjRegister_Schema import CreateProjectRequest, CreateProjectResponse
|
|
|
|
router = APIRouter(prefix="/api/b02", tags=["B02_ProjRegister"])
|
|
|
|
|
|
@router.post("/project", response_model=CreateProjectResponse)
|
|
async def post_project(
|
|
request: Request,
|
|
payload: CreateProjectRequest,
|
|
session: dict[str, Any] = Depends(require_company),
|
|
) -> CreateProjectResponse:
|
|
"""신규 프로젝트를 생성한다."""
|
|
|
|
company_id = session.get("company_id")
|
|
if company_id is None:
|
|
raise HTTPException(status_code=403, detail="회사 연결이 필요합니다.")
|
|
|
|
title_block = payload.model_dump(
|
|
include={
|
|
"client_org",
|
|
"project_number",
|
|
"work_amount",
|
|
"design_date",
|
|
"pm_user_id",
|
|
"field_lead_user_id",
|
|
"designer_user_id",
|
|
"logo_asset_id",
|
|
"route_start_m",
|
|
"route_end_m",
|
|
}
|
|
)
|
|
# 시작이 종료보다 뒤면 남는 구간이 없다 — 저장 전에 막는다 (2026-09-04 사용자 지시).
|
|
start_m, end_m = payload.route_start_m, payload.route_end_m
|
|
if start_m is not None and end_m is not None and start_m >= end_m:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="노선 시작 누가거리는 종료 누가거리보다 작아야 합니다.",
|
|
)
|
|
# 담당자·로고는 같은 회사 것만 (B01 프로젝트 수정과 같은 규칙).
|
|
await check_project_refs(int(company_id), title_block)
|
|
|
|
try:
|
|
result = await create_project(
|
|
request=request,
|
|
user_id=int(session["user_id"]),
|
|
company_id=int(company_id),
|
|
name=payload.name.strip(),
|
|
region=payload.region.strip(),
|
|
road_type=payload.road_type,
|
|
project_year=payload.project_year,
|
|
estimated_length_m=payload.estimated_length_m,
|
|
memo=payload.memo.strip() if payload.memo else None,
|
|
title_block=title_block,
|
|
)
|
|
return CreateProjectResponse(**result)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
except Exception as exc:
|
|
raise HTTPException(
|
|
status_code=500, detail="프로젝트 생성 처리 중 오류가 발생했습니다."
|
|
) from exc
|