import json from datetime import datetime from fastapi import APIRouter, Depends, HTTPException from app.core.security import require_permission from app.repositories import policy_repository from app.schemas.policy import PolicySaveRequest from app.services.common_service import policy_row_to_dict, validate_channel_code router = APIRouter(tags=["admin-policy"]) @router.get("/admin/policy") def admin_get_policy(app_id: str, channel: str, auth=Depends(require_permission("policy:view"))): row = policy_repository.get_policy(app_id, channel) result = policy_row_to_dict(row) result.update({"app_id": app_id, "channel": channel, "saved": bool(row)}) return result @router.post("/admin/policy/save") def admin_save_policy(body: PolicySaveRequest, auth=Depends(require_permission("policy:manage"))): app_id = body.app_id.strip() channel = body.channel.strip() if not app_id or not validate_channel_code(channel): raise HTTPException(status_code=400, detail="App ID 或渠道无效") disabled = body.disabled_versions or [] if any(not isinstance(v, str) for v in disabled): raise HTTPException(status_code=400, detail="disabled_versions 必须是版本字符串数组") valid_until = body.valid_until.strip() try: datetime.fromisoformat(valid_until.replace("Z", "+00:00")) except ValueError: raise HTTPException(status_code=400, detail="valid_until 必须是 ISO-8601 时间") result, next_seq = policy_repository.save_policy( app_id, channel, bool(body.force_update), bool(body.allow_rollback), bool(body.offline_allowed), valid_until, body.min_supported_version.strip(), json.dumps(disabled, ensure_ascii=False), body.message.strip(), ) if result == "channel_not_found": raise HTTPException(status_code=400, detail={"error": "channel_not_found", "msg": f"渠道 {channel} 不存在"}) if result == "channel_disabled": raise HTTPException(status_code=403, detail={"error": "channel_disabled", "msg": f"渠道 {channel} 已停用"}) return {"msg": "策略已保存并递增 policy_seq", "policy_seq": next_seq}