Browse Source

基础流程框架

wandaan 2 months ago
parent
commit
9ed6578b53
58 changed files with 1347 additions and 65 deletions
  1. 3 0
      .gitignore
  2. 8 8
      README.md
  3. 0 0
      core/base/__init__.py
  4. 0 0
      core/construction_review/__init__.py
  5. 0 0
      core/construction_review/workflows/__init__.py
  6. 6 0
      core/construction_review/workflows/ai_review.py
  7. 32 0
      core/construction_review/workflows/document_ans.py
  8. 0 0
      core/construction_review/workflows/task_progress.py
  9. 0 0
      data_pipeline/base/__init__.py
  10. 0 0
      data_pipeline/document/parsers/__init__.py
  11. 0 0
      data_pipeline/document/processors/__init__.py
  12. BIN
      data_pipeline/test_rawdata/1f3e1d98-5b4a-4a06-87b3-c7f0413b901a.pdf
  13. 0 0
      database/base/__init__.py
  14. 0 0
      database/migrations/__init__.py
  15. 0 0
      database/models/__init__.py
  16. 0 0
      database/repositories/__init__.py
  17. 0 0
      foundation/__init__.py
  18. 0 0
      foundation/agent/__init__.py
  19. 2 2
      foundation/agent/base_agent.py
  20. 0 0
      foundation/agent/function/test_funciton.py
  21. 0 0
      foundation/agent/generate/__init__.py
  22. 2 2
      foundation/agent/generate/model_generate.py
  23. 4 4
      foundation/agent/generate/test_intent.py
  24. 7 7
      foundation/agent/test_agent.py
  25. 0 0
      foundation/agent/workflow/test_cus_state.py
  26. 5 5
      foundation/agent/workflow/test_workflow_graph.py
  27. 7 7
      foundation/agent/workflow/test_workflow_node.py
  28. 1 1
      foundation/base/async_redis_lock.py
  29. 0 0
      foundation/base/config.py
  30. 1 1
      foundation/base/redis_config.py
  31. 3 3
      foundation/base/redis_connection.py
  32. 0 0
      foundation/base/redis_lock.py
  33. 0 0
      foundation/core_enums.py
  34. 1 1
      foundation/logger/loggering.py
  35. 0 0
      foundation/rag/__init__.py
  36. 0 0
      foundation/schemas/__init__.py
  37. 0 0
      foundation/schemas/test_schemas.py
  38. 0 0
      foundation/services/base/__init__.py
  39. 0 0
      foundation/services/external/__init__.py
  40. 0 0
      foundation/services/integration/__init__.py
  41. 0 0
      foundation/services/model/__init__.py
  42. 0 0
      foundation/services/monitoring/__init__.py
  43. 0 0
      foundation/utils/common.py
  44. 3 3
      foundation/utils/redis_utils.py
  45. 4 4
      foundation/utils/tool_utils.py
  46. 2 2
      foundation/utils/utils.py
  47. 8 6
      foundation/utils/yaml_utils.py
  48. 2 2
      server/app.py
  49. 4 0
      test/construction_review/__init__.py
  50. 281 0
      test/construction_review/api_test_client.py
  51. 2 0
      test_document.pdf
  52. 15 0
      views/construction_review/__init__.py
  53. 107 0
      views/construction_review/app.py
  54. 133 0
      views/construction_review/file_upload.py
  55. 225 0
      views/construction_review/review_results.py
  56. 315 0
      views/construction_review/schemas/error_schemas.py
  57. 157 0
      views/construction_review/task_progress.py
  58. 7 7
      views/test_views.py

+ 3 - 0
.gitignore

@@ -58,3 +58,6 @@ docs/_build/
 # PyBuilder
 target/
 
+todo.md
+.design
+.claude

+ 8 - 8
README.md

@@ -51,14 +51,14 @@
         }
 
     - stream
-      http://localhost:8001/test/agent/stream
-        {
-          "config": {
-              "session_id":"111"
-          },
-          "input": "你好"
-        }
-
+curl -X POST "http://localhost:8001/test/agent/stream" \
+-H "Content-Type: application/json" \
+-d '{
+    "config": {
+        "session_id": "111"
+    },
+    "input": "你好"
+}'
 
   #### Workflow-Graph stream
     - chat

+ 0 - 0
core/base/__init__.py


+ 0 - 0
core/construction_review/__init__.py


+ 0 - 0
core/construction_review/workflows/__init__.py


+ 6 - 0
core/construction_review/workflows/ai_review.py

@@ -0,0 +1,6 @@
+
+
+from pydantic import BaseModel
+
+class ReviewWorkflow(BaseModel):
+    

+ 32 - 0
core/construction_review/workflows/document_ans.py

@@ -0,0 +1,32 @@
+
+
+# 文档解析流程
+from langchain_community.document_loaders import PyPDFLoader  # 加载PDF文件
+from langchain.text_splitter import RecursiveCharacterTextSplitter  # 文本分块
+from foundation.logger.loggering import server_logger
+
+logger = server_logger
+
+
+class DocumentParse:
+
+    """
+    文档解析
+    """
+
+    @staticmethod
+    def document_parse(file_path):
+        # 1. 加载PDF
+        loader = PyPDFLoader(file_path)
+        documents = loader.load()
+        
+        # 2. 文本分块
+        text_splitter = RecursiveCharacterTextSplitter(
+            chunk_size=1000,  # 块大小
+            chunk_overlap=20,  # 块重叠
+            separators=["\n\n", "\n", " ", ""]  # 分块分隔符
+        )
+        splits = text_splitter.split_documents(documents)  # 得到分块后的文档
+        logger.info(f"加载的: {len(splits)}条审查条款")
+        return splits
+

+ 0 - 0
core/construction_review/workflows/task_progress.py


+ 0 - 0
data_pipeline/base/__init__.py


+ 0 - 0
data_pipeline/document/parsers/__init__.py


+ 0 - 0
data_pipeline/document/processors/__init__.py


BIN
data_pipeline/test_rawdata/1f3e1d98-5b4a-4a06-87b3-c7f0413b901a.pdf


+ 0 - 0
database/base/__init__.py


+ 0 - 0
database/migrations/__init__.py


+ 0 - 0
database/models/__init__.py


+ 0 - 0
database/repositories/__init__.py


+ 0 - 0
foundation/__init__.py


+ 0 - 0
agent/__init__.py → foundation/agent/__init__.py


+ 2 - 2
agent/base_agent.py → foundation/agent/base_agent.py

@@ -12,8 +12,8 @@ from datetime import datetime
 from io import StringIO
 from contextlib import redirect_stdout
 from typing import Dict, List, Optional
