wandaan vor 3 Monaten
Ursprung
Commit
b78e18f884

+ 2 - 0
config/config.ini

@@ -42,3 +42,5 @@ CONSOLE_OUTPUT=True
 
 
 
+[user_lists]
+USERS=['user-001']

+ 13 - 18
foundation/base/config.py

@@ -1,35 +1,30 @@
-# !/usr/bin/python
-# -*- encoding: utf-8 -*-
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
 """
-@Time    :   2025/07/10 14:40
-@Author  :   
-@File    :   config.py
-@Software:   VScode
-@Desc    :   None
+配置管理器
+Configuration Manager
 """
+
 from configparser import ConfigParser
+import os
 
 
 class ConfigHandler:
-    def __init__(self, config_file=""):
+    def __init__(self, config_file=None):
         self.config = ConfigParser()
-        self.config.read(config_file, encoding='utf-8')
-
+        if os.path.exists(config_file):
+            self.config.read(config_file, encoding='utf-8')
+    # @staticmethod
     def get(self, section, option, default=None):
         try:
-            if section == "before":
-                option = f"online_{option}" if bool(self.config.get("general", "is_online")) else f"inline_{option}"
-                value = self.config.get(section, option)
-            else:
-                value = self.config.get(section, option)
+            value = self.config.get(section, option)
             if "#" in value:
                 value = value.split('#')[0].strip()
-        except Exception as err:
+        except Exception:
             value = default
         return value
 
-    def getboolean(self, section, option):
-        return self.config.getboolean(section, option)
 
 
+# 全局配置实例
 config_handler = ConfigHandler("./config/config.ini")

+ 370 - 0
test/construction_review/test_error_codes_pytest.py

