Files
ecm_sync_system/sync_state_machine/domain/personnel/api_handler.py
T
strepsiades f5729d5a18 整理代码
2026-04-03 09:18:44 +08:00

303 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Personnel API Handler
职责:
- 加载人员列表(依赖 Project)
- 人员创建/更新/删除(批量接口)
"""
from typing import List, Dict, Any
from ...datasource.api.handler import BaseApiHandler
from ...datasource.task_result import TaskResult
from ...common.sync_node import SyncNode
from ...datasource.api.errors import ERROR_NODE_DATA_NONE, ERROR_PROJECT_ID_NOT_FOUND
from .sync_node import PersonnelSyncNode
from schemas.power.power import PowerResponse, PowerCreate, PowerUpdate, PowerPlanUpdate
class PersonnelApiHandler(BaseApiHandler):
"""人员 API Handler"""
def __init__(self):
super().__init__(node_class=PersonnelSyncNode, schema=PowerResponse)
self.update_schemas = [PowerCreate, PowerUpdate, PowerPlanUpdate]
async def load(self) -> List[SyncNode]:
"""加载人员列表(依赖 Project"""
projects = self._collection.filter(node_type="project")
project_ids = [p.data_id for p in projects if p.data_id]
if not project_ids:
return []
all_personnel = []
for project_id in project_ids:
try:
personnel_list = await api_get_personnel_list(self.api_client, project_id)
for personnel in personnel_list:
all_personnel.append(self._create_node(personnel))
except Exception as e:
print(f"❌ Failed to load personnel for project {project_id}: {e}")
return all_personnel
async def create_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
"""批量创建人员"""
results = []
for node in nodes:
data = node.get_data()
if not data:
results.append(TaskResult.failed(node_id=node.node_id, error=ERROR_NODE_DATA_NONE))
continue
project_id = node.context.get("project_id") or data.get("project_id")
if not project_id:
results.append(TaskResult.failed(node_id=node.node_id, error=ERROR_PROJECT_ID_NOT_FOUND))
continue
push_id = self._generate_push_id()
try:
request_data = {"project_id": project_id, **data}
PowerCreate.model_validate(request_data)
response = await api_create_personnel(
self.api_client,
request_data,
push_id,
steps=self._get_request_steps(node),
)
if self.poll_mode == "sync":
created_id = self._extract_created_id_from_response(response)
if not created_id:
results.append(
TaskResult.failed(
node_id=node.node_id,
error="Missing created_id in create response"
)
)
continue
results.append(TaskResult.success(node_id=node.node_id, data_id=created_id))
else:
results.append(TaskResult.in_progress(node_id=node.node_id, task_id=push_id))
except Exception as e:
results.append(TaskResult.failed(node_id=node.node_id, error=str(e)))
return results
async def update_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
"""
批量更新人员
支持两种更新类型:
1. PowerUpdate - 基础信息更新
2. PowerPlanUpdate - 计划信息更新
更新逻辑:
- 对每种 schema,检查是否有差异
- 如果有差异,调用对应 API
"""
from pydantic import ValidationError
results = []
for node in nodes:
data = node.get_data()
if not data:
results.append(TaskResult.failed(node_id=node.node_id, error=ERROR_NODE_DATA_NONE))
continue
origin_data = node.get_origin_data() or {}
biz_id = node.data_id or data.get("id")
if not biz_id:
results.append(TaskResult.failed(node_id=node.node_id, error="Missing biz_id for update"))
continue
node_updated = False
error = None
steps_payload = self._get_request_steps(node)
steps_sent = False
# 1. 基础信息更新 (PowerUpdate)
force_steps_update = self._steps_changed(node)
base_diff = self.get_update_schema_diff(
PowerUpdate,
data,
origin_data,
node.node_id,
force_full_schema=force_steps_update,
)
if base_diff:
try:
update_model = PowerUpdate.model_validate(data)
push_id = self._generate_push_id()
await api_update_personnel(
self.api_client,
update_model.model_dump(),
push_id,
biz_id,
steps=steps_payload if not steps_sent else [],
)
self._mark_approval_snapshot_synced(node)
steps_sent = steps_sent or bool(steps_payload)
node_updated = True
except ValidationError as e:
error = f"Validation failed: {e}"
# 2. 计划信息更新 (PowerPlanUpdate)
plan_fields = [
"plan_start_date",
"plan_start_quantity",
"plan_exit_date",
"plan_node",
]
plan_diff = {
key: data.get(key)
for key in plan_fields
if data.get(key) != origin_data.get(key)
}
if not error and plan_diff:
try:
plan_model = PowerPlanUpdate.model_validate(data)
push_id = self._generate_push_id()
await api_update_personnel_plan(
self.api_client,
plan_model.model_dump(),
push_id,
biz_id,
steps=steps_payload if not steps_sent else [],
)
self._mark_approval_snapshot_synced(node)
steps_sent = steps_sent or bool(steps_payload)
node_updated = True
except ValidationError as e:
error = f"Validation failed: {e}"
# 汇总结果
if error:
results.append(TaskResult.failed(node_id=node.node_id, error=error))
elif node_updated:
results.append(TaskResult.success(node_id=node.node_id))
else:
# 没有任何字段需要更新
from ...common.types import SyncAction
results.append(TaskResult.skipped(node_id=node.node_id, reason="No updatable fields changed"))
return results
async def delete_all(self, nodes: List[SyncNode]) -> List[TaskResult]:
"""批量删除人员"""
results = []
for node in nodes:
push_id = self._generate_push_id()
biz_id = node.data_id
if not biz_id:
results.append(TaskResult.failed(node_id=node.node_id, error="Missing biz_id for delete"))
continue
try:
await api_delete_personnel(self.api_client, biz_id, push_id)
results.append(TaskResult.success(node_id=node.node_id))
except Exception as e:
results.append(TaskResult.failed(node_id=node.node_id, error=str(e)))
return results
async def poll_tasks(self, task_ids: List[str]) -> Dict[str, TaskResult]:
"""轮询异步任务状态(占位)"""
return await super().poll_tasks(task_ids)
def _create_node(self, data: Dict[str, Any]) -> SyncNode:
node = self._create_basic_node(data, depend_ids=[])
if data.get("project_id"):
node.context["project_id"] = data["project_id"]
node.context["personnel_id"] = node.data_id
return node
# ========== 静态 API 函数 ==========
async def api_get_personnel(client, personnel_id: str) -> Dict[str, Any]:
"""获取单个力能数据 GET /personnel"""
response = await client.request(
method="GET",
endpoint="/personnel",
params={"personnel_id": personnel_id}
)
if response.get("code") != 200:
raise Exception(f"API error: {response.get('message', 'Unknown error')}")
return response.get("data", {})
async def api_create_personnel(
client,
data: Dict[str, Any],
push_id: str,
*,
steps: List[Dict[str, Any]] | None = None,
) -> Dict[str, Any]:
"""创建力能 POST /personnel/create"""
request_data = {"push_id": push_id, "data": data, "steps": steps or []}
response = await client.request(method="POST", endpoint="/personnel/create", data=request_data)
if response.get("code") != 200:
raise Exception(f"API error: {response.get('message', 'Unknown error')}")
return response
async def api_update_personnel(
client,
data: Dict[str, Any],
push_id: str,
biz_id: str,
*,
steps: List[Dict[str, Any]] | None = None,
) -> None:
"""更新力能 PUT /personnel/update"""
request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
response = await client.request(method="PUT", endpoint="/personnel/update", data=request_data)
if response.get("code") != 200:
raise Exception(f"API error: {response.get('message', 'Unknown error')}")
# ========== 静态 API 函数 ==========
async def api_get_personnel_list(client, project_id: str) -> List[Dict[str, Any]]:
"""GET /personnel/list - 获取人员列表"""
response = await client.request(
method="GET",
endpoint="/personnel/list",
params={"project_id": project_id}
)
if response.get("code") != 200:
raise Exception(f"API error: {response.get('message', 'Unknown error')}")
items = response.get("data", [])
validated_items: List[Dict[str, Any]] = []
for item in items:
validated = PowerResponse.model_validate(item)
validated_items.append(validated.model_dump())
return validated_items
async def api_delete_personnel(client, personnel_id: str, push_id: str) -> None:
"""删除力能 DELETE /personnel/delete"""
request_data = {"push_id": push_id, "biz_id": personnel_id}
response = await client.request(method="DELETE", endpoint="/personnel/delete", data=request_data)
if response.get("code") != 200:
raise Exception(f"API error: {response.get('message', 'Unknown error')}")
async def api_update_personnel_plan(
client,
data: Dict[str, Any],
push_id: str,
biz_id: str,
*,
steps: List[Dict[str, Any]] | None = None,
) -> None:
"""更新力能计划 PUT /personnel/plan"""
request_data = {"push_id": push_id, "biz_id": biz_id, "data": data, "steps": steps or []}
response = await client.request(method="PUT", endpoint="/personnel/plan", data=request_data)
if response.get("code") != 200:
raise Exception(f"API error: {response.get('message', 'Unknown error')}")