""" models.py 数据模型模块:使用 Pydantic 定义请求/响应的数据结构。 FastAPI 会自动用这些模型做参数校验和文档生成。 """ from pydantic import BaseModel, Field from typing import Optional, List from datetime import datetime # ═══════════════════════════════════════════════════════════════ # 设备相关模型 # ═══════════════════════════════════════════════════════════════ class DeviceCheckRequest(BaseModel): """设备校验请求:APP 启动时携带 SN 进行合法性校验""" sn: str = Field(..., min_length=5, description="设备序列号,如 GD30-20260507-001") class LicenseGenerateRequest(BaseModel): """生成授权文件请求:管理后台为某台设备创建授权""" sn: str = Field(..., description="目标设备 SN") modules: List[str] = Field(default=[], description="激活的模块列表,如 ['1D', '2D']") valid_days: int = Field(default=365, ge=1, le=3650, description="授权有效期(天),默认一年") config_id: Optional[int] = Field(None, description="关联的配置文件 ID,授权文件中会嵌入该配置") class ActivateReportRequest(BaseModel): """上报激活状态请求:设备主机首次联网后回传激活结果""" sn: str = Field(..., description="设备序列号") status: str = Field(..., pattern="^(已激活|激活失败)$", description="上报状态:已激活 或 激活失败") class DeviceInfo(BaseModel): """设备信息响应""" sn: str status: str activated_at: Optional[str] = None created_at: str class CheckResponse(BaseModel): """设备校验响应:告诉 APP 这台设备是否合法、是否已激活""" valid: bool = Field(..., description="SN 是否存在于平台") activated: bool = Field(..., description="是否已激活") message: str = Field(..., description="给 APP 的提示信息") class LicenseData(BaseModel): """授权文件内的业务数据结构(未加密前)""" version: str = Field(default="1.0", description="授权文件版本") device_sn: str = Field(..., description="绑定的设备 SN") generated_at: str = Field(..., description="生成时间 ISO8601") valid_until: str = Field(..., description="有效期截止时间") modules: List[str] = Field(default=[], description="授权模块列表") status: str = Field(default="active", description="授权状态") class LicenseResponse(BaseModel): """生成授权文件响应:返回加密后的授权文件字符串""" success: bool encrypted_license: Optional[str] = Field(None, description="Base64 编码的加密授权文件") raw_license: Optional[dict] = Field(None, description="明文授权文件(仅调试用,生产环境可去掉)") message: str class ActivateResponse(BaseModel): """上报激活状态响应""" success: bool sn: str new_status: str activated_at: Optional[str] = None message: str # ═══════════════════════════════════════════════════════════════ # 配置文件相关模型 # ═══════════════════════════════════════════════════════════════ class ConfigFileCreateRequest(BaseModel): """创建配置文件请求""" name: str = Field(..., description="配置名称,如 GD30-2024-标准参数") model: str = Field(..., description="适用设备型号,如 GD30-2024") version: str = Field(default="v1.0", description="配置版本号") status: str = Field(default="生效", description="状态:生效/失效") # 发射参数 max_tx_voltage: Optional[str] = Field(None, description="最大发射电压,如 1200V / 1500V") max_tx_current: Optional[str] = Field(None, description="最大发射电流,如 6A / 10A") tx_waveform: Optional[str] = Field(None, description="发射波形,如 0+0- / +0-0 / +-") tx_pulse_width: Optional[str] = Field(None, description="发射脉宽,如 0.25s/0.5s/1s/2s/4s/8s/16s") # 采集参数 acq_channels: Optional[str] = Field(None, description="支持通道数,如 1 / 6 / 12") acq_sample_rate: Optional[str] = Field(None, description="采样率,如 50Hz/60Hz/100Hz/1000Hz") acq_voltage_range: Optional[str] = Field(None, description="电压测量量程,如 ±2.5V/±80V / ±80V/±600V") full_waveform_capture: Optional[str] = Field(None, description="全波形采集,如 支持 / 不支持") # 网络参数 ssid_prefix: Optional[str] = Field(None, description="WiFi SSID前缀,如 GD30-AP") class ConfigFileUpdateRequest(BaseModel): """更新配置文件请求""" name: Optional[str] = Field(None, description="配置名称") model: Optional[str] = Field(None, description="适用设备型号") version: Optional[str] = Field(None, description="配置版本号") status: Optional[str] = Field(None, description="状态:生效/失效") # 发射参数 max_tx_voltage: Optional[str] = Field(None, description="最大发射电压") max_tx_current: Optional[str] = Field(None, description="最大发射电流") tx_waveform: Optional[str] = Field(None, description="发射波形") tx_pulse_width: Optional[str] = Field(None, description="发射脉宽") # 采集参数 acq_channels: Optional[str] = Field(None, description="支持通道数") acq_sample_rate: Optional[str] = Field(None, description="采样率") acq_voltage_range: Optional[str] = Field(None, description="电压测量量程") full_waveform_capture: Optional[str] = Field(None, description="全波形采集") # 网络参数 ssid_prefix: Optional[str] = Field(None, description="WiFi SSID前缀") class ConfigFileInfo(BaseModel): """配置文件信息响应""" id: int name: str model: str version: str status: str # 发射参数 max_tx_voltage: Optional[str] = None max_tx_current: Optional[str] = None tx_waveform: Optional[str] = None tx_pulse_width: Optional[str] = None # 采集参数 acq_channels: Optional[str] = None acq_sample_rate: Optional[str] = None acq_voltage_range: Optional[str] = None full_waveform_capture: Optional[str] = None # 网络参数 ssid_prefix: Optional[str] = None created_at: Optional[str] = None updated_at: Optional[str] = None # ═══════════════════════════════════════════════════════════════ # 校准文件相关模型 # ═══════════════════════════════════════════════════════════════ class CalibChannelData(BaseModel): """校准通道数据""" ChannelId: int = Field(..., description="通道ID") factor_80v: float = Field(..., description="80V量程校准系数") offset_80v: float = Field(..., description="80V量程偏移量") factor_2_5v: float = Field(..., description="2.5V量程校准系数") offset_2_5v: float = Field(..., description="2.5V量程偏移量") class CalibrationUploadRequest(BaseModel): """校准文件上传请求(JSON格式,由Windows校准软件调用)""" SN: str = Field(..., description="校准文件中的SN") UID: str = Field(..., description="校准文件中的UID") CalibrateFactor: List[dict] = Field(..., description="各通道校准系数数组") class CalibrationFileInfo(BaseModel): """校准文件信息响应""" id: int material_sn: str = Field(..., description="关联物料SN") sn: Optional[str] = Field(None, description="校准SN") uid: Optional[str] = Field(None, description="校准UID") file_name: Optional[str] = Field(None, description="文件名") file_size: Optional[int] = Field(None, description="文件大小(字节)") md5: Optional[str] = Field(None, description="文件MD5") result: Optional[str] = Field(None, description="校准结果:合格/不合格") operator: Optional[str] = Field(None, description="操作员") remark: Optional[str] = Field(None, description="备注") upload_time: Optional[str] = Field(None, description="上传时间") channels_count: Optional[int] = Field(None, description="通道数量") channels: Optional[List[dict]] = Field(None, description="通道详细数据") class CalibrationUpdateRequest(BaseModel): """校准文件更新请求""" operator: Optional[str] = Field(None, description="操作员") remark: Optional[str] = Field(None, description="备注") result: Optional[str] = Field(None, description="校准结果") # ═══════════════════════════════════════════════════════════════ # APP 版本相关模型 # ═══════════════════════════════════════════════════════════════ class AppVersionCreateRequest(BaseModel): """创建 APP 版本请求""" app_name: str = Field(..., description="应用名称,如 GeoData Mobile") package_name: Optional[str] = Field(None, description="包名,如 com.geomative.geodata") platform_type: int = Field(default=2, description="平台类型:1=iOS, 2=Android") version_name: str = Field(..., description="版本号名称,如 1.2.0") major_version: int = Field(default=1, ge=0) minor_version: int = Field(default=0, ge=0) patch_version: int = Field(default=0, ge=0) file_type: str = Field(default="apk", description="文件类型:apk / ipa") distribution_type: str = Field(default="direct", description="分发方式:direct / appstore") os_min_version: Optional[str] = Field(None, description="最低系统版本要求") is_force_update: bool = Field(default=False, description="是否强制更新") changelog: Optional[List[str]] = Field(None, description="更新日志列表") status: int = Field(default=1, description="状态:0=草稿, 1=已发布, 2=已下架") class AppVersionUpdateRequest(BaseModel): """更新 APP 版本请求""" app_name: Optional[str] = Field(None, description="应用名称") package_name: Optional[str] = Field(None, description="包名") version_name: Optional[str] = Field(None, description="版本号") platform_type: Optional[int] = Field(None, description="平台类型") os_min_version: Optional[str] = Field(None, description="最低系统版本") is_force_update: Optional[bool] = Field(None, description="是否强制更新") changelog: Optional[List[str]] = Field(None, description="更新日志") status: Optional[int] = Field(None, description="状态:0=草稿, 1=已发布, 2=已下架") class AppVersionInfo(BaseModel): """APP 版本信息响应""" id: int app_name: str package_name: Optional[str] = None platform_type: int version_name: str major_version: int minor_version: int patch_version: int file_name: Optional[str] = None file_size: Optional[int] = None file_type: Optional[str] = None distribution_type: Optional[str] = None primary_url: Optional[str] = None os_min_version: Optional[str] = None is_force_update: bool = False changelog: Optional[str] = None status: int created_at: Optional[str] = None updated_at: Optional[str] = None class AppDownloadQuery(BaseModel): """APP 下载查询参数""" app_name: Optional[str] = Field(None, description="应用名称") platform_type: Optional[int] = Field(None, description="平台类型:1=iOS, 2=Android") version_name: Optional[str] = Field(None, description="指定版本号") class AppCheckUpdateRequest(BaseModel): """APP 检查更新请求""" app_name: str = Field(..., description="应用名称") platform_type: int = Field(..., description="平台类型:1=iOS, 2=Android") current_version: str = Field(..., description="当前版本号,如 1.1.0") class AppCheckUpdateResponse(BaseModel): """APP 检查更新响应""" has_update: bool = Field(..., description="是否有更新") force_update: bool = Field(..., description="是否强制更新") version_name: Optional[str] = Field(None, description="最新版本号") download_url: Optional[str] = Field(None, description="下载地址") changelog: Optional[List[str]] = Field(None, description="更新日志") file_size: Optional[int] = Field(None, description="文件大小") # ═══════════════════════════════════════════════════════════════ # 固件版本相关模型 # ═══════════════════════════════════════════════════════════════ class FirmwareCreateRequest(BaseModel): """创建固件版本请求""" version: str = Field(..., description="固件版本号,如 v3.2.1") firmware_type: str = Field(..., description="固件类型:采集板/发射板/主协板/主机服务") board_model: Optional[str] = Field(None, description="板卡型号,如 GD30-ACQ-01") device_model: Optional[str] = Field(None, description="设备型号,如 GD30-2024") hw_range: Optional[str] = Field(None, description="硬件适用范围") upgrade_type: str = Field(default="可选", description="升级类型:强制/可选") signed: bool = Field(default=False, description="是否已签名") notes: Optional[List[str]] = Field(None, description="更新说明列表") status: str = Field(default="已发布", description="状态:草稿/已发布/已下架/兼容") class FirmwareUpdateRequest(BaseModel): """更新固件版本请求""" version: Optional[str] = Field(None, description="版本号") firmware_type: Optional[str] = Field(None, description="固件类型") board_model: Optional[str] = Field(None, description="板卡型号") device_model: Optional[str] = Field(None, description="设备型号") hw_range: Optional[str] = Field(None, description="硬件适用范围") upgrade_type: Optional[str] = Field(None, description="升级类型") signed: Optional[bool] = Field(None, description="是否已签名") notes: Optional[List[str]] = Field(None, description="更新说明") status: Optional[str] = Field(None, description="状态") class FirmwareInfo(BaseModel): """固件版本信息响应""" id: int version: str firmware_type: str board_model: Optional[str] = None device_model: Optional[str] = None file_name: Optional[str] = None file_size: Optional[int] = None hw_range: Optional[str] = None upgrade_type: Optional[str] = None signed: bool = False notes: Optional[str] = None status: str created_at: Optional[str] = None updated_at: Optional[str] = None class FirmwareDownloadQuery(BaseModel): """固件下载查询参数""" firmware_type: str = Field(..., description="固件类型") device_model: Optional[str] = Field(None, description="设备型号") board_model: Optional[str] = Field(None, description="板卡型号") class FirmwareCheckUpdateRequest(BaseModel): """固件检查更新请求""" firmware_type: str = Field(..., description="固件类型") device_model: Optional[str] = Field(None, description="设备型号") board_model: Optional[str] = Field(None, description="板卡型号") current_version: str = Field(..., description="当前版本号") class FirmwareCheckUpdateResponse(BaseModel): """固件检查更新响应""" has_update: bool = Field(..., description="是否有更新") force_update: bool = Field(..., description="是否强制更新") version: Optional[str] = Field(None, description="最新版本号") download_url: Optional[str] = Field(None, description="下载地址") notes: Optional[List[str]] = Field(None, description="更新说明") file_size: Optional[int] = Field(None, description="文件大小")