result_models.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. """结果接收平台的 JSON 契约;与取数网关及内部审计结构分开。"""
  2. from datetime import datetime
  3. from typing import Literal
  4. from urllib.parse import urlsplit
  5. from pydantic import BaseModel, ConfigDict, Field, field_validator
  6. ResultCode = Literal["NORMAL", "WARNING", "ABNORMAL", "UNDETERMINED"]
  7. class PlatformJudgment(BaseModel):
  8. model_config = ConfigDict(extra="forbid", strict=True)
  9. resultCode: ResultCode
  10. result: str = Field(min_length=1)
  11. reason: str = Field(min_length=1)
  12. evidenceLink: str = ""
  13. @field_validator("evidenceLink")
  14. @classmethod
  15. def evidence_url(cls, value):
  16. if value:
  17. parts = urlsplit(value)
  18. if parts.scheme not in {"http", "https"} or not parts.netloc or parts.username or parts.password:
  19. raise ValueError("evidenceLink 只能填写可访问的 HTTP(S) 证据页地址或空字符串")
  20. return value
  21. @field_validator("reason")
  22. @classmethod
  23. def report_text(cls, value):
  24. if "data:image/" in value or ";base64," in value:
  25. raise ValueError("报告正文不能包含图片编码")
  26. return value
  27. class PlatformResultPayload(BaseModel):
  28. model_config = ConfigDict(extra="forbid", strict=True)
  29. agentCode: str = Field(min_length=1, max_length=64)
  30. agentName: str = Field(min_length=1)
  31. taskId: str = Field(pattern=r"^[0-9a-f]{32}$")
  32. checkedAt: str
  33. mineCode: str = Field(min_length=1)
  34. mineName: str = Field(min_length=1)
  35. objectDetail: str = Field(min_length=1)
  36. dataDetail: str
  37. judgment: PlatformJudgment
  38. @field_validator("checkedAt")
  39. @classmethod
  40. def completion_time(cls, value):
  41. parsed = datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
  42. if parsed.strftime("%Y-%m-%d %H:%M:%S") != value:
  43. raise ValueError("checkedAt 必须使用 yyyy-MM-dd HH:mm:ss(Asia/Shanghai)")
  44. return value
  45. @field_validator("agentCode", "agentName", "mineCode", "mineName", "objectDetail")
  46. @classmethod
  47. def nonblank(cls, value):
  48. if not value.strip():
  49. raise ValueError("平台身份及研判对象不能为空白")
  50. return value