提交 4e31fa9b authored 作者: 陈泽健's avatar 陈泽健

fix(device-sim): 修复创建设备 metadata 字段错误

- service 层:data.metadata → data.extra_attrs
- schema:添加 field_validator 过滤 SQLAlchemy MetaData
- schema:恢复 topic_params 字段和主题模板 Schema
- 前端类型:SimulatorUpdate.metadata → extraAttrs
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 46a566f2
......@@ -11,7 +11,7 @@
from datetime import datetime
from typing import Optional, List, Dict
from pydantic import BaseModel, Field, ConfigDict
from pydantic import BaseModel, Field, ConfigDict, field_validator
def to_camel(string: str) -> str:
......@@ -99,6 +99,7 @@ class SimulatorBase(BaseModel):
device_id: str = Field(..., min_length=1, max_length=200, description="模拟设备 ID")
auto_reconnect: bool = Field(default=True, description="是否自动重连")
extra_attrs: Optional[dict] = Field(default=None, description="设备自定义属性")
topic_params: Optional[Dict[str, str]] = Field(default=None, description="主题动态参数")
model_config = ConfigDict(
alias_generator=to_camel,
......@@ -117,6 +118,7 @@ class SimulatorUpdate(BaseModel):
device_name: Optional[str] = Field(default=None, min_length=1, max_length=200, description="模拟设备名称")
auto_reconnect: Optional[bool] = Field(default=None, description="是否自动重连")
extra_attrs: Optional[dict] = Field(default=None, description="设备自定义属性")
topic_params: Optional[Dict[str, str]] = Field(default=None, description="主题动态参数")
model_config = ConfigDict(
alias_generator=to_camel,
......@@ -134,6 +136,17 @@ class SimulatorResponse(SimulatorBase):
created_at: datetime = Field(..., description="创建时间")
updated_at: datetime = Field(..., description="更新时间")
# 显式声明 metadata 字段,避免 Pydantic 尝试序列化 SQLAlchemy 的 MetaData 对象
metadata: Optional[dict] = Field(default=None, exclude=True)
@field_validator('metadata', mode='before')
@classmethod
def ignore_sqlalchemy_metadata(cls, v):
"""忽略 SQLAlchemy 的 MetaData 对象,只接受 dict 类型"""
if not isinstance(v, dict):
return None
return v
model_config = ConfigDict(
from_attributes=True,
alias_generator=to_camel,
......@@ -197,3 +210,34 @@ class TestConnectionResponse(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
# ==================== 主题模板 Schema ====================
class TopicTemplateResponse(BaseModel):
"""主题模板响应"""
key: str = Field(..., description="模板唯一标识")
template: str = Field(..., description="主题路径模板")
label: str = Field(..., description="中文标签")
params: List[str] = Field(default=[], description="需要填写的动态参数名列表")
param_labels: Dict[str, str] = Field(default={}, description="参数名 -> 中文标签映射")
param_defaults: Dict[str, str] = Field(default={}, description="参数名 -> 默认值映射")
direction: str = Field(..., description="方向:publish(上报) / subscribe(订阅)")
model_config = ConfigDict(
alias_generator=to_camel,
populate_by_name=True,
)
class TopicTemplateListResponse(BaseModel):
"""主题模板列表响应"""
device_type: str = Field(..., description="设备类型")
templates: List[TopicTemplateResponse] = Field(default=[], description="主题模板列表")
has_real_topics: bool = Field(default=False, description="是否有真实主题模板")
model_config = ConfigDict(
alias_generator=to_camel,
populate_by_name=True,
)
......@@ -386,7 +386,8 @@ class DeviceSimService:
device_name=data.device_name,
device_id=data.device_id,
auto_reconnect=data.auto_reconnect,
extra_attrs=data.metadata or {},
extra_attrs=data.extra_attrs or {},
topic_params=data.topic_params or {},
status="stopped",
)
......@@ -507,6 +508,7 @@ class DeviceSimService:
env_config_id=config.id,
mqtt_manager=mqtt_manager,
topics=topics,
topic_params=simulator.topic_params,
)
if not sim:
raise ValueError(f"不支持的设备类型: {simulator.device_type}")
......
......@@ -78,6 +78,7 @@ export interface Simulator {
status: string
autoReconnect: boolean
extraAttrs: Record<string, any> | null
topicParams: Record<string, string> | null
lastReportedAt: string | null
totalReports: number
createdAt: string
......@@ -92,13 +93,15 @@ export interface SimulatorCreate {
deviceId: string
autoReconnect?: boolean
extraAttrs?: Record<string, any> | null
topicParams?: Record<string, string> | null
}
/** 更新模拟设备请求 */
export interface SimulatorUpdate {
deviceName?: string
autoReconnect?: boolean
metadata?: Record<string, any> | null
extraAttrs?: Record<string, any> | null
topicParams?: Record<string, string> | null
}
/** 上报记录 */
......@@ -143,3 +146,21 @@ export interface ReportLogListResponse {
items: ReportLog[]
total: number
}
/** 主题模板 */
export interface TopicTemplate {
key: string
template: string
label: string
params: string[]
paramLabels: Record<string, string>
paramDefaults: Record<string, string>
direction: 'publish' | 'subscribe'
}
/** 主题模板列表响应 */
export interface TopicTemplateListResponse {
deviceType: string
templates: TopicTemplate[]
hasRealTopics: boolean
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论