-from logger.loggering import server_logger
-from utils.redis_utils import get_redis_result_cache_data_and_delete_key
+from foundation.logger.loggering import server_logger
+from foundation.utils.redis_utils import get_redis_result_cache_data_and_delete_key
 
 class BaseAgent:
     """

+ 0 - 0
agent/function/test_funciton.py → foundation/agent/function/test_funciton.py


+ 0 - 0
agent/generate/__init__.py → foundation/agent/generate/__init__.py


+ 2 - 2
agent/generate/model_generate.py → foundation/agent/generate/model_generate.py

@@ -11,8 +11,8 @@
 from typing import Dict, Optional
 from langchain_core.prompts import HumanMessagePromptTemplate
 from langchain_core.prompts import ChatPromptTemplate
-from utils.utils import get_models
-from utils.yaml_utils import system_prompt_config
+from foundation.utils.utils import get_models
+from foundation.utils.yaml_utils import system_prompt_config
 
 
 class TestGenerateModelClient:

+ 4 - 4
agent/generate/test_intent.py → foundation/agent/generate/test_intent.py

@@ -13,14 +13,14 @@ import os
 import sys
 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
 
-from logger.loggering import server_logger
-from utils.utils import get_models
+from foundation.logger.loggering import server_logger
+from foundation.utils.utils import get_models
 from langchain_core.prompts import SystemMessagePromptTemplate
 from langchain_core.prompts import HumanMessagePromptTemplate
 from langchain_core.prompts import ChatPromptTemplate
 from langchain_core.prompts import FewShotChatMessagePromptTemplate
-from utils import yaml_utils
-from base.config import config_handler
+from foundation.utils import yaml_utils
+from foundation.base.config import config_handler
 
 
 class TestIntentIdentifyClient:

+ 7 - 7
agent/test_agent.py → foundation/agent/test_agent.py

@@ -11,10 +11,10 @@ import json
 
 from langgraph.prebuilt import create_react_agent
 from sqlalchemy.sql.functions import user
-from logger.loggering import server_logger
-from utils.common import handler_err
-from utils.utils import get_models
-from utils.yaml_utils import system_prompt_config
+from foundation.logger.loggering import server_logger
+from foundation.utils.common import handler_err
+from foundation.utils.utils import get_models
+from foundation.utils.yaml_utils import system_prompt_config
 
 import threading
 import time
@@ -22,9 +22,9 @@ from typing import Dict, List, Optional, AsyncGenerator, Any, OrderedDict
 from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
 from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
 from langchain_core.runnables import RunnableConfig
-from agent.base_agent import BaseAgent
-from schemas.test_schemas import FormConfig
-from agent.function.test_funciton import test_funtion
+from foundation.agent.base_agent import BaseAgent
+from foundation.schemas.test_schemas import FormConfig
+from foundation.agent.function.test_funciton import test_funtion
 
 
 class TestAgentClient(BaseAgent):

+ 0 - 0
agent/workflow/test_cus_state.py → foundation/agent/workflow/test_cus_state.py


+ 5 - 5
agent/workflow/test_workflow_graph.py → foundation/agent/workflow/test_workflow_graph.py

@@ -9,17 +9,17 @@
 @Date       :2025/08/10 18:00
 '''
 
-from agent.workflow.test_cus_state import TestCusState
-from agent.workflow.test_workflow_node import TestWorkflowNode
+from foundation.agent.workflow.test_cus_state import TestCusState
+from foundation.agent.workflow.test_workflow_node import TestWorkflowNode
 from langgraph.graph import START, StateGraph, END
 from langgraph.checkpoint.memory import MemorySaver
-from logger.loggering import server_logger
+from foundation.logger.loggering import server_logger
 from typing import AsyncGenerator
 import time
 from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
-from utils.common import return_json, handler_err
+from foundation.utils.common import return_json, handler_err
 import json
-from schemas.test_schemas import TestForm, FormConfig
+from foundation.schemas.test_schemas import TestForm, FormConfig
 
 
 class TestWorkflowGraph:

+ 7 - 7
agent/workflow/test_workflow_node.py → foundation/agent/workflow/test_workflow_node.py

@@ -13,15 +13,15 @@
 
 import json
 import sys
-from logger.loggering import server_logger
-from utils.common import handler_err
+from foundation.logger.loggering import server_logger
+from foundation.utils.common import handler_err
 from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
 from langchain_core.prompts import ChatPromptTemplate
-from agent.workflow.test_cus_state import TestCusState
-from agent.generate.test_intent import intent_identify_client
-from agent.test_agent import test_agent_client
-from schemas.test_schemas import FormConfig
-from agent.generate.model_generate import test_generate_model_client
+from foundation.agent.workflow.test_cus_state import TestCusState
+from foundation.agent.generate.test_intent import intent_identify_client
+from foundation.agent.test_agent import test_agent_client
+from foundation.schemas.test_schemas import FormConfig
+from foundation.agent.generate.model_generate import test_generate_model_client
 
 
 

+ 1 - 1
base/async_redis_lock.py → foundation/base/async_redis_lock.py

@@ -2,7 +2,7 @@ import asyncio
 import time
 import uuid
 from typing import Optional
-from logger.loggering import server_logger
+from foundation.logger.loggering import server_logger
 
 class AsyncRedisLock:
     def __init__(self, redis_client, lock_name: str, expire_time: int = 30):

+ 0 - 0
base/config.py → foundation/base/config.py


+ 1 - 1
base/redis_config.py → foundation/base/redis_config.py

@@ -9,7 +9,7 @@
 '''
 
 from dataclasses import dataclass
-from base.config import config_handler
+from foundation.base.config import config_handler
 
 
 @dataclass

+ 3 - 3
base/redis_connection.py → foundation/base/redis_connection.py

@@ -12,9 +12,9 @@ from redis import asyncio as aioredis
 
 
 from typing import Optional, Protocol, Dict, Any
-from base.redis_config import RedisConfig
-from base.redis_config import load_config_from_env
-from logger.loggering import server_logger
+from foundation.base.redis_config import RedisConfig
+from foundation.base.redis_config import load_config_from_env
+from foundation.logger.loggering import server_logger
 from typing import Dict, Any, List
 from typing import Tuple, Optional
 from langchain_community.storage import RedisStore

+ 0 - 0
base/redis_lock.py → foundation/base/redis_lock.py


+ 0 - 0
core/common_enums.py → foundation/core_enums.py


+ 1 - 1
logger/loggering.py → foundation/logger/loggering.py

@@ -7,7 +7,7 @@
 @Author     :
 @Date       :2025/7/11 10:48
 '''
-from base.config import config_handler
+from foundation.base.config import config_handler
 
 
 import os

+ 0 - 0
foundation/rag/__init__.py


+ 0 - 0
schemas/__init__.py → foundation/schemas/__init__.py


+ 0 - 0
schemas/test_schemas.py → foundation/schemas/test_schemas.py


+ 0 - 0
foundation/services/base/__init__.py


+ 0 - 0
foundation/services/external/__init__.py


+ 0 - 0
foundation/services/integration/__init__.py


+ 0 - 0
foundation/services/model/__init__.py


+ 0 - 0
foundation/services/monitoring/__init__.py


+ 0 - 0
utils/common.py → foundation/utils/common.py


+ 3 - 3
utils/redis_utils.py → foundation/utils/redis_utils.py

@@ -1,8 +1,8 @@
 
 import json
-from logger.loggering import server_logger
-from base.redis_connection import RedisConnectionFactory
-from base.config import config_handler
+from foundation.logger.loggering import server_logger
+from foundation.base.redis_connection import RedisConnectionFactory
+from foundation.base.config import config_handler
 # 缓存数据有效期 默认 3 分钟
 CACHE_DATA_EXPIRED_TIME = 3 * 60
 

+ 4 - 4
utils/tool_utils.py → foundation/utils/tool_utils.py

@@ -2,12 +2,12 @@ import time
 from math import log
 import os
 from dotenv import load_dotenv
-from core.common_enums import ErrorCodeEnum
+from foundation.core_enums import ErrorCodeEnum
 from functools import wraps
 
-from logger.loggering import server_logger
-from utils.common import handler_err
-from base.config import config_handler
+from foundation.logger.loggering import server_logger
+from foundation.utils.common import handler_err
+from foundation.base.config import config_handler
 
 # 获取当前文件的目录
 current_dir = os.path.dirname(__file__)

+ 2 - 2
utils/utils.py → foundation/utils/utils.py

@@ -7,8 +7,8 @@ from typing import List, Dict, Optional
 from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
 from langchain_openai import ChatOpenAI
 
-from base.config import config_handler
-from logger.loggering import server_logger
+from foundation.base.config import config_handler
+from foundation.logger.loggering import server_logger
 
 
 def get_models():

+ 8 - 6
utils/yaml_utils.py → foundation/utils/yaml_utils.py

@@ -11,20 +11,22 @@
 
 import os
 import yaml
-from logger.loggering import server_logger
+from foundation.logger.loggering import server_logger
 
 import os
 from dotenv import load_dotenv
 from functools import wraps
 
-from logger.loggering import server_logger
-from utils.common import handler_err
-from base.config import config_handler
+from foundation.logger.loggering import server_logger
+from foundation.utils.common import handler_err
+from foundation.base.config import config_handler
 
 # 获取当前文件的目录
 current_dir = os.path.dirname(__file__)
+# 获取项目根目录
+project_root = os.path.dirname(os.path.dirname(current_dir))
 # 构建到 .env 的相对路径
-conf_file_path = os.path.join(current_dir , '../',  '.env')
+conf_file_path = os.path.join(project_root, '.env')
 #server_logger.info(f"当前目录: {conf_file_path}")
 
 
@@ -58,7 +60,7 @@ def get_yaml_file_path(file_name: str) -> str:
         :param file_name:
         :return:
     """
