идентификаторы удалений, заготовки

This commit is contained in:
2026-08-20 13:51:51 +03:00
parent 9422719ea6
commit d90c4500ad
4 changed files with 166 additions and 25 deletions

View File

@@ -3906,12 +3906,9 @@ def create_hockey_agent_router(
async def mapping_data_catalog(payload: MappingDataContextPayload, user: HockeyUser = Depends(auth_dependency)) -> dict[str, Any]:
return hub.mapping_data.data_catalog(user, payload.context)
@router.post("/api/hockey/context/{key}")
async def hockey_context_set(key: str, payload: ContextValuePayload, user: HockeyUser = Depends(auth_dependency)) -> dict[str, Any]:
result = hub.mapping_data.set_context_value(key, payload.value, user, payload.context)
result["mapping_apply"] = await hub.apply_mapping_for_user(user, reason=f"context_changed:{key}")
return result
# BUILD86: static context routes MUST be registered before /{key}.
# Starlette matches routes in registration order; the old order treated the
# literal word "batch" (and "resolve") as a context variable key.
@router.post("/api/hockey/context/batch")
async def hockey_context_batch(payload: ContextValuesPayload, user: HockeyUser = Depends(auth_dependency)) -> dict[str, Any]:
if len(payload.values) > 32:
@@ -3934,6 +3931,12 @@ def create_hockey_agent_router(
context, variables = hub.mapping_data.resolve_context(user, payload.context)
return {"context": context, "variables": variables}
@router.post("/api/hockey/context/{key}")
async def hockey_context_set(key: str, payload: ContextValuePayload, user: HockeyUser = Depends(auth_dependency)) -> dict[str, Any]:
result = hub.mapping_data.set_context_value(key, payload.value, user, payload.context)
result["mapping_apply"] = await hub.apply_mapping_for_user(user, reason=f"context_changed:{key}")
return result
@router.websocket("/ws/hockey-agent")
async def agent_socket(websocket: WebSocket) -> None:
await websocket.accept()

View File

@@ -687,9 +687,39 @@ class MappingDataService:
context, _ = self.resolve_context(user, supplied)
with self.database.session() as session:
variable = session.scalar(select(MappingContextVariable).where(MappingContextVariable.key == key))
if variable is None: raise HTTPException(status_code=404, detail="Переменная не найдена")
if variable is None:
# BUILD86: be defensive with long-lived production databases.
# Penalty selection keys were added over several builds; if a row
# is missing for any reason, restore the known default contract
# immediately instead of breaking an operator click with 404.
default_index = next((i for i, item in enumerate(DEFAULT_CONTEXT_VARIABLES) if item.get("key") == key), -1)
default_item = DEFAULT_CONTEXT_VARIABLES[default_index] if default_index >= 0 else None
if default_item is not None:
now = _utcnow()
variable = MappingContextVariable(
key=default_item["key"],
label=default_item["label"],
category=default_item.get("category", "Контекст"),
description=default_item.get("description", ""),
value_type=default_item.get("value_type", "id"),
entity_type=default_item.get("entity_type", ""),
scope=default_item.get("scope", "match"),
source_type=default_item.get("source_type", "manual"),
default_value=default_item.get("default_value", ""),
is_system=default_item.get("source_type") == "system",
enabled=True,
sort_order=default_index,
created_by="system",
updated_by="system",
created_at=now,
updated_at=now,
)
session.add(variable)
session.flush()
else:
raise HTTPException(status_code=404, detail=f"Переменная не найдена: {key}")
if variable.source_type == "system" or variable.is_system:
raise HTTPException(status_code=409, detail="Системная переменная задаётся программой")
raise HTTPException(status_code=409, detail=f"Системная переменная задаётся программой: {key}")
scope_key = self._scope_key(variable, user, context)
row = session.scalar(select(MappingContextValue).where(and_(MappingContextValue.variable_id == variable.id, MappingContextValue.scope_key == scope_key)))
if row is None: