260710_0
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
"""B02_ProjRegister aiomysql Raw SQL 저장소."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
import aiomysql
|
||||
|
||||
from common_util.common_util_json import atomic_write_json
|
||||
from common_util.common_util_workflow import load_project_workflow
|
||||
from config.config_db import get_db_pool
|
||||
from config.config_system import STORAGE_BASE_DIR
|
||||
|
||||
PROJECT_STAGE_SUBDIRS = (
|
||||
("B03_FileInput", "input"),
|
||||
("B04_wf1_Surface", "processed"),
|
||||
("B05_wf2_Route", "route"),
|
||||
("B06_wf3_ProfileCross", "profiles"),
|
||||
("B07_wf4_DesignDetail", "structures"),
|
||||
("B08_wf5_Quantity", "quantities"),
|
||||
("B09_wf6_Estimation", "v1"),
|
||||
)
|
||||
|
||||
|
||||
def _build_project_storage(company_id: int, user_id: int, project_id: str) -> tuple[str, Path]:
|
||||
relative_path = f"storage/{company_id}/{user_id}/{project_id}"
|
||||
storage_root = Path(STORAGE_BASE_DIR).resolve()
|
||||
project_root = (storage_root / str(company_id) / str(user_id) / project_id).resolve()
|
||||
if storage_root not in (project_root, *project_root.parents):
|
||||
raise ValueError("프로젝트 저장 경로가 저장소 루트를 벗어났습니다.")
|
||||
return relative_path, project_root
|
||||
|
||||
|
||||
def _initialize_project_storage(project_root: Path, project_id: str) -> None:
|
||||
for stage, subdir in PROJECT_STAGE_SUBDIRS:
|
||||
(project_root / stage / subdir).mkdir(parents=True, exist_ok=True)
|
||||
atomic_write_json(project_root / "workflow.json", load_project_workflow(project_root))
|
||||
atomic_write_json(
|
||||
project_root / "project_manifest.json",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"storage_version": 1,
|
||||
"created_at": datetime.utcnow().isoformat(timespec="seconds"),
|
||||
"stages": [f"{stage}/{subdir}" for stage, subdir in PROJECT_STAGE_SUBDIRS],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def create_project(
|
||||
*,
|
||||
user_id: int,
|
||||
company_id: int,
|
||||
name: str,
|
||||
region: str,
|
||||
road_type: str,
|
||||
project_year: int,
|
||||
estimated_length_m: float | None,
|
||||
memo: str | None,
|
||||
) -> dict[str, Any]:
|
||||
"""신규 프로젝트를 DB에 저장하고 워크플로우 저장소를 초기화한다."""
|
||||
|
||||
project_id = str(uuid4())
|
||||
storage_path, project_root = _build_project_storage(company_id, user_id, project_id)
|
||||
now = datetime.utcnow()
|
||||
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
|
||||
try:
|
||||
await connection.begin()
|
||||
await cursor.execute(
|
||||
"""
|
||||
INSERT INTO projects (
|
||||
id, user_id, company_id, name, region, road_type,
|
||||
project_year, estimated_length_m, memo, status,
|
||||
crs_epsg, storage_path, created_at, updated_at
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, 'NEW', 5178, %s, %s, %s)
|
||||
""",
|
||||
(
|
||||
project_id,
|
||||
user_id,
|
||||
company_id,
|
||||
name,
|
||||
region,
|
||||
road_type,
|
||||
project_year,
|
||||
estimated_length_m,
|
||||
memo,
|
||||
storage_path,
|
||||
now,
|
||||
now,
|
||||
),
|
||||
)
|
||||
await cursor.execute(
|
||||
"""INSERT INTO system_audit_logs (user_id, action, resource_type, resource_id)
|
||||
VALUES (%s, 'PROJECT_CREATE', 'project', NULL)""",
|
||||
(user_id,),
|
||||
)
|
||||
_initialize_project_storage(project_root, project_id)
|
||||
await connection.commit()
|
||||
except Exception:
|
||||
await connection.rollback()
|
||||
raise
|
||||
|
||||
await cursor.execute(
|
||||
"""
|
||||
SELECT id AS project_id, name, region, road_type, project_year,
|
||||
estimated_length_m, memo, status, storage_path,
|
||||
DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%H:%%i:%%s') AS created_at
|
||||
FROM projects
|
||||
WHERE id = %s
|
||||
""",
|
||||
(project_id,),
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
raise RuntimeError("생성된 프로젝트를 다시 조회할 수 없습니다.")
|
||||
return dict(row)
|
||||
|
||||
|
||||
async def get_project_by_id(project_id: str) -> dict[str, Any] | None:
|
||||
"""프로젝트 단건 조회."""
|
||||
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
|
||||
await cursor.execute(
|
||||
"""
|
||||
SELECT id AS project_id, user_id, company_id, name, region, road_type,
|
||||
project_year, estimated_length_m, memo, status, storage_path,
|
||||
DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%H:%%i:%%s') AS created_at
|
||||
FROM projects
|
||||
WHERE id = %s AND deleted_at IS NULL
|
||||
""",
|
||||
(project_id,),
|
||||
)
|
||||
return await cursor.fetchone()
|
||||
Reference in New Issue
Block a user