-    yaml_file = os.path.join(current_dir , '../', 'config', 'prompt' , file_name)
+    yaml_file = os.path.join(project_root, 'config', 'prompt' , file_name)
     if not os.path.exists(yaml_file):
         raise FileNotFoundError(f"Prompt文件不存在: {file_name}")
     return yaml_file

+ 2 - 2
server/app.py

@@ -11,7 +11,7 @@ from fastapi.middleware.cors import CORSMiddleware
 
 from fastapi import FastAPI
 
-from logger.loggering import server_logger
+from foundation.logger.loggering import server_logger
 from views.test_views import test_router
 
 
@@ -43,4 +43,4 @@ server_logger.info(msg="APP init successfully")
 # 运行Uvicorn服务器
 if __name__ == "__main__":
     import uvicorn
-    uvicorn.run(app, host="0.0.0.0", port=8001)
+    uvicorn.run(app, host="0.0.0.0", port=8001,reload=True)

+ 4 - 0
test/construction_review/__init__.py

@@ -0,0 +1,4 @@
+"""
+施工方案审查API测试模块
+包含接口测试和前端联调测试
+"""

+ 281 - 0
test/construction_review/api_test_client.py

@@ -0,0 +1,281 @@
+"""
+施工方案审查API测试客户端
+用于测试Mock接口和前端联调
+"""
+
+import requests
+import json
+import time
+import uuid
+from pathlib import Path
+from typing import Optional, Dict, Any
+
+class ConstructionReviewAPIClient:
+    """施工方案审查API客户端"""
+
+    def __init__(self, base_url: str = "http://127.0.0.1:8034", api_key: Optional[str] = None):
+        self.base_url = base_url.rstrip('/')
+        self.api_key = api_key
+        self.session = requests.Session()
+
+        if api_key:
+            self.session.headers.update({
+                'Authorization': f'Bearer {api_key}'
+            })
+
+    def upload_file(self, file_path: str, project_plan_type: str, user: str,
+                   callback_url: Optional[str] = None) -> Dict[str, Any]:
+        """
+        上传文件
+
+        Args:
+            file_path: 文件路径
+            project_plan_type: 工程方案类型
+            user: 用户标识
+            callback_url: 回调URL(可选)
+
+        Returns:
+            上传响应结果
+        """
+        url = f"{self.base_url}/sgsc/file_upload"
+
+        if not Path(file_path).exists():
+            raise FileNotFoundError(f"文件不存在: {file_path}")
+
+        with open(file_path, 'rb') as f:
+            files = {'file': f}
+            data = {
+                'project_plan_type': project_plan_type,
+                'user': user
+            }
+
+            if callback_url:
+                data['callback_url'] = callback_url
+
+            response = self.session.post(url, files=files, data=data)
+            response.raise_for_status()
+            return response.json()
+
+    def get_task_progress(self, callback_task_id: str, user: str) -> Dict[str, Any]:
+        """
+        查询任务进度
+
+        Args:
+            callback_task_id: 任务ID
+            user: 用户标识
+
+        Returns:
+            进度查询结果
+        """
+        url = f"{self.base_url}/sgsc/task_progress/{callback_task_id}"
+        params = {'user': user}
+
+        response = self.session.get(url, params=params)
+        response.raise_for_status()
+        return response.json()
+
+    def get_review_results(self, file_id: str, user: str, result_type: str) -> Dict[str, Any]:
+        """
+        获取审查结果
+
+        Args:
+            file_id: 文件ID
+            user: 用户标识
+            result_type: 结果类型 ("summary" 或 "issues")
+
+        Returns:
+            审查结果
+        """
+        url = f"{self.base_url}/sgsc/review_results"
+        data = {
+            'id': file_id,
+            'user': user,
+            'type': result_type
+        }
+
+        response = self.session.post(url, json=data)
+        response.raise_for_status()
+        return response.json()
+
+    def wait_for_completion(self, callback_task_id: str, user: str,
+                          timeout: int = 1800, interval: int = 10) -> Dict[str, Any]:
+        """
+        等待任务完成
+
+        Args:
+            callback_task_id: 任务ID
+            user: 用户标识
+            timeout: 超时时间(秒)
+            interval: 轮询间隔(秒)
+
+        Returns:
+            最终任务状态
+        """
+        start_time = time.time()
+
+        while time.time() - start_time < timeout:
+            try:
+                result = self.get_task_progress(callback_task_id, user)
+
+                if result['data']['review_task_status'] == 'completed':
+                    print(f"任务完成! 总进度: {result['data']['overall_progress']}%")
+                    return result
+                else:
+                    progress = result['data']['overall_progress']
+                    print(f"任务进行中... 进度: {progress}%")
+                    time.sleep(interval)
+
+            except Exception as e:
+                print(f"查询进度失败: {e}")
+                time.sleep(interval)
+
+        raise TimeoutError(f"任务超时,等待时间超过 {timeout} 秒")
+
+class MockTestRunner:
+    """Mock测试运行器"""
+
+    def __init__(self, client: ConstructionReviewAPIClient):
+        self.client = client
+
+    def test_file_upload(self, file_path: str = None) -> Dict[str, Any]:
+        """测试文件上传"""
+        print("=== 测试文件上传 ===")
+
+        # 创建测试文件(如果没有提供文件路径)
+        if not file_path:
+            test_file = Path(r"D:\wx_work\sichuan_luqiao\LQAgentPlatform\data_pipeline\test_rawdata\1f3e1d98-5b4a-4a06-87b3-c7f0413b901a.pdf")
+            if not test_file.exists():
+                # 创建一个简单的测试PDF文件内容
+                test_file.write_bytes(b"%PDF-1.4\n%Mock PDF for testing\n")
+            file_path = str(test_file)
+
+        try:
+            result = self.client.upload_file(
+                file_path=file_path,
+                project_plan_type="bridge_up_part",
+                user=str(uuid.uuid4()),
+                callback_url="https://client.example.com/callback"
+            )
+
+            print(f"✅ 文件上传成功")
+            print(f"文件ID: {result['data']['id']}")
+            print(f"任务ID: {result['data']['callback_task_id']}")
+
+            return result
+
+        except Exception as e:
+            print(f"❌ 文件上传失败: {e}")
+            raise
+
+    def test_progress_query(self, callback_task_id: str, user: str) -> None:
+        """测试进度查询"""
+        print("\n=== 测试进度查询 ===")
+
+        try:
+            result = self.client.get_task_progress(callback_task_id, user)
+
+            print(f"✅ 进度查询成功")
+            print(f"任务状态: {result['data']['review_task_status']}")
+            print(f"总进度: {result['data']['overall_progress']}%")
+
+            for stage in result['data']['stages']:
+                print(f"  - {stage['stage_name']}: {stage['progress']}% ({stage['stage_status']})")
+
+        except Exception as e:
+            print(f"❌ 进度查询失败: {e}")
+            raise
+
+    def test_review_results(self, file_id: str, user: str) -> None:
+        """测试审查结果获取"""
+        print("\n=== 测试审查结果获取 ===")
+
+        # 测试获取总结报告
+        try:
+            result = self.client.get_review_results(file_id, user, "summary")
+
+            print(f"✅ 总结报告获取成功")
+            print(f"风险统计: {result['data']['risk_stats']}")
+            print(f"四维评分: {result['data']['dimension_scores']}")
+            print(f"总结报告: {result['data']['summary_report']}")
+
+        except Exception as e:
+            print(f"❌ 总结报告获取失败: {e}")
+
+        # 测试获取问题条文
+        try:
+            result = self.client.get_review_results(file_id, user, "issues")
+
+            print(f"\n✅ 问题条文获取成功")
+            issues = result['data']['issues']
+            print(f"发现问题数量: {len(issues)}")
+
+            for i, issue in enumerate(issues):
+                print(f"\n问题 {i+1}:")
+                print(f"  ID: {issue['issue_id']}")
+                print(f"  页码: {issue['metadata']['page']}")
+                print(f"  章节: {issue['metadata']['chapter']}")
+                print(f"  风险等级: {issue['risk_summary']['max_risk_level']}")
+                print(f"  检查项数量: {len(issue['review_lists'])}")
+
+        except Exception as e:
+            print(f"❌ 问题条文获取失败: {e}")
+
+    def run_complete_test(self) -> None:
+        """运行完整测试流程"""
+        print("开始施工方案审查API完整测试...")
+
+        try:
+            # 1. 上传文件
+            upload_result = self.test_file_upload()
+            file_id = upload_result['data']['id']
+            callback_task_id = upload_result['data']['callback_task_id']
+            user = str(uuid.uuid4())  # 实际应该从上传响应中获取,这里简化
+
+            # 2. 查询进度(等待一段时间让任务完成)
+            print("\n等待任务完成...")
+            time.sleep(2)  # 短暂等待
+
+            # 先测试进度查询
+            self.test_progress_query(callback_task_id, user)
+
+            # 3. 获取审查结果(可能需要等待任务完成)
+            print("\n获取审查结果...")
+
+            # 如果任务还未完成,直接标记完成(仅用于Mock测试)
+            try:
+                self.test_review_results(file_id, user)
+            except Exception as e:
+                print(f"审查结果获取失败,尝试完成任务: {e}")
+
+                # 完成任务(Mock功能)
+                response = requests.post(f"{self.client.base_url}/sgsc/mock/complete_task",
+                                       data={"callback_task_id": callback_task_id})
+                print("任务已强制完成,重新获取结果...")
+
+                self.test_review_results(file_id, user)
+
+            print("\n🎉 完整测试流程执行成功!")
+
+        except Exception as e:
+            print(f"\n❌ 测试失败: {e}")
+            raise
+
+def main():
+    """主函数 - 运行测试"""
+    print("施工方案审查API Mock测试客户端")
+    print("=" * 50)
+
+    # 创建客户端
+    client = ConstructionReviewAPIClient(
+        base_url="http://127.0.0.1:8034",
+        api_key="mock-api-key-12345"
+    )
+
+    # 创建测试运行器
+    test_runner = MockTestRunner(client)
+
+    # 运行完整测试
+    test_runner.run_complete_test()
+
+if __name__ == "__main__":
+    main()

