diff --git a/app/api/account.py b/app/api/account.py index e118fbd..4019df7 100644 --- a/app/api/account.py +++ b/app/api/account.py @@ -9,7 +9,12 @@ from ..schemas.account import ( AccountApiResponse, AccountListResponse,AccountGetRequestYH ) from ..services.account import AccountService - +import json +from typing import List, Optional +from urllib.error import HTTPError, URLError +from urllib import request as urllib_request +from urllib.parse import urlencode +from socket import timeout router = APIRouter(prefix="/accounts", tags=["账号管理"]) @router.post("/create", response_model=AccountApiResponse, status_code=status.HTTP_201_CREATED) @@ -112,6 +117,7 @@ def update_account(request: AccountUpdateRequest, db: Session = Depends(get_db)) @router.post("/delete", response_model=AccountApiResponse) def delete_account(request: AccountDeleteRequest, db: Session = Depends(get_db)): + """删除账号""" if not AccountService.delete_account(db, request.account_id): return AccountApiResponse( @@ -124,3 +130,101 @@ def delete_account(request: AccountDeleteRequest, db: Session = Depends(get_db)) message="账号删除成功", data=None ) +# 获取今日上传的数据接口 +@router.post("/get_uplaod_data", response_model=AccountListResponse) +def get_uplaod_data(request: AccountGetRequest, db: Session = Depends(get_db)): + """根据多种条件查询账号,并合并外部接口的 is_ok 字段(仅返回 today_data 中存在的账号)""" + # 1. 从数据库查询账号列表(原有逻辑不变) + accounts = AccountService.search_accounts( + db, + account_id=request.account_id, + username=request.username, + project_name=request.project_name, + status=request.status, + today_updated=request.today_updated, + yh_id=request.yh_id, + cl_name=request.cl_name + ) + + # 2. 调用外部接口构建 user_is_ok_map(替换为 urllib 实现,修复命名冲突和超时异常) + user_is_ok_map = {} + api_url = "https://engineering.yuxindazhineng.com/index/index/get_over_data" + payload = {"user_id": "68c0dbfdb7cbcd616e7c5ab5"} + + try: + # 步骤1:将表单 payload 转为 URL 编码的字节流(urllib POST 要求数据为字节流) + payload_bytes = urlencode(payload).encode("utf-8") + + # 步骤2:构造 Request 对象(使用重命名后的 urllib_request,避免冲突) + req = urllib_request.Request( + url=api_url, + data=payload_bytes, + method="POST" # 显式指定 POST 方法 + ) + + # 步骤3:发送请求并读取响应(设置 10 秒超时,避免接口挂起) + with urllib_request.urlopen(req, timeout=10) as resp: + # 读取响应字节流并解码为 UTF-8 字符串 + response_str = resp.read().decode("utf-8") + + # 步骤4:将 JSON 字符串解析为字典 + api_response = json.loads(response_str) + + # 步骤5:验证接口返回格式并构建 user_is_ok_map 映射 + if api_response.get("code") == 0: + today_data = api_response.get("data", []) # 给默认值,避免 KeyError + for item in today_data: + # 安全获取字段并校验类型 + if 'user_name' in item and 'is_ok' in item: + user_is_ok_map[item['user_name']] = item['is_ok'] + + except HTTPError as e: + # 捕获 HTTP 状态码错误(4xx/5xx) + print(f"外部接口 HTTP 错误:{e.code} - {e.reason}") + except TimeoutError: # 修复:正确的超时异常名称(首字母大写,原 timeout 会未定义报错) + # 捕获请求超时异常 + print(f"外部接口调用超时(超过 10 秒)") + except URLError as e: + # 捕获 URL 解析错误、网络连接错误等 + print(f"外部接口网络错误:{e.reason}") + except json.JSONDecodeError: + # 捕获非合法 JSON 格式响应 + print(f"外部接口返回数据格式错误,非合法 JSON 字符串") + except Exception as e: + # 捕获其他未知异常 + print(f"外部接口处理未知异常:{str(e)}") + + # 3. 关键修改:仅保留 today_data 中存在的账号(核心过滤逻辑) + accounts_with_is_ok = [] + for account in accounts: + # 步骤1:将 AccountResponse 对象转为字典(Pydantic 模型自带 dict() 方法) + account_dict = account.dict() + + # 步骤2:获取当前账号 username,判断是否在 user_is_ok_map 中(不存在则跳过) + current_username = account_dict.get("username", "") + if current_username not in user_is_ok_map: + continue # 核心:过滤掉 today_data 中没有的账号 + + # 步骤3:给字典添加 is_ok 字段(仅处理存在的账号,无需默认值 0) + account_dict['is_ok'] = user_is_ok_map[current_username] + + # 步骤4:将处理后的字典加入新列表 + accounts_with_is_ok.append(account_dict) + + # 4. 处理空结果返回(原有逻辑不变) + if not accounts_with_is_ok: + return AccountListResponse( + code=ResponseCode.ACCOUNT_NOT_FOUND, + message=ResponseMessage.ACCOUNT_NOT_FOUND, + total=0, + data=[] + ) + + # 5. 正常返回:数据传入新列表 accounts_with_is_ok(仅包含 today_data 中的账号) + # print(accounts_with_is_ok) + return AccountListResponse( + code=ResponseCode.SUCCESS, + message="查询成功", + total=len(accounts_with_is_ok), + data=accounts_with_is_ok + ) diff --git a/app/models/account.py b/app/models/account.py index b4f828f..67fd059 100644 --- a/app/models/account.py +++ b/app/models/account.py @@ -17,9 +17,10 @@ class Account(Base): max_variation = Column(Integer, default=1, comment="变化量的绝对值,单位是毫米") yh_id = Column(String(1000), comment="宇恒一号用户id") cl_name = Column(String(100), nullable=True, comment="测量人员") - # device_name = Column(String(1000), comment="设备名称") - # device_port = Column(String(1000), comment="设备端口") - # device_ip = Column(String(1000), comment="设备局域网内ip地址") + device_name = Column(String(1000), comment="设备名称") + device_port = Column(String(1000), comment="设备端口") + device_ip = Column(String(1000), comment="设备局域网内ip地址") + is_ok = Column(Integer, default=0, comment="是否可以上传") # 模型转字典 diff --git a/app/schemas/account.py b/app/schemas/account.py index 8cd8089..79120da 100644 --- a/app/schemas/account.py +++ b/app/schemas/account.py @@ -12,6 +12,10 @@ class AccountBase(BaseModel): max_variation: Optional[int] = None yh_id: Optional[str] = None cl_name: Optional[str] = None + device_name: Optional[str] = None + device_port: Optional[str] = None + device_ip: Optional[str] = None + is_ok: Optional[int] = None class AccountCreate(AccountBase): pass @@ -49,7 +53,11 @@ class AccountResponse(AccountBase): update_time=account.update_time, max_variation=account.max_variation, yh_id=account.yh_id, - cl_name=account.cl_name + cl_name=account.cl_name, + device_name=account.device_name, + device_port=account.device_port, + device_ip=account.device_ip, + is_ok = account.is_ok ) class AccountListRequest(BaseModel): @@ -90,4 +98,5 @@ class AccountListResponse(BaseModel): code: int = 0 message: str total: int - data: List[AccountResponse] = [] \ No newline at end of file + data: List[AccountResponse] = [] +