@@ -0,0 +1,370 @@
+"""
+施工方案审查API错误码测试 - pytest版本
+使用pytest运行的标准测试套件
+"""
+
+import pytest
+import requests
+import json
+import uuid
+import time
+import os
+from typing import Dict, Any
+
+# pytest fixtures
+@pytest.fixture(scope="class")
+def api_config():
+    """API配置fixture"""
+    return {
+        "base_url": "http://127.0.0.1:8034",
+        "api_prefix": "/sgsc",
+        "valid_user": "user-001",
+        "valid_project_type": "bridge_up_part",
+        "test_callback_url": "http://test.callback.com"
+    }
+
+@pytest.fixture
+def test_file():
+    """测试文件fixture - 每个测试都创建新的文件对象"""
+    file_path = "data_pipeline/test_rawdata/1f3e1d98-5b4a-4a06-87b3-c7f0413b901a.pdf"
+
+    class TestFile:
+        def __init__(self):
+            if os.path.exists(file_path):
+                self.file = open(file_path, 'rb')
+                self.file_tuple = (os.path.basename(file_path), self.file, 'application/pdf')
+                self.close_file = True
+            else:
+                self.file = None
+                self.file_tuple = ("test.pdf", b"mock pdf content", "application/pdf")
+                self.close_file = False
+
+        def get_file(self):
+            """获取文件元组"""
+            if self.close_file and self.file:
+                # 重新打开文件,确保文件未被关闭
+                self.file.seek(0)
+            return self.file_tuple
+
+        def cleanup(self):
+            """清理资源"""
+            if self.close_file and self.file:
+                self.file.close()
+
+    test_file_obj = TestFile()
+    yield test_file_obj
+    test_file_obj.cleanup()
+
+class TestFileUploadErrors:
+    """文件上传接口错误码测试"""
+
+    @pytest.mark.parametrize("test_case,expected_code", [
+        ("missing_file", "WJSC001"),
+        ("empty_file", "WJSC003"),
+        ("unsupported_format", "WJSC004"),
+        ("invalid_project_type", "WJSC006")
+    ])
+    def test_file_upload_errors(self, api_config, test_case, expected_code):
+        """测试文件上传各种错误场景"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
+
+        if test_case == "missing_file":
+            # 不上传文件
+            data = {
+                "callback_url": api_config["test_callback_url"],
+                "project_plan_type": api_config["valid_project_type"],
+                "user": api_config["valid_user"]
+            }
+            response = requests.post(url, data=data)
+
+        elif test_case == "empty_file":
+            # 上传空文件
+            files = {"file": ("empty.pdf", b"", "application/pdf")}
+            data = {
+                "callback_url": api_config["test_callback_url"],
+                "project_plan_type": api_config["valid_project_type"],
+                "user": api_config["valid_user"]
+            }
+            response = requests.post(url, files=files, data=data)
+
+        elif test_case == "unsupported_format":
+            # 上传不支持的格式
+            files = {"file": ("test.txt", b"text content", "text/plain")}
+            data = {
+                "callback_url": api_config["test_callback_url"],
+                "project_plan_type": api_config["valid_project_type"],
+                "user": api_config["valid_user"]
+            }
+            response = requests.post(url, files=files, data=data)
+
+        elif test_case == "invalid_project_type":
+            # 无效的工程方案类型
+            files = {"file": ("test.pdf", b"mock pdf content", "application/pdf")}
+            data = {
+                "callback_url": api_config["test_callback_url"],
+                "project_plan_type": "invalid_type",
+                "user": api_config["valid_user"]
+            }
+            response = requests.post(url, files=files, data=data)
+
+        # 验证错误响应
+        assert response.status_code in [400, 403, 404]  # 允许的业务错误状态码
+
+        try:
+            error_data = response.json()
+            assert error_data["code"] == expected_code
+            assert "error_type" in error_data
+            assert "message" in error_data
+        except json.JSONDecodeError:
+            pytest.fail(f"响应不是有效的JSON: {response.text}")
+
+    def test_file_upload_success(self, api_config, test_file):
+        """测试文件上传成功"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
+        files = {"file": test_file.get_file()}
+        data = {
+            "callback_url": api_config["test_callback_url"],
+            "project_plan_type": api_config["valid_project_type"],
+            "user": api_config["valid_user"]
+        }
+
+        response = requests.post(url, files=files, data=data)
+        assert response.status_code == 200
+
+        result = response.json()
+        assert "data" in result
+        assert "callback_task_id" in result["data"]
+        assert "id" in result["data"]
+
+    @pytest.mark.skip(reason="文件大小检查未实现")
+    def test_wjsc005_file_size_exceeded(self, api_config):
+        """测试WJSC005: 文件过大 - 跳过因为未实现"""
+        pass
+
+    @pytest.mark.skip(reason="认证检查未实现")
+    def test_wjsc007_unauthorized(self, api_config):
+        """测试WJSC007: 认证失败 - 跳过因为未实现"""
+        pass
+
+
+class TestTaskProgressErrors:
+    """进度查询接口错误码测试"""
+
+    def test_jdlx001_missing_parameters(self, api_config):
+        """测试JDLX001: 请求参数缺失"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/test-callback-id"
+        response = requests.get(url)  # 不提供user参数
+
+        assert response.status_code == 400
+        error_data = response.json()
+        assert error_data["code"] == "JDLX001"
+
+    @pytest.mark.parametrize("invalid_id", ["short", "123", "invalid-format"])
+    def test_jdlx002_invalid_param_format(self, api_config, invalid_id):
+        """测试JDLX002: 请求参数格式错误"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{invalid_id}"
+        params = {"user": api_config["valid_user"]}
+
+        response = requests.get(url, params=params)
+        assert response.status_code == 400
+
+        error_data = response.json()
+        assert error_data["code"] == "JDLX002"
+
+    @pytest.mark.parametrize("invalid_user", ["invalid_user", "user-999", ""])
+    def test_jdlx004_invalid_user(self, api_config, test_file, invalid_user):
+        """测试JDLX004: 用户标识无效"""
+        # 先上传文件获取有效的callback_task_id
+        callback_task_id = self._upload_file_and_get_callback(api_config, test_file)
+
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{callback_task_id}"
+        params = {"user": invalid_user}
+
+        response = requests.get(url, params=params)
+        assert response.status_code == 403
+
+        error_data = response.json()
+        assert error_data["code"] == "JDLX004"
+
+    def test_jdlx005_task_not_found(self, api_config):
+        """测试JDLX005: 任务不存在"""
+        fake_callback_id = f"{uuid.uuid4()}-{int(time.time())}"
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{fake_callback_id}"
+        params = {"user": api_config["valid_user"]}
+
+        response = requests.get(url, params=params)
+        assert response.status_code == 404
+
+        error_data = response.json()
+        assert error_data["code"] == "JDLX005"
+
+    def _upload_file_and_get_callback(self, api_config, test_file):
+        """辅助方法:上传文件并获取callback_task_id"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
+        files = {"file": test_file.get_file()}
+        data = {
+            "callback_url": api_config["test_callback_url"],
+            "project_plan_type": api_config["valid_project_type"],
+            "user": api_config["valid_user"]
+        }
+
+        response = requests.post(url, files=files, data=data)
+        assert response.status_code == 200
+        result = response.json()
+        return result["data"]["callback_task_id"]
+
+    @pytest.mark.skip(reason="认证检查未实现")
+    def test_jdlx003_unauthorized(self, api_config):
+        """测试JDLX003: 认证失败 - 跳过因为未实现"""
+        pass
+
+
+class TestReviewResultsErrors:
+    """审查结果接口错误码测试"""
+
+    @pytest.mark.parametrize("invalid_type", ["invalid", "risk", "detail", ""])
+    def test_scjg001_invalid_type(self, api_config, invalid_type):
+        """测试SCJG001: 结果类型无效"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
+        payload = {
+            "id": str(uuid.uuid4()),
+            "user": api_config["valid_user"],
+            "type": invalid_type
+        }
+
+        response = requests.post(url, json=payload)
+        assert response.status_code == 400
+
+        error_data = response.json()
+        assert error_data["code"] == "SCJG001"
+
+    @pytest.mark.parametrize("invalid_id", ["", None])
+    def test_scjg002_missing_param_id(self, api_config, invalid_id):
+        """测试SCJG002: 缺少文档ID"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
+
+        if invalid_id is None:
+            payload = {
+                "user": api_config["valid_user"],
+                "type": "summary"
+            }
+        else:
+            payload = {
+                "id": invalid_id,
+                "user": api_config["valid_user"],
+                "type": "summary"
+            }
+
+        response = requests.post(url, json=payload)
+        assert response.status_code == 400
+
+        error_data = response.json()
+        assert error_data["code"] == "SCJG002"
+
+    @pytest.mark.parametrize("invalid_format", ["123", "short-id", "invalid-uuid-format"])
+    def test_scjg003_invalid_id_format(self, api_config, invalid_format):
+        """测试SCJG003: 文档ID格式错误"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
+        payload = {
+            "id": invalid_format,
+            "user": api_config["valid_user"],
+            "type": "summary"
+        }
+
+        response = requests.post(url, json=payload)
+        assert response.status_code == 400
+
+        error_data = response.json()
+        assert error_data["code"] == "SCJG003"
+
+    def test_scjg005_invalid_user_review_results(self, api_config, test_file):
+        """测试SCJG005: 用户标识无效(审查结果接口)"""
+        # 先上传文件获取有效的文件ID
+        file_id = self._upload_file_and_get_file_id(api_config, test_file)
+
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
+        payload = {
+            "id": file_id,
+            "user": "invalid_user",
+            "type": "summary"
+        }
+
+        response = requests.post(url, json=payload)
+        assert response.status_code == 403
+
+        error_data = response.json()
+        assert error_data["code"] == "SCJG005"
+
+    def test_scjg006_task_not_found_review_results(self, api_config):
+        """测试SCJG006: 任务不存在(审查结果接口)"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
+        payload = {
+            "id": str(uuid.uuid4()),
+            "user": api_config["valid_user"],
+            "type": "summary"
+        }
+
+        response = requests.post(url, json=payload)
+        assert response.status_code == 404
+
+        error_data = response.json()
+        assert error_data["code"] == "SCJG006"
+
+    def _upload_file_and_get_file_id(self, api_config, test_file):
+        """辅助方法:上传文件并获取文件ID"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
+        files = {"file": test_file.get_file()}
+        data = {
+            "callback_url": api_config["test_callback_url"],
+            "project_plan_type": api_config["valid_project_type"],
+            "user": api_config["valid_user"]
+        }
+
+        response = requests.post(url, files=files, data=data)
+        assert response.status_code == 200
+        result = response.json()
+        return result["data"]["id"]
+
+    @pytest.mark.skip(reason="认证检查未实现")
+    def test_scjg004_unauthorized(self, api_config):
+        """测试SCJG004: 认证失败 - 跳过因为未实现"""
+        pass
+
+
+class TestIntegration:
+    """集成测试"""
+
+    def test_complete_workflow_success(self, api_config, test_file):
+        """测试完整工作流程成功场景"""
+        # 1. 文件上传
+        callback_task_id = self._upload_file_and_get_callback(api_config, test_file)
+        assert callback_task_id is not None
+
+        # 2. 进度查询
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{callback_task_id}"
+        params = {"user": api_config["valid_user"]}
+        response = requests.get(url, params=params)
+        assert response.status_code == 200
+
+    def _upload_file_and_get_callback(self, api_config, test_file):
+        """辅助方法:上传文件并获取callback_task_id"""
+        url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
+        files = {"file": test_file.get_file()}
+        data = {
+            "callback_url": api_config["test_callback_url"],
+            "project_plan_type": api_config["valid_project_type"],
+            "user": api_config["valid_user"]
+        }
+
+        response = requests.post(url, files=files, data=data)
+        assert response.status_code == 200
+        result = response.json()
+        return result["data"]["callback_task_id"]
+
+
+if __name__ == "__main__":
+    # 如果直接运行此文件,给出提示
+    print("请使用 pytest 运行此测试文件:")
+    print("pytest test/construction_review/test_error_codes_pytest.py -v")
+    print("或者运行所有测试:")
+    print("pytest test/ -v")