+ 2 - 0
test_document.pdf

@@ -0,0 +1,2 @@
+%PDF-1.4
+%Mock PDF for testing

+ 15 - 0
views/construction_review/__init__.py

@@ -0,0 +1,15 @@
+"""
+Mock服务模块 - 施工方案审查API模拟实现
+用于前端开发和接口联调测试
+"""
+
+# 注释掉自动导入,避免模块依赖问题
+# from .file_upload import file_upload_router
+# from .task_progress import task_progress_router
+# from .review_results import review_results_router
+
+__all__ = [
+    'file_upload_router',
+    'task_progress_router',
+    'review_results_router'
+]

+ 107 - 0
views/construction_review/app.py

@@ -0,0 +1,107 @@
+"""
+服务主应用
+整合所有接口,提供统一的测试服务
+"""
+
+import datetime
+import sys
+import os
+
+# 添加项目根目录到Python路径
+current_dir = os.path.dirname(os.path.abspath(__file__))
+project_root = os.path.dirname(os.path.dirname(current_dir))
+sys.path.insert(0, project_root)
+
+from fastapi import FastAPI, HTTPException
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import JSONResponse
+import uvicorn
+
+# 现在可以正常导入了
+from views.construction_review.file_upload import file_upload_router
+from views.construction_review.task_progress import task_progress_router
+from views.construction_review.review_results import review_results_router
+
+def create_app() -> FastAPI:
+    """创建接口服务"""
+    app = FastAPI(
+        title="施工方案审查API服务",
+        description="用于前端开发和接口联调服务",
+        version="0.0.1"
+    )
+
+    # 添加CORS中间件(允许前端访问)
+    app.add_middleware(
+        CORSMiddleware,
+        allow_origins=["*"],  # 在生产环境中应该设置具体的域名
+        allow_credentials=True,
+        allow_methods=["*"],
+        allow_headers=["*"],
+    )
+
+    # 添加路由
+    app.include_router(file_upload_router)
+    app.include_router(task_progress_router)
+    app.include_router(review_results_router)
+
+    # 全局异常处理
+    @app.exception_handler(HTTPException)
+    async def http_exception_handler(request, exc):
+        return JSONResponse(
+            status_code=exc.status_code,
+            content=exc.detail
+        )
+
+    # 健康检查
+    @app.get("/health")
+    async def health_check():
+        timestamp = datetime.datetime.now().isoformat()
+        return {"status": "healthy", "timestamp": timestamp}
+
+    # API文档
+    @app.get("/api/docs")
+    async def api_docs():
+        return {
+            "title": "施工方案审查服务API文档",
+            "description": "API接口文档",
+            "version": "V.0.1",
+            "apis": [
+                {
+                    "name": "文档上传",
+                    "path": "/sgsc/file_upload",
+                    "method": "POST",
+                    "description": "上传施工方案文档"
+                },
+                {
+                    "name": "进度查询",
+                    "path": "/sgsc/task_progress/{callback_task_id}",
+                    "method": "GET",
+                    "description": "查询审查任务进度"
+                },
+                {
+                    "name": "结果获取",
+                    "path": "/sgsc/review_results",
+                    "method": "POST",
+                    "description": "获取审查结果"
+                }
+            ],
+
+            
+        }
+
+    return app
+app = create_app()
+
+def run_server(host: str = "127.0.0.1", port: int = 8034, reload: bool = True):
+    """运行服务器"""
+    if reload:
+        # 重载模式需要正确的模块路径
+        app_import_path = "views.construction_review.app:app"
+        uvicorn.run(app_import_path, host=host, port=port, reload=reload)
+    else:
+        # 直接运行模式,直接使用app对象
+        uvicorn.run(app, host=host, port=port)
+
+
+if __name__ == "__main__":
+    run_server(reload=True)  # 直接运行时关闭重载

