This commit is contained in:
2026-07-08 20:28:24 +09:00
parent 38b5c2492e
commit 76d91efcb8
11 changed files with 1064 additions and 7 deletions
+224
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import json
import shutil
from datetime import datetime, timedelta
from typing import Any
@@ -116,6 +117,69 @@ async def list_all_projects() -> list[dict[str, Any]]:
return [_project_row(row) for row in await cursor.fetchall()]
async def get_project(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, user_id, company_id, name, region, road_type, project_year,
estimated_length_m, memo, status
FROM projects WHERE id = %s AND deleted_at IS NULL""",
(project_id,),
)
return await cursor.fetchone()
async def update_project(project_id: str, data: dict[str, Any], actor_id: int) -> bool:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await connection.begin()
await cursor.execute(
"""UPDATE projects
SET name = %s, region = %s, road_type = %s, project_year = %s,
estimated_length_m = %s, memo = %s, status = COALESCE(%s, status)
WHERE id = %s AND deleted_at IS NULL""",
(
data["name"],
data.get("region"),
data.get("road_type"),
data.get("project_year"),
data.get("estimated_length_m"),
data.get("memo"),
data.get("status"),
project_id,
),
)
changed = cursor.rowcount > 0
if changed:
await cursor.execute(
"""INSERT INTO system_audit_logs (user_id, action, resource_type, resource_id)
VALUES (%s, 'PROJECT_UPDATE', 'project', NULL)""",
(actor_id,),
)
await connection.commit()
return changed
async def soft_delete_project(project_id: str, actor_id: int) -> bool:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await connection.begin()
await cursor.execute(
"""UPDATE projects SET deleted_at = CURRENT_TIMESTAMP
WHERE id = %s AND deleted_at IS NULL""",
(project_id,),
)
changed = cursor.rowcount > 0
if changed:
await cursor.execute(
"""INSERT INTO system_audit_logs (user_id, action, resource_type, resource_id)
VALUES (%s, 'PROJECT_DELETE', 'project', NULL)""",
(actor_id,),
)
await connection.commit()
return changed
async def get_user_company(company_id: int | None) -> dict[str, Any] | None:
if company_id is None:
return None
@@ -358,6 +422,55 @@ async def change_user_role(user_id: int, role: str) -> bool:
return changed
async def get_user_admin_target(user_id: int) -> 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, company_id, role, is_master, status
FROM users WHERE id = %s AND deleted_at IS NULL""",
(user_id,),
)
row = await cursor.fetchone()
if row:
row["role"] = _role(row.get("role"))
row["is_master"] = bool(row.get("is_master"))
return row
async def update_admin_user(user_id: int, data: dict[str, Any]) -> bool:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await cursor.execute(
"""UPDATE users
SET name = %s, position = %s, department = %s, phone = %s,
status = COALESCE(%s, status)
WHERE id = %s AND deleted_at IS NULL""",
(
data["name"],
data.get("position"),
data.get("department"),
data.get("phone"),
data.get("status"),
user_id,
),
)
changed = cursor.rowcount > 0
await connection.commit()
return changed
async def count_company_admins(company_id: int) -> int:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
await cursor.execute(
"""SELECT COUNT(*) AS cnt FROM users
WHERE company_id = %s AND role = 'ADMIN' AND deleted_at IS NULL""",
(company_id,),
)
row = await cursor.fetchone()
return int(row["cnt"] if row else 0)
async def assign_user_company(user_id: int, company_id: int | None) -> bool:
status = "ACTIVE" if company_id else "NO_COMPANY"
pool = get_db_pool()
@@ -442,3 +555,114 @@ async def get_system_resources(days: int = 30) -> dict[str, Any]:
(bucket_seconds, bucket_seconds, since, bucket_seconds),
)
return {"current": current, "history": list(await cursor.fetchall()), "stats": current}
async def list_project_automations(project_id: str) -> list[dict[str, Any]]:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
await cursor.execute(
"""SELECT id, project_id, name, logic_type, config_json, status,
last_executed_at, created_at, updated_at
FROM project_automations
WHERE project_id = %s AND deleted_at IS NULL
ORDER BY updated_at DESC, created_at DESC""",
(project_id,),
)
return list(await cursor.fetchall())
async def create_project_automation(
project_id: str, actor_id: int, data: dict[str, Any]
) -> dict[str, Any]:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
await connection.begin()
await cursor.execute(
"""INSERT INTO project_automations
(project_id, name, logic_type, config_json, status, created_by, updated_by)
VALUES (%s, %s, %s, %s, %s, %s, %s)""",
(
project_id,
data["name"],
data["logic_type"],
json.dumps(data["config_json"], ensure_ascii=False),
data["status"],
actor_id,
actor_id,
),
)
automation_id = cursor.lastrowid
await connection.commit()
await cursor.execute("SELECT * FROM project_automations WHERE id = %s", (automation_id,))
return await cursor.fetchone()
async def get_project_automation(automation_id: int) -> dict[str, Any] | None:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
await cursor.execute(
"""SELECT a.*, p.company_id
FROM project_automations a
JOIN projects p ON p.id = a.project_id
WHERE a.id = %s AND a.deleted_at IS NULL AND p.deleted_at IS NULL""",
(automation_id,),
)
return await cursor.fetchone()
async def update_project_automation(
automation_id: int, actor_id: int, data: dict[str, Any]
) -> bool:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await cursor.execute(
"""UPDATE project_automations
SET name = %s, logic_type = %s, config_json = %s, status = %s, updated_by = %s
WHERE id = %s AND deleted_at IS NULL""",
(
data["name"],
data["logic_type"],
json.dumps(data["config_json"], ensure_ascii=False),
data["status"],
actor_id,
automation_id,
),
)
changed = cursor.rowcount > 0
await connection.commit()
return changed
async def delete_project_automation(automation_id: int, actor_id: int) -> bool:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await connection.begin()
await cursor.execute(
"""UPDATE project_automations
SET deleted_at = CURRENT_TIMESTAMP, updated_by = %s
WHERE id = %s AND deleted_at IS NULL""",
(actor_id, automation_id),
)
changed = cursor.rowcount > 0
if changed:
await cursor.execute(
"""INSERT INTO system_audit_logs (user_id, action, resource_type, resource_id)
VALUES (%s, 'AUTOMATION_DELETE', 'automation', %s)""",
(actor_id, automation_id),
)
await connection.commit()
return changed
async def mark_project_automation_executed(automation_id: int, actor_id: int) -> bool:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await cursor.execute(
"""UPDATE project_automations
SET last_executed_at = CURRENT_TIMESTAMP, updated_by = %s
WHERE id = %s AND deleted_at IS NULL""",
(actor_id, automation_id),
)
changed = cursor.rowcount > 0
await connection.commit()
return changed