+ 10 - 0
test/pytest.ini

@@ -0,0 +1,10 @@
+[tool:pytest]
+testpaths = test
+python_files = test_*.py
+python_classes = Test*
+python_functions = test_*
+addopts = -v --tb=short
+markers =
+    error_codes: 标记错误码测试
+    integration: 标记集成测试
+    unit: 标记单元测试

+ 45 - 16
views/construction_review/file_upload.py

@@ -2,15 +2,16 @@
 文档上传接口实现
 模拟文件上传功能,返回文件ID和回调任务ID
 """
-
+import ast
 import uuid
 import time
 from datetime import datetime
 from fastapi import APIRouter, UploadFile, File, Form, HTTPException
 from pydantic import BaseModel
-from typing import Optional
+from typing import Optional,List
 from core.construction_review.workflows.document_ans import DocumentParse
 from foundation.logger.loggering import server_logger as logger
+from foundation.base.config import config_handler
 from .schemas.error_schemas import FileUploadErrors
 
 
@@ -25,12 +26,14 @@ class FileUploadResponse(BaseModel):
 def validate_file(file: UploadFile) -> None:
     """验证文件格式和大小"""
     # 检查文件是否存在
+
     if not file or not file.filename:
         raise FileUploadErrors.file_missing()
 
     # 检查文件大小(Mock中假设文件大小合理,实际应该读取文件内容)
     # 这里可以添加文件大小检查逻辑
-    if hasattr(file, 'size') and file.size == 0:
+    file_size = getattr(file, 'size', None)
+    if file_size is not None and file_size == 0:
         raise FileUploadErrors.file_rejected("文件为空")
 
     # 支持的文件类型
@@ -46,34 +49,58 @@ def validate_file(file: UploadFile) -> None:
 
 @file_upload_router.post("/file_upload", response_model=FileUploadResponse)
 async def file_upload(
-    file: UploadFile = File(...),
-    callback_url: str = Form(...),
-    project_plan_type: str = Form(...),
-    user: str = Form(...)
+    file: List[UploadFile] = File([]),  # 改为文件列表,支持多文件检测
+    callback_url: str = Form(None),
+    project_plan_type: str = Form(None),
+    user: str = Form(None)  # 用户参数从表单获取,不从配置获取
 ):
     """
     文件上传接口
     """
 
     # 调试日志信息
-    logger.info(f"文件上传请求 - 用户: {user}, 文件名: {file.filename if file else 'None'}",
+    logger.info(f"文件上传请求 - 用户: {user}, 文件数量: {len(file) if file else 0}",
                 log_type="upload", trace_id=f"upload-{int(time.time())}")
-    logger.info(f"文件信息 - 大小: {file.size if file else 'None'}, 类型: {file.content_type if file else 'None'}",
-                log_type="upload")
+
+    # 记录每个文件的信息
+    if file:
+        for i, f in enumerate(file):
+            file_size = getattr(f, 'size', 0)  # 安全获取文件大小,避免属性不存在错误
+            logger.info(f"文件 {i+1}: {f.filename}, 大小: {file_size}, 类型: {f.content_type}", log_type="upload")
     logger.info(f"请求参数 - 回调URL: {callback_url}, 工程类型: {project_plan_type}",
                 log_type="upload")
+    logger.info(f"用户标识: {user}")
 
     try:
-        # 验证文件
-        validate_file(file)
-
         # 验证工程方案类型
         valid_project_types = {
             'bridge_up_part',  # 桥梁上部结构
             'tunnel_construction',  # 隧道施工
             'road_repair'  # 道路维修
         }
-
+        valid_users = ast.literal_eval(config_handler.get("user_lists", "USERS"))
+        
+        # 验证文件上传
+        if not file or len(file) == 0:
+            raise FileUploadErrors.file_missing()
+        elif len(file) > 1:
+            raise FileUploadErrors.file_multiple()
+        
+        
+
+        # 验证文件格式和大小(只验证第一个文件)
+
+        if file and len(file) > 0:
+            validate_file(file[0])
+        # 验证回调地址
+        if callback_url is '':
+            raise FileUploadErrors.callback_url_missing()
+ 
+        # 验证用户标识
+        if user is None or user not in valid_users:
+            raise FileUploadErrors.invalid_user()
+        
+        # 工程方案类型校验
         if project_plan_type not in valid_project_types:
             raise FileUploadErrors.project_plan_type_invalid()
 
@@ -85,7 +112,7 @@ async def file_upload(
         # 保存文件信息
         file_info = {
             "id": file_id,
-            "name": file.filename,
+            "name": file[0].filename,
             "size": 1024 * 1024,  # 文件大小 1MB
             "created_at": created_at,
             "status": "success",
@@ -119,7 +146,7 @@ async def file_upload(
             code=200,
             data={
                 "id": file_id,
-                "name": file.filename,
+                "name": file[0].filename,
                 "size": file_info["size"],
                 "created_at": file_info["created_at"],
                 "status": "success",
@@ -127,6 +154,8 @@ async def file_upload(
             }
         )
 
+    except HTTPException:
+        raise
     except Exception as e:
         raise FileUploadErrors.internal_error(e)
 

+ 25 - 26
views/construction_review/review_results.py

@@ -9,7 +9,7 @@ from datetime import datetime
 from fastapi import APIRouter, HTTPException
 from pydantic import BaseModel
 from typing import Optional, Dict, Any
-
+from .schemas.error_schemas import ReviewResultsErrors
 review_results_router = APIRouter(prefix="/sgsc", tags=["审查结果Mock"])
 
 # 导入文件上传模块的存储
@@ -18,19 +18,16 @@ try:
 except ImportError:
     from views.construction_review.file_upload import uploaded_files
 
-# 导入错误码定义
-from .schemas.error_schemas import ReviewResultsErrors
-
 class ReviewResultsRequest(BaseModel):
-    callback_task_id: str  # 改为callback_task_id
-    user: str
-    type: str  # "summary" 或 "issues"
+    id: str = None
+    user: str = None
+    type: str = None  # "summary" 或 "issues"
 
 class ReviewResultsResponse(BaseModel):
     code: int
     data: Dict[str, Any]
 
-def generate_mock_risk_stats():
+def generate_risk_stats():
     """生成模拟风险统计"""
     return {
         "high": random.randint(1, 5),
@@ -38,7 +35,7 @@ def generate_mock_risk_stats():
         "low": random.randint(2, 6)
     }
 
-def generate_mock_dimension_scores():
+def generate_dimension_scores():
     """生成模拟四维评分"""
     return {
         "safety": random.randint(60, 95),
@@ -47,7 +44,7 @@ def generate_mock_dimension_scores():
         "cost": random.randint(65, 90)
     }
 
-def generate_mock_summary_report(risk_stats):
+def generate_summary_report(risk_stats):
     """生成模拟总结报告"""
     total_issues = sum(risk_stats.values())
     if risk_stats["high"] > 0:
@@ -57,7 +54,7 @@ def generate_mock_summary_report(risk_stats):
     else:
         return "该施工方案整体符合规范要求,存在少量细节问题,可正常施工。"
 
-def generate_mock_issues():
+def generate_issues():
     """生成模拟问题条文"""
     issues = []
 
@@ -158,7 +155,7 @@ def generate_mock_issues():
     return issues
 
 @review_results_router.post("/review_results", response_model=ReviewResultsResponse)
-async def mock_review_results(request: ReviewResultsRequest):
+async def review_results(request: ReviewResultsRequest):
     """
     Mock审查结果获取接口
     """
@@ -167,11 +164,11 @@ async def mock_review_results(request: ReviewResultsRequest):
         if not request.type or request.type not in ["summary", "issues"]:
             raise ReviewResultsErrors.invalid_type()
 
-        if not request.callback_task_id or not isinstance(request.callback_task_id, str):
+        if not request.id or not isinstance(request.id, str):
             raise ReviewResultsErrors.missing_param_id()
 
-        # 验证callback_task_id格式(应该是UUID-时间戳格式)
-        if len(request.callback_task_id) < 20 or request.callback_task_id.count('-') < 4:
+        # 验证UUID格式(简单检查
+        if len(request.id) != 36 or request.id.count('-') != 4:
             raise ReviewResultsErrors.invalid_id_format()
 
         # 验证用户标识(应该是指定用户如user-001)
@@ -179,27 +176,27 @@ async def mock_review_results(request: ReviewResultsRequest):
         if not request.user or request.user not in valid_users:
             raise ReviewResultsErrors.invalid_user()
 
-        # 检查任务是否存在
-        if request.callback_task_id not in uploaded_files:
+        # 检查文档是否存在
+        if request.id not in uploaded_files:
             raise ReviewResultsErrors.task_not_found()
 
         # 验证用户权限
-        task_info = uploaded_files[request.callback_task_id]
-        # 通过callback_task_id找到对应的文件信息
-        file_id = task_info.get("file_id")
-        file_info = uploaded_files.get(file_id, {})
+        file_info = uploaded_files[request.id]
         if file_info.get("user") != request.user:
             raise ReviewResultsErrors.invalid_user()
 
         # 检查任务状态(模拟:只有完成的任务才能查看结果)
-        if task_info and task_info.get("review_task_status") != "completed":
+        callback_task_id = file_info.get("callback_task_id")
+        if callback_task_id in uploaded_files:
+            task_info = uploaded_files[callback_task_id]
+            if task_info.get("review_task_status") != "completed":
                 raise ReviewResultsErrors.no_review_results()
 
         # 根据类型返回结果
         if request.type == "summary":
-            risk_stats = generate_mock_risk_stats()
-            dimension_scores = generate_mock_dimension_scores()
-            summary_report = generate_mock_summary_report(risk_stats)
+            risk_stats = generate_risk_stats()
+            dimension_scores = generate_dimension_scores()
+            summary_report = generate_summary_report(risk_stats)
 
             return ReviewResultsResponse(
                 code=200,
@@ -212,7 +209,7 @@ async def mock_review_results(request: ReviewResultsRequest):
             )
 
         else:  # issues
-            issues = generate_mock_issues()
+            issues = generate_issues()
 
             return ReviewResultsResponse(
                 code=200,
@@ -221,5 +218,7 @@ async def mock_review_results(request: ReviewResultsRequest):
                 }
             )
 
+    except HTTPException:
+        raise
     except Exception as e:
         raise ReviewResultsErrors.server_error(e)

+ 23 - 1
views/construction_review/schemas/error_schemas.py

@@ -62,6 +62,20 @@ class ErrorCodes:
 
     WJSC008 = {
         "code": "WJSC008",
+        "error_type": "INVALID_USER",
+        "message": "用户标识(user)无效",
+        "status_code": 403
+    }
+
+    WJSC009 = {
+        "code": "WJSC009",
+        "error_type": "CALLBACK_URL_MISS",
+        "message": "回调客户端地址缺失,请提供回调客户端地址",
+        "status_code": 403
+    }
+
+    WJSC010 = {
+        "code": "WJSC010",
         "error_type": "INTERNAL_ERROR",
         "message": "服务端内部错误",
         "status_code": 500
@@ -245,10 +259,18 @@ class FileUploadErrors:
     @staticmethod
     def unauthorized():
         return create_http_exception(ErrorCodes.WJSC007)
+    
+    @staticmethod
+    def invalid_user():
+        return create_http_exception(ErrorCodes.WJSC008)
+    
+    @staticmethod
+    def callback_url_missing():
+        return create_http_exception(ErrorCodes.WJSC009)
 
     @staticmethod
     def internal_error(original_error: Exception):
-        return create_server_error("WJSC008", original_error)
+        return create_server_error("WJSC010", original_error)
 
 
 class TaskProgressErrors:

+ 7 - 2
views/construction_review/task_progress.py

@@ -101,13 +101,16 @@ def update_task_progress(callback_task_id: str) -> dict:
 @task_progress_router.get("/task_progress/{callback_task_id}", response_model=TaskProgressResponse)
 async def task_progress(
     callback_task_id: str,
-    user: str = Query(...)
+    user: str = Query(None)
 ):
     """
     Mock任务进度轮询接口
     """
     try:
         # 验证参数
+        if user is None or not isinstance(user, str):
+            raise TaskProgressErrors.missing_parameters()
+
         if not callback_task_id or not isinstance(callback_task_id, str):
             raise TaskProgressErrors.missing_parameters()
 
@@ -117,7 +120,7 @@ async def task_progress(
 
         # 验证用户标识(应该是指定用户如user-001)
         valid_users = {"user-001", "user-002", "user-003"}  # 可以配置化
-        if not user or user not in valid_users:
+        if user == "" or user not in valid_users:
             raise TaskProgressErrors.invalid_user()
 
         # 检查任务是否存在
@@ -145,6 +148,8 @@ async def task_progress(
             }
         )
 
+    except HTTPException:
+        raise
     except Exception as e:
         raise TaskProgressErrors.server_internal_error(e)