+ 133 - 0
views/construction_review/file_upload.py

@@ -0,0 +1,133 @@
+"""
+文档上传接口实现
+模拟文件上传功能,返回文件ID和回调任务ID
+"""
+
+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 core.construction_review.workflows.document_ans import DocumentParse
+from foundation.logger.loggering import server_logger as logger
+from .schemas.error_schemas import FileUploadErrors
+
+
+
+file_upload_router = APIRouter(prefix="/sgsc", tags=["文档上传"])
+uploaded_files = {}
+
+class FileUploadResponse(BaseModel):
+    code: int
+    data: dict
+
+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:
+        raise FileUploadErrors.file_rejected("文件为空")
+
+    # 支持的文件类型
+    allowed_mime_types = {
+        'application/pdf',
+        'application/msword',
+        'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
+    }
+
+    # 检查文件格式
+    if file.content_type not in allowed_mime_types:
+        raise FileUploadErrors.file_format_unsupported()
+
+@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(...)
+):
+    """
+    文件上传接口
+    """
+
+    # 调试日志信息
+    logger.info(f"文件上传请求 - 用户: {user}, 文件名: {file.filename if file else 'None'}",
+                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")
+    logger.info(f"请求参数 - 回调URL: {callback_url}, 工程类型: {project_plan_type}",
+                log_type="upload")
+
+    try:
+        # 验证文件
+        validate_file(file)
+
+        # 验证工程方案类型
+        valid_project_types = {
+            'bridge_up_part',  # 桥梁上部结构
+            'tunnel_construction',  # 隧道施工
+            'road_repair'  # 道路维修
+        }
+
+        if project_plan_type not in valid_project_types:
+            raise FileUploadErrors.project_plan_type_invalid()
+
+        # 生成文件ID和回调任务ID
+        file_id = str(uuid.uuid4())
+        created_at = int(time.time())
+        callback_task_id = f"{file_id}-{created_at}"
+
+        # 保存文件信息
+        file_info = {
+            "id": file_id,
+            "name": file.filename,
+            "size": 1024 * 1024,  # 文件大小 1MB
+            "created_at": created_at,
+            "status": "success",
+            "callback_task_id": callback_task_id,
+            "callback_url": callback_url,
+            "project_plan_type": project_plan_type,
+            "user": user,
+            "upload_time": datetime.now().isoformat()
+        }
+
+        # 文档处理(暂时注释,等文件保存逻辑实现后再启用)
+        # DocumentParse.document_parse(file_path)
+
+        uploaded_files[file_id] = file_info
+        uploaded_files[callback_task_id] = {
+            "file_id": file_id,
+            "user": user,
+            "review_task_status": "processing",
+            "overall_progress": 0,
+            "stages": [
+                {"stage_name": "文件上传", "progress": 100, "stage_status": "completed"},
+                {"stage_name": "格式校验", "progress": 0, "stage_status": "pending"},
+                {"stage_name": "内容提取", "progress": 0, "stage_status": "pending"},
+                {"stage_name": "智能审查", "progress": 0, "stage_status": "pending"}
+            ],
+            "updated_at": int(time.time()),
+            "estimated_remaining": 1800  # 预计30分钟
+        }
+
+        return FileUploadResponse(
+            code=200,
+            data={
+                "id": file_id,
+                "name": file.filename,
+                "size": file_info["size"],
+                "created_at": file_info["created_at"],
+                "status": "success",
+                "callback_task_id": callback_task_id
+            }
+        )
+
+    except Exception as e:
+        raise FileUploadErrors.internal_error(e)
+
+

+ 225 - 0
views/construction_review/review_results.py

@@ -0,0 +1,225 @@
+"""
+审查结果获取接口Mock实现
+模拟风险统计、总结报告和问题条文返回
+"""
+
+import uuid
+import random
+from datetime import datetime
+from fastapi import APIRouter, HTTPException
+from pydantic import BaseModel
+from typing import Optional, Dict, Any
+
+review_results_router = APIRouter(prefix="/sgsc", tags=["审查结果Mock"])
+
+# 导入文件上传模块的存储
+try:
+    from .file_upload import uploaded_files
+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"
+
+class ReviewResultsResponse(BaseModel):
+    code: int
+    data: Dict[str, Any]
+
+def generate_mock_risk_stats():
+    """生成模拟风险统计"""
+    return {
+        "high": random.randint(1, 5),
+        "medium": random.randint(3, 8),
+        "low": random.randint(2, 6)
+    }
+
+def generate_mock_dimension_scores():
+    """生成模拟四维评分"""
+    return {
+        "safety": random.randint(60, 95),
+        "quality": random.randint(55, 90),
+        "schedule": random.randint(70, 95),
+        "cost": random.randint(65, 90)
+    }
+
+def generate_mock_summary_report(risk_stats):
+    """生成模拟总结报告"""
+    total_issues = sum(risk_stats.values())
+    if risk_stats["high"] > 0:
+        return f"该施工方案存在{risk_stats['high']}处高风险问题,需重点整改。建议在施工前完善相关技术细节,确保符合规范要求。"
+    elif total_issues > 5:
+        return f"该施工方案整体符合规范要求,但存在{total_issues}处中低风险问题,建议优化完善。"
+    else:
+        return "该施工方案整体符合规范要求,存在少量细节问题,可正常施工。"
+
+def generate_mock_issues():
+    """生成模拟问题条文"""
+    issues = []
+
+    # 高风险问题示例
+    high_risk_issues = [
+        {
+            "page": 12,
+            "chapter": "1.1 路面材料要求",
+            "original_content": "采用沥青、混凝土作为路面施工材料,未明确标号及来源;施工段落仅标注主线段,未细化具体桩号范围"
+        },
+        {
+            "page": 45,
+            "chapter": "3.2 模板安装工艺",
+            "original_content": "模板未按设计要求进行预压,直接浇筑混凝土;预压观测记录采用文字描述,未体现观测点布置及沉降数据"
+        }
+    ]
+
+    # 中风险问题示例
+    medium_risk_issues = [
+        {
+            "page": 28,
+            "chapter": "2.3 施工机械配置",
+            "original_content": "施工机械清单未包含备用设备,未制定设备故障应急预案"
+        },
+        {
+            "page": 67,
+            "chapter": "4.1 质量保证措施",
+            "original_content": "质量检测频次未明确具体标准,检验方法描述不够详细"
+        }
+    ]
+
+    # 生成高风险问题
+    for i, issue_data in enumerate(high_risk_issues):
+        issue_id = f"ISSUE-HL-{datetime.now().strftime('%Y%m%d')}-{i+1:03d}"
+
+        reviews = [
+            {
+                "check_item": "强制性标准符合性检查",
+                "check_result": "不符合",
+                "risk_info": {"risk_level": "high"},
+                "suggestion": {
+                    "suggestion_type": "professional",
+                    "suggestion_content": "按相关规范要求,明确材料规格和施工参数,确保符合技术标准要求",
+                    "verification_standard": "整改后需提供技术规格书,由项目总工签字确认"
+                }
+            },
+            {
+                "check_item": "条文完整性检查",
+                "check_result": "不符合",
+                "risk_info": {"risk_level": "low"},
+                "suggestion": {
+                    "suggestion_type": "completeness",
+                    "suggestion_content": "补充详细的施工范围描述,与施工平面布置图桩号标注一致",
+                    "verification_standard": "参考施工方案编制导则相关条款"
+                }
+            }
+        ]
+
+        issues.append({
+            "issue_id": issue_id,
+            "metadata": issue_data,
+            "risk_summary": {
+                "max_risk_level": "high",
+                "risk_count": {"high": 1, "medium": 1, "low": 1},
+                "key_risk_reminder": "高风险点:技术参数缺失,需24小时内整改"
+            },
+            "review_lists": reviews
+        })
+
+    # 生成中风险问题
+    for i, issue_data in enumerate(medium_risk_issues):
+        issue_id = f"ISSUE-ML-{datetime.now().strftime('%Y%m%d')}-{i+1:03d}"
+
+        reviews = [
+            {
+                "check_item": "规范性检查",
+                "check_result": "不符合",
+                "risk_info": {"risk_level": "medium"},
+                "suggestion": {
+                    "suggestion_type": "normative",
+                    "suggestion_content": "完善应急预案制定,明确设备故障处理流程和备用资源配置",
+                    "verification_standard": "参考《施工组织设计规范》相关要求"
+                }
+            }
+        ]
+
+        issues.append({
+            "issue_id": issue_id,
+            "metadata": issue_data,
+            "risk_summary": {
+                "max_risk_level": "medium",
+                "risk_count": {"high": 0, "medium": 1, "low": 0},
+                "key_risk_reminder": "中风险点:管理措施不完善,需在施工前完善"
+            },
+            "review_lists": reviews
+        })
+
+    return issues
+
+@review_results_router.post("/review_results", response_model=ReviewResultsResponse)
+async def mock_review_results(request: ReviewResultsRequest):
+    """
+    Mock审查结果获取接口
+    """
+    try:
+        # 验证参数
+        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):
+            raise ReviewResultsErrors.missing_param_id()
+
+        # 验证callback_task_id格式(应该是UUID-时间戳格式)
+        if len(request.callback_task_id) < 20 or request.callback_task_id.count('-') < 4:
+            raise ReviewResultsErrors.invalid_id_format()
+
+        # 验证用户标识(应该是指定用户如user-001)
+        valid_users = {"user-001", "user-002", "user-003"}  # 可以配置化
+        if not request.user or request.user not in valid_users:
+            raise ReviewResultsErrors.invalid_user()
+
+        # 检查任务是否存在
+        if request.callback_task_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, {})
+        if file_info.get("user") != request.user:
+            raise ReviewResultsErrors.invalid_user()
+
+        # 检查任务状态(模拟:只有完成的任务才能查看结果)
+        if task_info and 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)
+
+            return ReviewResultsResponse(
+                code=200,
+                data={
+                    "risk_stats": risk_stats,
+                    "dimension_scores": dimension_scores,
+                    "summary_report": summary_report,
+                    "multidimensional_report": summary_report + "建议重点关注安全管理和质量控制措施的落实。"
+                }
+            )
+
+        else:  # issues
+            issues = generate_mock_issues()
+
+            return ReviewResultsResponse(
+                code=200,
+                data={
+                    "issues": issues
+                }
+            )
+
+    except Exception as e:
+        raise ReviewResultsErrors.server_error(e)

