wandaan 3 bulan lalu
induk
melakukan
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
 from configparser import ConfigParser
+import os
 
 
 
 
 class ConfigHandler:
 class ConfigHandler:
-    def __init__(self, config_file=""):
+    def __init__(self, config_file=None):
         self.config = ConfigParser()
         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):
     def get(self, section, option, default=None):
         try:
         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:
             if "#" in value:
                 value = value.split('#')[0].strip()
                 value = value.split('#')[0].strip()
-        except Exception as err:
+        except Exception:
             value = default
             value = default
         return value
         return value
 
 
-    def getboolean(self, section, option):
-        return self.config.getboolean(section, option)
 
 
 
 
+# 全局配置实例
 config_handler = ConfigHandler("./config/config.ini")
 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
 模拟文件上传功能,返回文件ID和回调任务ID
 """
 """
-
+import ast
 import uuid
 import uuid
 import time
 import time
 from datetime import datetime
 from datetime import datetime
 from fastapi import APIRouter, UploadFile, File, Form, HTTPException
 from fastapi import APIRouter, UploadFile, File, Form, HTTPException
 from pydantic import BaseModel
 from pydantic import BaseModel
-from typing import Optional
+from typing import Optional,List
 from core.construction_review.workflows.document_ans import DocumentParse
 from core.construction_review.workflows.document_ans import DocumentParse
 from foundation.logger.loggering import server_logger as logger
 from foundation.logger.loggering import server_logger as logger
+from foundation.base.config import config_handler
 from .schemas.error_schemas import FileUploadErrors
 from .schemas.error_schemas import FileUploadErrors
 
 
 
 
@@ -25,12 +26,14 @@ class FileUploadResponse(BaseModel):
 def validate_file(file: UploadFile) -> None:
 def validate_file(file: UploadFile) -> None:
     """验证文件格式和大小"""
     """验证文件格式和大小"""
     # 检查文件是否存在
     # 检查文件是否存在
+
     if not file or not file.filename:
     if not file or not file.filename:
         raise FileUploadErrors.file_missing()
         raise FileUploadErrors.file_missing()
 
 
     # 检查文件大小(Mock中假设文件大小合理,实际应该读取文件内容)
     # 检查文件大小(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("文件为空")
         raise FileUploadErrors.file_rejected("文件为空")
 
 
     # 支持的文件类型
     # 支持的文件类型
@@ -46,34 +49,58 @@ def validate_file(file: UploadFile) -> None:
 
 
 @file_upload_router.post("/file_upload", response_model=FileUploadResponse)
 @file_upload_router.post("/file_upload", response_model=FileUploadResponse)
 async def file_upload(
 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())}")
                 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}",
     logger.info(f"请求参数 - 回调URL: {callback_url}, 工程类型: {project_plan_type}",
                 log_type="upload")
                 log_type="upload")
+    logger.info(f"用户标识: {user}")
 
 
     try:
     try:
-        # 验证文件
-        validate_file(file)
-
         # 验证工程方案类型
         # 验证工程方案类型
         valid_project_types = {
         valid_project_types = {
             'bridge_up_part',  # 桥梁上部结构
             'bridge_up_part',  # 桥梁上部结构
             'tunnel_construction',  # 隧道施工
             'tunnel_construction',  # 隧道施工
             'road_repair'  # 道路维修
             '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:
         if project_plan_type not in valid_project_types:
             raise FileUploadErrors.project_plan_type_invalid()
             raise FileUploadErrors.project_plan_type_invalid()
 
 
@@ -85,7 +112,7 @@ async def file_upload(
         # 保存文件信息
         # 保存文件信息
         file_info = {
         file_info = {
             "id": file_id,
             "id": file_id,
-            "name": file.filename,
+            "name": file[0].filename,
             "size": 1024 * 1024,  # 文件大小 1MB
             "size": 1024 * 1024,  # 文件大小 1MB
             "created_at": created_at,
             "created_at": created_at,
             "status": "success",
             "status": "success",
@@ -119,7 +146,7 @@ async def file_upload(
             code=200,
             code=200,
             data={
             data={
                 "id": file_id,
                 "id": file_id,
-                "name": file.filename,
+                "name": file[0].filename,
                 "size": file_info["size"],
                 "size": file_info["size"],
                 "created_at": file_info["created_at"],
                 "created_at": file_info["created_at"],
                 "status": "success",
                 "status": "success",
@@ -127,6 +154,8 @@ async def file_upload(
             }
             }
         )
         )
 
 
+    except HTTPException:
+        raise
     except Exception as e:
     except Exception as e:
         raise FileUploadErrors.internal_error(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 fastapi import APIRouter, HTTPException
 from pydantic import BaseModel
 from pydantic import BaseModel
 from typing import Optional, Dict, Any
 from typing import Optional, Dict, Any
-
+from .schemas.error_schemas import ReviewResultsErrors
 review_results_router = APIRouter(prefix="/sgsc", tags=["审查结果Mock"])
 review_results_router = APIRouter(prefix="/sgsc", tags=["审查结果Mock"])
 
 
 # 导入文件上传模块的存储
 # 导入文件上传模块的存储
@@ -18,19 +18,16 @@ try:
 except ImportError:
 except ImportError:
     from views.construction_review.file_upload import uploaded_files
     from views.construction_review.file_upload import uploaded_files
 
 
-# 导入错误码定义
-from .schemas.error_schemas import ReviewResultsErrors
-
 class ReviewResultsRequest(BaseModel):
 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):
 class ReviewResultsResponse(BaseModel):
     code: int
     code: int
     data: Dict[str, Any]
     data: Dict[str, Any]
 
 
-def generate_mock_risk_stats():
+def generate_risk_stats():
     """生成模拟风险统计"""
     """生成模拟风险统计"""
     return {
     return {
         "high": random.randint(1, 5),
         "high": random.randint(1, 5),
@@ -38,7 +35,7 @@ def generate_mock_risk_stats():
         "low": random.randint(2, 6)
         "low": random.randint(2, 6)
     }
     }
 
 
-def generate_mock_dimension_scores():
+def generate_dimension_scores():
     """生成模拟四维评分"""
     """生成模拟四维评分"""
     return {
     return {
         "safety": random.randint(60, 95),
         "safety": random.randint(60, 95),
@@ -47,7 +44,7 @@ def generate_mock_dimension_scores():
         "cost": random.randint(65, 90)
         "cost": random.randint(65, 90)
     }
     }
 
 
-def generate_mock_summary_report(risk_stats):
+def generate_summary_report(risk_stats):
     """生成模拟总结报告"""
     """生成模拟总结报告"""
     total_issues = sum(risk_stats.values())
     total_issues = sum(risk_stats.values())
     if risk_stats["high"] > 0:
     if risk_stats["high"] > 0:
@@ -57,7 +54,7 @@ def generate_mock_summary_report(risk_stats):
     else:
     else:
         return "该施工方案整体符合规范要求,存在少量细节问题,可正常施工。"
         return "该施工方案整体符合规范要求,存在少量细节问题,可正常施工。"
 
 
-def generate_mock_issues():
+def generate_issues():
     """生成模拟问题条文"""
     """生成模拟问题条文"""
     issues = []
     issues = []
 
 
@@ -158,7 +155,7 @@ def generate_mock_issues():
     return issues
     return issues
 
 
 @review_results_router.post("/review_results", response_model=ReviewResultsResponse)
 @review_results_router.post("/review_results", response_model=ReviewResultsResponse)
-async def mock_review_results(request: ReviewResultsRequest):
+async def review_results(request: ReviewResultsRequest):
     """
     """
     Mock审查结果获取接口
     Mock审查结果获取接口
     """
     """
@@ -167,11 +164,11 @@ async def mock_review_results(request: ReviewResultsRequest):
         if not request.type or request.type not in ["summary", "issues"]:
         if not request.type or request.type not in ["summary", "issues"]:
             raise ReviewResultsErrors.invalid_type()
             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()
             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()
             raise ReviewResultsErrors.invalid_id_format()
 
 
         # 验证用户标识(应该是指定用户如user-001)
         # 验证用户标识(应该是指定用户如user-001)
@@ -179,27 +176,27 @@ async def mock_review_results(request: ReviewResultsRequest):
         if not request.user or request.user not in valid_users:
         if not request.user or request.user not in valid_users:
             raise ReviewResultsErrors.invalid_user()
             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()
             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:
         if file_info.get("user") != request.user:
             raise ReviewResultsErrors.invalid_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()
                 raise ReviewResultsErrors.no_review_results()
 
 
         # 根据类型返回结果
         # 根据类型返回结果
         if request.type == "summary":
         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(
             return ReviewResultsResponse(
                 code=200,
                 code=200,
@@ -212,7 +209,7 @@ async def mock_review_results(request: ReviewResultsRequest):
             )
             )
 
 
         else:  # issues
         else:  # issues
-            issues = generate_mock_issues()
+            issues = generate_issues()
 
 
             return ReviewResultsResponse(
             return ReviewResultsResponse(
                 code=200,
                 code=200,
@@ -221,5 +218,7 @@ async def mock_review_results(request: ReviewResultsRequest):
                 }
                 }
             )
             )
 
 
+    except HTTPException:
+        raise
     except Exception as e:
     except Exception as e:
         raise ReviewResultsErrors.server_error(e)
         raise ReviewResultsErrors.server_error(e)

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

@@ -62,6 +62,20 @@ class ErrorCodes:
 
 
     WJSC008 = {
     WJSC008 = {
         "code": "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",
         "error_type": "INTERNAL_ERROR",
         "message": "服务端内部错误",
         "message": "服务端内部错误",
         "status_code": 500
         "status_code": 500
@@ -245,10 +259,18 @@ class FileUploadErrors:
     @staticmethod
     @staticmethod
     def unauthorized():
     def unauthorized():
         return create_http_exception(ErrorCodes.WJSC007)
         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
     @staticmethod
     def internal_error(original_error: Exception):
     def internal_error(original_error: Exception):
-        return create_server_error("WJSC008", original_error)
+        return create_server_error("WJSC010", original_error)
 
 
 
 
 class TaskProgressErrors:
 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)
 @task_progress_router.get("/task_progress/{callback_task_id}", response_model=TaskProgressResponse)
 async def task_progress(
 async def task_progress(
     callback_task_id: str,
     callback_task_id: str,
-    user: str = Query(...)
+    user: str = Query(None)
 ):
 ):
     """
     """
     Mock任务进度轮询接口
     Mock任务进度轮询接口
     """
     """
     try:
     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):
         if not callback_task_id or not isinstance(callback_task_id, str):
             raise TaskProgressErrors.missing_parameters()
             raise TaskProgressErrors.missing_parameters()
 
 
@@ -117,7 +120,7 @@ async def task_progress(
 
 
         # 验证用户标识(应该是指定用户如user-001)
         # 验证用户标识(应该是指定用户如user-001)
         valid_users = {"user-001", "user-002", "user-003"}  # 可以配置化
         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()
             raise TaskProgressErrors.invalid_user()
 
 
         # 检查任务是否存在
         # 检查任务是否存在
@@ -145,6 +148,8 @@ async def task_progress(
             }
             }
         )
         )
 
 
+    except HTTPException:
+        raise
     except Exception as e:
     except Exception as e:
         raise TaskProgressErrors.server_internal_error(e)
         raise TaskProgressErrors.server_internal_error(e)