+ 315 - 0
views/construction_review/schemas/error_schemas.py

@@ -0,0 +1,315 @@
+"""
+施工方案审查API错误码统一定义
+集中管理所有接口的错误码和错误响应格式
+"""
+
+from typing import Dict, Any
+from fastapi import HTTPException
+
+
+class ErrorCodes:
+    """错误码常量定义"""
+
+    # 文件上传接口错误码 (WJSC001-WJSC008)
+    WJSC001 = {
+        "code": "WJSC001",
+        "error_type": "FILE_MISSING",
+        "message": "未上传文件",
+        "status_code": 400
+    }
+
+    WJSC002 = {
+        "code": "WJSC002",
+        "error_type": "FILE_MULTIPLE",
+        "message": "仅支持单文件上传",
+        "status_code": 400
+    }
+
+    WJSC003 = {
+        "code": "WJSC003",
+        "error_type": "FILE_REJECTED",
+        "message": "格式错误、内容违规、文件为空",
+        "status_code": 400
+    }
+
+    WJSC004 = {
+        "code": "WJSC004",
+        "error_type": "FILE_FORMAT_UNSUPPORTED",
+        "message": "文件格式不支持(仅允许pdf/doc/docx)",
+        "status_code": 400
+    }
+
+    WJSC005 = {
+        "code": "WJSC005",
+        "error_type": "FILE_SIZE_EXCEEDED",
+        "message": "文件过大(最大30MB)",
+        "status_code": 400
+    }
+
+    WJSC006 = {
+        "code": "WJSC006",
+        "error_type": "PROJECT_PLAN_TYPE_INVALID",
+        "message": "工程方案类型无效(未注册)",
+        "status_code": 400
+    }
+
+    WJSC007 = {
+        "code": "WJSC007",
+        "error_type": "UNAUTHORIZED",
+        "message": "认证失败(未提供或无效的Authorization)",
+        "status_code": 401
+    }
+
+    WJSC008 = {
+        "code": "WJSC008",
+        "error_type": "INTERNAL_ERROR",
+        "message": "服务端内部错误",
+        "status_code": 500
+    }
+
+    # 进度查询接口错误码 (JDLX001-JDLX006)
+    JDLX001 = {
+        "code": "JDLX001",
+        "error_type": "MISSING_PARAMETERS",
+        "message": "请求参数缺失",
+        "status_code": 400
+    }
+
+    JDLX002 = {
+        "code": "JDLX002",
+        "error_type": "INVALID_PARAM_FORMAT",
+        "message": "请求参数格式错误",
+        "status_code": 400
+    }
+
+    JDLX003 = {
+        "code": "JDLX003",
+        "error_type": "UNAUTHORIZED",
+        "message": "认证失败(未提供或无效的Authorization)",
+        "status_code": 401
+    }
+
+    JDLX004 = {
+        "code": "JDLX004",
+        "error_type": "INVALID_USER",
+        "message": "用户标识(user)无效",
+        "status_code": 403
+    }
+
+    JDLX005 = {
+        "code": "JDLX005",
+        "error_type": "TASK_NOT_FOUND",
+        "message": "任务ID不存在或已过期",
+        "status_code": 404
+    }
+
+    JDLX006 = {
+        "code": "JDLX006",
+        "error_type": "SERVER_INTERNAL_ERROR",
+        "message": "服务端内部错误",
+        "status_code": 500
+    }
+
+    # 审查结果接口错误码 (SCJG001-SCJG008)
+    SCJG001 = {
+        "code": "SCJG001",
+        "error_type": "INVALID_TYPE",
+        "message": "结果类型无效(非'summary'或'issues')",
+        "status_code": 400
+    }
+
+    SCJG002 = {
+        "code": "SCJG002",
+        "error_type": "MISSING_PARAM_ID",
+        "message": "callback_task_id缺失",
+        "status_code": 400
+    }
+
+    SCJG003 = {
+        "code": "SCJG003",
+        "error_type": "INVALID_ID_FORMAT",
+        "message": "callback_task_id格式错误",
+        "status_code": 400
+    }
+
+    SCJG004 = {
+        "code": "SCJG004",
+        "error_type": "UNAUTHORIZED",
+        "message": "认证失败(未提供或无效的Authorization)",
+        "status_code": 401
+    }
+
+    SCJG005 = {
+        "code": "SCJG005",
+        "error_type": "INVALID_USER",
+        "message": "用户标识无效",
+        "status_code": 403
+    }
+
+    SCJG006 = {
+        "code": "SCJG006",
+        "error_type": "TASK_NOT_FOUND",
+        "message": "callback_task_id不存在或已过期",
+        "status_code": 404
+    }
+
+    SCJG007 = {
+        "code": "SCJG007",
+        "error_type": "NO_REVIEW_RESULTS",
+        "message": "无审查结果数据",
+        "status_code": 404
+    }
+
+    SCJG008 = {
+        "code": "SCJG008",
+        "error_type": "SERVER_ERROR",
+        "message": "服务端内部错误(审查结果生成失败)",
+        "status_code": 500
+    }
+
+
+def create_http_exception(error_code: Dict[str, Any], custom_message: str = None) -> HTTPException:
+    """
+    创建HTTP异常
+
+    Args:
+        error_code: 错误码字典
+        custom_message: 自定义错误消息,可选
+
+    Returns:
+        HTTPException: FastAPI异常对象
+    """
+    detail = {
+        "code": error_code["code"],
+        "error_type": error_code["error_type"],
+        "message": custom_message or error_code["message"]
+    }
+
+    return HTTPException(
+        status_code=error_code["status_code"],
+        detail=detail
+    )
+
+
+def create_server_error(error_code: str, original_error: Exception) -> HTTPException:
+    """
+    创建服务器内部错误异常
+
+    Args:
+        error_code: 错误码 (如 "WJSC008", "JDLX006", "SCJG008")
+        original_error: 原始异常
+
+    Returns:
+        HTTPException: FastAPI异常对象
+    """
+    error_map = {
+        "WJSC008": ErrorCodes.WJSC008,
+        "JDLX006": ErrorCodes.JDLX006,
+        "SCJG008": ErrorCodes.SCJG008
+    }
+
+    error_config = error_map.get(error_code, ErrorCodes.WJSC008)
+    message = f"{error_config['message']}: {str(original_error)}"
+
+    return create_http_exception(error_config, message)
+
+
+# 便捷的错误创建函数
+class FileUploadErrors:
+    """文件上传接口错误"""
+
+    @staticmethod
+    def file_missing():
+        return create_http_exception(ErrorCodes.WJSC001)
+
+    @staticmethod
+    def file_multiple():
+        return create_http_exception(ErrorCodes.WJSC002)
+
+    @staticmethod
+    def file_rejected(message: str = None):
+        return create_http_exception(ErrorCodes.WJSC003, message)
+
+    @staticmethod
+    def file_format_unsupported():
+        return create_http_exception(ErrorCodes.WJSC004)
+
+    @staticmethod
+    def file_size_exceeded():
+        return create_http_exception(ErrorCodes.WJSC005)
+
+    @staticmethod
+    def project_plan_type_invalid():
+        return create_http_exception(ErrorCodes.WJSC006)
+
+    @staticmethod
+    def unauthorized():
+        return create_http_exception(ErrorCodes.WJSC007)
+
+    @staticmethod
+    def internal_error(original_error: Exception):
+        return create_server_error("WJSC008", original_error)
+
+
+class TaskProgressErrors:
+    """进度查询接口错误"""
+
+    @staticmethod
+    def missing_parameters():
+        return create_http_exception(ErrorCodes.JDLX001)
+
+    @staticmethod
+    def invalid_param_format():
+        return create_http_exception(ErrorCodes.JDLX002)
+
+    @staticmethod
+    def unauthorized():
+        return create_http_exception(ErrorCodes.JDLX003)
+
+    @staticmethod
+    def invalid_user():
+        return create_http_exception(ErrorCodes.JDLX004)
+
+    @staticmethod
+    def task_not_found():
+        return create_http_exception(ErrorCodes.JDLX005)
+
+    @staticmethod
+    def server_internal_error(original_error: Exception):
+        return create_server_error("JDLX006", original_error)
+
+
+class ReviewResultsErrors:
+    """审查结果接口错误"""
+
+    @staticmethod
+    def invalid_type():
+        return create_http_exception(ErrorCodes.SCJG001)
+
+    @staticmethod
+    def missing_param_id():
+        return create_http_exception(ErrorCodes.SCJG002)
+
+    @staticmethod
+    def invalid_id_format():
+        return create_http_exception(ErrorCodes.SCJG003)
+
+    @staticmethod
+    def unauthorized():
+        return create_http_exception(ErrorCodes.SCJG004)
+
+    @staticmethod
+    def invalid_user():
+        return create_http_exception(ErrorCodes.SCJG005)
+
+    @staticmethod
+    def task_not_found():
+        return create_http_exception(ErrorCodes.SCJG006)
+
+    @staticmethod
+    def no_review_results():
+        return create_http_exception(ErrorCodes.SCJG007)
+
+    @staticmethod
+    def server_error(original_error: Exception):
+        return create_server_error("SCJG008", original_error)

+ 157 - 0
views/construction_review/task_progress.py

@@ -0,0 +1,157 @@
+"""
+审查进度轮询接口Mock实现
+模拟任务进度更新,支持多阶段进度展示
+"""
+
+import time
+import random
+from datetime import datetime
+from fastapi import APIRouter, HTTPException, Query
+from pydantic import BaseModel
+from typing import Optional
+
+task_progress_router = APIRouter(prefix="/sgsc", tags=["进度轮询Mock"])
+
+# 导入文件上传模块的存储
+try:
+    from .file_upload import uploaded_files
+except ImportError:
+    from views.construction_review.file_upload import uploaded_files
+
+# 导入错误码定义
+from .schemas.error_schemas import TaskProgressErrors
+
+class TaskProgressResponse(BaseModel):
+    code: int
+    data: dict
+
+def update_task_progress(callback_task_id: str) -> dict:
+    """更新任务进度(模拟真实的处理过程)"""
+    if callback_task_id not in uploaded_files:
+        return None
+
+    task_info = uploaded_files[callback_task_id]
+    current_time = int(time.time())
+
+    # 根据时间模拟进度推进
+    time_elapsed = current_time - task_info.get("updated_at", current_time)
+
+    # 定义各阶段的时间分配(总时长约30分钟)
+    stage_durations = {
+        "格式校验": 60,      # 1分钟
+        "内容提取": 900,     # 15分钟
+        "智能审查": 840      # 14分钟
+    }
+
+    total_duration = sum(stage_durations.values())
+
+    # 计算当前应该处于哪个阶段
+    accumulated_time = 0
+    overall_progress = 0
+    stages = []
+
+    for stage_name, duration in stage_durations.items():
+        if time_elapsed > accumulated_time + duration:
+            # 阶段已完成
+            stages.append({
+                "stage_name": stage_name,
+                "progress": 100,
+                "stage_status": "completed"
+            })
+            accumulated_time += duration
+        elif time_elapsed > accumulated_time:
+            # 阶段进行中
+            stage_progress = min(100, int((time_elapsed - accumulated_time) / duration * 100))
+            stages.append({
+                "stage_name": stage_name,
+                "progress": stage_progress,
+                "stage_status": "processing"
+            })
+            accumulated_time += duration
+        else:
+            # 阶段未开始
+            stages.append({
+                "stage_name": stage_name,
+                "progress": 0,
+                "stage_status": "pending"
+            })
+
+    # 计算总进度
+    overall_progress = min(100, int(time_elapsed / total_duration * 100))
+
+    # 确定任务状态
+    if overall_progress >= 100:
+        review_task_status = "completed"
+        estimated_remaining = 0
+    else:
+        review_task_status = "processing"
+        estimated_remaining = max(0, total_duration - time_elapsed)
+
+    # 更新任务信息
+    task_info.update({
+        "review_task_status": review_task_status,
+        "overall_progress": overall_progress,
+        "stages": stages,
+        "updated_at": current_time,
+        "estimated_remaining": estimated_remaining
+    })
+
+    return task_info
+
+@task_progress_router.get("/task_progress/{callback_task_id}", response_model=TaskProgressResponse)
+async def task_progress(
+    callback_task_id: str,
+    user: str = Query(...)
+):
+    """
+    Mock任务进度轮询接口
+    """
+    try:
+        # 验证参数
+        if not callback_task_id or not isinstance(callback_task_id, str):
+            raise TaskProgressErrors.missing_parameters()
+
+        # 检查callback_task_id格式(应该是UUID-时间戳格式)
+        if len(callback_task_id) < 20 or callback_task_id.count('-') < 4:
+            raise TaskProgressErrors.invalid_param_format()
+
+        # 验证用户标识(应该是指定用户如user-001)
+        valid_users = {"user-001", "user-002", "user-003"}  # 可以配置化
+        if not user or user not in valid_users:
+            raise TaskProgressErrors.invalid_user()
+
+        # 检查任务是否存在
+        if callback_task_id not in uploaded_files:
+            raise TaskProgressErrors.task_not_found()
+
+        # 验证用户权限
+        task_info = uploaded_files[callback_task_id]
+        if task_info.get("user") != user:
+            raise TaskProgressErrors.invalid_user()
+
+        # 更新进度
+        updated_task = update_task_progress(callback_task_id)
+
+        return TaskProgressResponse(
+            code=200,
+            data={
+                "callback_task_id": callback_task_id,
+                "user": user,
+                "review_task_status": updated_task["review_task_status"],
+                "overall_progress": updated_task["overall_progress"],
+                "stages": updated_task["stages"],
+                "updated_at": updated_task["updated_at"],
+                "estimated_remaining": updated_task["estimated_remaining"]
+            }
+        )
+
+    except Exception as e:
+        raise TaskProgressErrors.server_internal_error(e)
+
+@task_progress_router.post("/mock/advance_time")
+async def advance_time(seconds: int = 300):
+    """Mock接口:推进时间(用于测试)"""
+    for callback_task_id in list(uploaded_files.keys()):
+        if "review_task_status" in uploaded_files[callback_task_id]:
+            uploaded_files[callback_task_id]["updated_at"] -= seconds
+    return {"message": f"时间推进了 {seconds} 秒"}

+ 7 - 7
views/test_views.py

@@ -14,13 +14,13 @@ from fastapi import Depends, Response, Header
 from sse_starlette import EventSourceResponse
 from starlette.responses import JSONResponse
 
-from agent.test_agent import test_agent_client
-from agent.generate.model_generate import test_generate_model_client
-from logger.loggering import server_logger
-from schemas.test_schemas import TestForm
-from utils.common import return_json, handler_err
+from foundation.agent.test_agent import test_agent_client
+from foundation.agent.generate.model_generate import test_generate_model_client
+from foundation.logger.loggering import server_logger
+from foundation.schemas.test_schemas import TestForm
+from foundation.utils.common import return_json, handler_err
 from views import test_router, get_operation_id
-from agent.workflow.test_workflow_graph import test_workflow_graph
+from foundation.agent.workflow.test_workflow_graph import test_workflow_graph
 
 
 
@@ -34,7 +34,7 @@ async def generate_chat_endpoint(
     """
     try:
         server_logger.info(trace_id=trace_id, msg=f"{param}")
-
+        print(trace_id)
         # 从字典中获取input
         input_query = param.input
         session_id = param.config.session_id