From 0215825987916ffabe7562f970649493c1a529fd Mon Sep 17 00:00:00 2001 From: ruoyunbai <19376215@buaa.edu.cn> Date: Tue, 30 Sep 2025 10:18:34 +0800 Subject: [PATCH] clean --- .../0/2-165956/2-165956/Dockerfile | 11 - .../0/2-165956/2-165956/README.md | 29 - .../0/2-165956/2-165956/docker-compose.yml | 17 - .../0/2-165956/2-165956/fastapi_server.py | 1109 ----------------- .../0/2-165956/2-165956/history_viewer.py | 315 ----- .../0/2-165956/2-165956/requirements.txt | 22 - .../0/2-165956/2-165956/set-permissions.sh | 3 - .../0/2-165956/2-165956/start.bat | 10 - .../0/2-165956/2-165956/start.sh | 10 - .../0/2-165956/2-165956/stop.bat | 4 - .../0/2-165956/2-165956/stop.sh | 5 - .../0/2-165956/2-165956/supervisord.conf | 20 - fix-existing-package.bat | 139 --- fix-line-endings.sh | 101 -- run-demo.sh | 11 - 15 files changed, 1806 deletions(-) delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/Dockerfile delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/README.md delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/docker-compose.yml delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/fastapi_server.py delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/history_viewer.py delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/requirements.txt delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/set-permissions.sh delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.bat delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.sh delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.bat delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.sh delete mode 100644 dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/supervisord.conf delete mode 100644 fix-existing-package.bat delete mode 100644 fix-line-endings.sh delete mode 100644 run-demo.sh diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/Dockerfile b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/Dockerfile deleted file mode 100644 index ab313ab..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM python:3.11-alpine -WORKDIR /app -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt -RUN pip install --no-cache-dir fastapi uvicorn[standard] -RUN apk add --no-cache supervisor curl bash -COPY . . -RUN mkdir -p /var/log/supervisor -COPY supervisord.conf /etc/supervisor/conf.d/ -EXPOSE 5050 5051 -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/README.md b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/README.md deleted file mode 100644 index 3955483..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# DMS Compliance Tool - Cross-Platform Version - -## Quick Start - -### Windows: -1. Run start.bat -2. Access web interface -3. Use stop.bat to stop - -### Linux/macOS: -1. chmod +x *.sh (or run ./set-permissions.sh) -2. ./start.sh -3. Access web interface -4. ./stop.sh to stop - -## Architecture -- FastAPI Server: http://localhost:5050 -- History Viewer: http://localhost:5051 -- Dual service architecture with supervisor - -## Management Commands -Windows: start.bat, stop.bat, logs.bat -Linux: start.sh, stop.sh, logs.sh - -## Package Contents -- docker-compose.yml: Service configuration (uses pre-built image) -- Dockerfile: Container build instructions (for reference) -- docker-image.tar: Pre-built Docker image (fast startup) -- Cross-platform management scripts (auto-load image) diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/docker-compose.yml b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/docker-compose.yml deleted file mode 100644 index 1a78eba..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - dms-compliance: - image: compliance-dms-windows:latest - container_name: dms-compliance-tool - ports: - - "5050:5050" - - "5051:5051" - volumes: - - ./uploads:/app/uploads - - ./logs:/app/logs - restart: unless-stopped - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5050/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/fastapi_server.py b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/fastapi_server.py deleted file mode 100644 index de3ea32..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/fastapi_server.py +++ /dev/null @@ -1,1109 +0,0 @@ -#!/usr/bin/env python3 -""" -DMS合规性测试工具 - FastAPI版本API服务器 -提供自动生成的交互式API文档 -""" - -import os -import sys -import json -import logging -import datetime -import traceback -from pathlib import Path -from typing import List, Optional, Dict, Any, Union -import unicodedata -import html - -# FastAPI imports -from fastapi import FastAPI, HTTPException, BackgroundTasks, status -from fastapi.responses import JSONResponse, FileResponse -from fastapi.middleware.cors import CORSMiddleware -from pydantic import BaseModel, Field, field_validator, model_validator -import uvicorn - -# PDF generation libraries - with fallback -try: - from reportlab.lib import colors - from reportlab.lib.pagesizes import A4 - from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle - from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable - from reportlab.pdfbase import pdfmetrics - from reportlab.pdfbase.ttfonts import TTFont - reportlab_available = True -except ImportError: - reportlab_available = False - -# Project-specific imports -from ddms_compliance_suite.api_caller.caller import APICallDetail -from ddms_compliance_suite.test_orchestrator import APITestOrchestrator, TestSummary -from ddms_compliance_suite.input_parser.parser import ParsedAPISpec -from ddms_compliance_suite.utils.response_utils import extract_data_for_validation -from ddms_compliance_suite.utils.data_generator import DataGenerator - -# Configure logging -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__name__) - -# FastAPI app instance -app = FastAPI( - title="DMS合规性测试工具 API", - description=""" - DMS合规性测试工具 FastAPI版本 - - 这是一个用于API合规性测试的工具,支持: - - YAPI规范测试 - 基于YAPI定义文件的测试 - Swagger/OpenAPI测试 - 基于OpenAPI规范的测试 - DMS服务发现测试 - 动态发现DMS服务的API进行测试 - 分页支持 - 支持大量API的分页获取,避免内存溢出 - PDF报告生成 - 生成详细的测试报告 - LLM集成 - 支持大语言模型辅助生成测试数据 - - 主要特性 - - 🚀 高性能: 基于FastAPI,支持异步处理 - 📊 分页支持: 解决大量API节点的内存问题 - 📝 自动文档: 自动生成交互式API文档 - 🔧 灵活配置: 支持多种测试配置选项 - 📈 详细报告: 生成PDF和JSON格式的测试报告 - """, - version="1.0.0", - docs_url="/docs", # Swagger UI - redoc_url="/redoc", # ReDoc -) - -# Add CORS middleware -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], # 在生产环境中应该限制具体域名 - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -# Pydantic models for request/response -class TestConfig(BaseModel): - """测试配置模型""" - - # API定义源 (三选一) - yapi: Optional[str] = Field(None, description="YAPI定义文件路径", exclude=True) - swagger: Optional[str] = Field(None, description="Swagger/OpenAPI定义文件路径", exclude=True) - dms: Optional[str] = Field("./assets/doc/dms/domain.json", description="DMS服务发现的domain mapping文件路径", example="./assets/doc/dms/domain.json") - # 基本配置 - base_url: str = Field("https://www.dev.ideas.cnpc/", description="API基础URL", example="https://www.dev.ideas.cnpc/") - - # 分页配置 - page_size: int = Field(10, description="DMS API分页大小,默认10。较小的值可以减少内存使用", ge=1, le=10000) - page_no: int = Field(1, description="起始页码,从1开始。可用于断点续传或跳过前面的页面", ge=1) - fetch_all_pages: bool = Field(False, description="是否获取所有页面。True=获取所有数据,False=只获取指定页面") - - # 过滤选项 - strictness_level: str = Field("CRITICAL", description="测试严格等级", pattern="^(CRITICAL|HIGH|MEDIUM|LOW)$") - - @field_validator('base_url') - @classmethod - def validate_base_url(cls, v): - if not v.startswith(('http://', 'https://')): - raise ValueError('base_url must start with http:// or https://') - return v - - @model_validator(mode='before') - @classmethod - def validate_api_source(cls, values): - """验证API定义源,确保三选一""" - if isinstance(values, dict): - api_sources = [values.get('yapi'), values.get('swagger'), values.get('dms')] - non_none_sources = [s for s in api_sources if s is not None] - if len(non_none_sources) > 1: - raise ValueError('只能选择一个API定义源:yapi、swagger或dms') - if len(non_none_sources) == 0: - raise ValueError('必须提供一个API定义源:yapi、swagger或dms') - return values - -class PaginationInfo(BaseModel): - """分页信息模型""" - page_size: int = Field(description="页面大小") - page_no_start: int = Field(description="起始页码") - total_pages: int = Field(description="总页数") - total_records: int = Field(description="总记录数") - pages_fetched: int = Field(description="已获取页数") - current_page: int = Field(description="当前页码") - -class TestResponse(BaseModel): - """测试响应模型""" - status: str = Field(description="测试状态", example="completed") - message: str = Field(description="状态消息") - report_directory: str = Field(description="报告目录路径") - summary: Dict[str, Any] = Field(description="测试摘要信息") - pagination: Optional[PaginationInfo] = Field(None, description="分页信息(仅DMS测试时返回)") - -class ErrorResponse(BaseModel): - """错误响应模型""" - status: str = Field("error", description="错误状态") - message: str = Field(description="错误消息") - traceback: Optional[str] = Field(None, description="错误堆栈跟踪") - -# Global variable to store running tasks -running_tasks: Dict[str, Dict[str, Any]] = {} - -@app.get("/", - summary="健康检查", - description="检查API服务器是否正常运行", - response_model=Dict[str, str]) -async def health_check(): - """健康检查端点,用于Docker健康检查""" - return { - "status": "healthy", - "service": "DMS Compliance API Server (FastAPI)", - "version": "2.0.0", - "docs_url": "/docs", - "redoc_url": "/redoc" - } - -@app.get("/info", - summary="服务信息", - description="获取API服务器的详细信息", - response_model=Dict[str, Any]) -async def get_info(): - """获取服务器信息""" - return { - "service": "DMS Compliance API Server", - "version": "2.0.0", - "framework": "FastAPI", - "features": [ - "YAPI规范测试", - "Swagger/OpenAPI测试", - "DMS服务发现测试", - "分页支持", - "PDF报告生成", - "LLM集成", - "自动API文档" - ], - "endpoints": { - "health": "/", - "info": "/info", - "run_tests": "/run", - "docs": "/docs", - "redoc": "/redoc" - }, - "reportlab_available": reportlab_available - } - -# Import the test logic from the original Flask version -def run_tests_logic(config: dict): - """ - Main logic for running tests, adapted from the original Flask version. - """ - try: - if config.get('verbose'): - logging.getLogger('ddms_compliance_suite').setLevel(logging.DEBUG) - logger.setLevel(logging.DEBUG) - logger.debug("Verbose logging enabled.") - - if not any(k in config for k in ['yapi', 'swagger', 'dms']): - raise ValueError("An API definition source is required: --yapi, --swagger, or --dms") - - if sum(k in config for k in ['yapi', 'swagger', 'dms']) > 1: - raise ValueError("API definition sources are mutually exclusive.") - - # Setup output directory with timestamp - base_output_dir = Path(config.get('output', './test_reports')) - timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") - output_directory = base_output_dir / timestamp - output_directory.mkdir(parents=True, exist_ok=True) - logger.info(f"Test reports will be saved to: {output_directory.resolve()}") - print(f"config{config}") - # Initialize the orchestrator - orchestrator = APITestOrchestrator( - base_url=config['base_url'], - custom_test_cases_dir=config.get('custom_test_cases_dir'), - llm_api_key=config.get('llm_api_key'), - llm_base_url=config.get('llm_base_url'), - llm_model_name=config.get('llm_model_name'), - use_llm_for_request_body=config.get('use_llm_for_request_body', False), - use_llm_for_path_params=config.get('use_llm_for_path_params', False), - use_llm_for_query_params=config.get('use_llm_for_query_params', False), - use_llm_for_headers=config.get('use_llm_for_headers', False), - output_dir=str(output_directory), - stages_dir=config.get('stages_dir'), - strictness_level=config.get('strictness_level', 'CRITICAL'), - ignore_ssl=config.get('ignore_ssl', False) - ) - - test_summary: Optional[TestSummary] = None - parsed_spec: Optional[ParsedAPISpec] = None - pagination_info: Dict[str, Any] = {} - - if 'yapi' in config: - logger.info(f"Running tests from YAPI file: {config['yapi']}") - test_summary, parsed_spec = orchestrator.run_tests_from_yapi( - yapi_file_path=config['yapi'], - categories=config.get('categories'), - custom_test_cases_dir=config.get('custom_test_cases_dir') - ) - elif 'swagger' in config: - logger.info(f"Running tests from Swagger file: {config['swagger']}") - test_summary, parsed_spec = orchestrator.run_tests_from_swagger( - swagger_file_path=config['swagger'], - tags=config.get('tags'), - custom_test_cases_dir=config.get('custom_test_cases_dir') - ) - elif 'dms' in config: - logger.info(f"Running tests from DMS service discovery: {config['dms']}") - test_summary, parsed_spec, pagination_info = orchestrator.run_tests_from_dms( - domain_mapping_path=config['dms'], - categories=config.get('categories'), - custom_test_cases_dir=config.get('custom_test_cases_dir'), - page_size=config.get('page_size', 1000), - page_no_start=config.get('page_no', 1), - fetch_all_pages=config.get('fetch_all_pages', True) - ) - - if not parsed_spec: - raise RuntimeError("Failed to parse the API specification.") - - if test_summary and config.get('stages_dir') and parsed_spec: - logger.info(f"Executing API test stages from directory: {config['stages_dir']}") - orchestrator.run_stages_from_spec(parsed_spec, test_summary) - - if test_summary: - # Save main summary - main_report_file_path = output_directory / "summary.json" - with open(main_report_file_path, 'w', encoding='utf-8') as f: - f.write(test_summary.to_json(pretty=True)) - - # Save API call details - api_calls_filename = "api_call_details.md" - save_api_call_details_to_file( - orchestrator.get_api_call_details(), - str(output_directory), - filename=api_calls_filename - ) - - # Generate PDF report if reportlab is available - if reportlab_available and config.get('generate_pdf', True): - pdf_report_path = output_directory / "report_cn.pdf" - save_pdf_report(test_summary.to_dict(), pdf_report_path, config.get('strictness_level', 'CRITICAL')) - - failed_count = getattr(test_summary, 'endpoints_failed', 0) + getattr(test_summary, 'test_cases_failed', 0) - error_count = getattr(test_summary, 'endpoints_error', 0) + getattr(test_summary, 'test_cases_error', 0) - - result = { - "status": "completed", - "message": "Tests finished." if failed_count == 0 and error_count == 0 else "Tests finished with failures or errors.", - "report_directory": str(output_directory.resolve()), - "summary": test_summary.to_dict() - } - - # 如果有分页信息,添加到返回结果中 - if pagination_info: - result["pagination"] = pagination_info - - return result - else: - raise RuntimeError("Test execution failed to produce a summary.") - - except Exception as e: - logger.error(f"An unexpected error occurred during test execution: {e}", exc_info=True) - return { - "status": "error", - "message": str(e), - "traceback": traceback.format_exc() - } - -def save_api_call_details_to_file(api_call_details: List[APICallDetail], output_dir_path: str, filename: str = "api_call_details.md"): - """ - 将API调用详情列表保存到指定目录下的 Markdown 文件中。 - 同时,额外生成一个纯文本文件 (.txt),每行包含一个 cURL 命令。 - """ - if not api_call_details: - logger.info("没有API调用详情可供保存。") - return - - output_dir = Path(output_dir_path) - try: - output_dir.mkdir(parents=True, exist_ok=True) - except OSError as e: - logger.error(f"创建API调用详情输出目录 {output_dir} 失败: {e}") - return - - # 主文件是 Markdown 文件 - md_output_file = output_dir / filename - # 确保它是 .md,尽管 main 函数应该已经处理了 - if md_output_file.suffix.lower() not in ['.md', '.markdown']: - md_output_file = md_output_file.with_suffix('.md') - - markdown_content = [] - - for detail in api_call_details: - - # Request URL with params (if any) - url_to_display = detail.request_url - if detail.request_params: - try: - # Ensure urllib is available for this formatting step - import urllib.parse - query_string = urllib.parse.urlencode(detail.request_params) - url_to_display = f"{detail.request_url}?{query_string}" - except Exception as e: - logger.warning(f"Error formatting URL with params for display: {e}") - # Fallback to just the base URL if params formatting fails - - markdown_content.append(f"## `{detail.request_method} {url_to_display}`") - markdown_content.append("**cURL Command:**") - markdown_content.append("```sh") - markdown_content.append(detail.curl_command) - markdown_content.append("```") - markdown_content.append("### Request Details") - markdown_content.append(f"- **Method:** `{detail.request_method}`") - markdown_content.append(f"- **Full URL:** `{url_to_display}`") - - markdown_content.append("- **Headers:**") - markdown_content.append("```json") - markdown_content.append(json.dumps(detail.request_headers, indent=2, ensure_ascii=False)) - markdown_content.append("```") - - if detail.request_params: - markdown_content.append("- **Query Parameters:**") - markdown_content.append("```json") - markdown_content.append(json.dumps(detail.request_params, indent=2, ensure_ascii=False)) - markdown_content.append("```") - - if detail.request_body is not None: - markdown_content.append("- **Body:**") - body_lang = "text" - formatted_body = str(detail.request_body) - try: - # Try to parse as JSON for pretty printing - if isinstance(detail.request_body, str): - try: - parsed_json = json.loads(detail.request_body) - formatted_body = json.dumps(parsed_json, indent=2, ensure_ascii=False) - body_lang = "json" - except json.JSONDecodeError: - pass # Keep as text - elif isinstance(detail.request_body, (dict, list)): - formatted_body = json.dumps(detail.request_body, indent=2, ensure_ascii=False) - body_lang = "json" - except Exception as e: - logger.warning(f"Error formatting request body for Markdown: {e}") - - markdown_content.append(f"```{body_lang}") - markdown_content.append(formatted_body) - markdown_content.append("```") - - markdown_content.append("### Response Details") - markdown_content.append(f"- **Status Code:** `{detail.response_status_code}`") - markdown_content.append(f"- **Elapsed Time:** `{detail.response_elapsed_time:.4f}s`") - - markdown_content.append("- **Headers:**") - markdown_content.append("```json") - markdown_content.append(json.dumps(detail.response_headers, indent=2, ensure_ascii=False)) - markdown_content.append("```") - - if detail.response_body is not None: - markdown_content.append("- **Body:**") - resp_body_lang = "text" - formatted_resp_body = str(detail.response_body) - try: - # Try to parse as JSON for pretty printing - if isinstance(detail.response_body, str): - try: - # If it's already a string that might be JSON, try parsing and re-dumping - parsed_json_resp = json.loads(detail.response_body) - formatted_resp_body = json.dumps(parsed_json_resp, indent=2, ensure_ascii=False) - resp_body_lang = "json" - except json.JSONDecodeError: - # It's a string, but not valid JSON, keep as text - pass - elif isinstance(detail.response_body, (dict, list)): - # It's already a dict/list, dump it as JSON - formatted_resp_body = json.dumps(detail.response_body, indent=2, ensure_ascii=False) - resp_body_lang = "json" - # If it's neither string nor dict/list (e.g. int, bool from parsed json), str() is fine. - except Exception as e: - logger.warning(f"Error formatting response body for Markdown: {e}") - - markdown_content.append(f"```{resp_body_lang}") - markdown_content.append(formatted_resp_body) - markdown_content.append("```") - markdown_content.append("") # Add a blank line for spacing before next --- or EOF - markdown_content.append("---") # Separator - - try: - with open(md_output_file, 'w', encoding='utf-8') as f_md: - f_md.write("\n".join(markdown_content)) - logger.info(f"API调用详情已保存为 Markdown: {md_output_file}") - except Exception as e: - logger.error(f"保存API调用详情到 Markdown 文件 {md_output_file} 失败: {e}", exc_info=True) - -def save_pdf_report(summary_data, output_path: Path, strictness_level: str = 'CRITICAL'): - """将测试摘要保存为格式化的PDF文件""" - logger.info(f"开始生成PDF报告: {output_path}") - output_path.parent.mkdir(parents=True, exist_ok=True) - - try: - # --- 统一的字体管理和注册 --- - font_name = 'SimSun' # 使用一个简单清晰的注册名 - font_path = 'assets/fonts/STHeiti-Medium-4.ttc' - - if not Path(font_path).exists(): - logger.error(f"字体文件未找到: {Path(font_path).resolve()}") - return - - # 关键修复: 对于 .ttc (TrueType Collection) 文件, 必须指定 subfontIndex - pdfmetrics.registerFont(TTFont(font_name, font_path, subfontIndex=0)) - # 将注册的字体关联到 'SimSun' 字体族 - pdfmetrics.registerFontFamily(font_name, normal=font_name, bold=font_name, italic=font_name, boldItalic=font_name) - - doc = SimpleDocTemplate(str(output_path), pagesize=A4, title="API测试报告") - elements = [] - - # --- 统一样式定义, 全部使用注册的字体名 --- - styles = getSampleStyleSheet() - title_style = ParagraphStyle('ChineseTitle', parent=styles['Title'], fontName=font_name, fontSize=22, leading=28) - heading_style = ParagraphStyle('ChineseHeading', parent=styles['Heading1'], fontName=font_name, fontSize=16, leading=20, spaceAfter=8) - normal_style = ParagraphStyle('ChineseNormal', parent=styles['Normal'], fontName=font_name, fontSize=10, leading=14) - small_style = ParagraphStyle('ChineseSmall', parent=styles['Normal'], fontName=font_name, fontSize=9, leading=12) - - def to_para(text, style=normal_style, escape=True): - """ - 根据用户建议移除 textwrap 以进行诊断。 - 此版本只包含净化和基本的换行符替换。 - """ - if text is None: - content = "" - else: - content = str(text) - - if escape: - content = html.escape(content) - - # 依然保留Unicode控制字符的净化 - content = "".join(ch for ch in content if unicodedata.category(ch)[0] != 'C') - - if not content.strip(): - # 对于完全空白或None的输入,返回一个安全的非换行空格 - return Paragraph(' ', style) - - # 只使用基本的换行符替换 - content = content.replace('\n', '
') - - return Paragraph(content, style) - - # 3. 填充PDF内容 - 优化后的报告格式 - - # 生成报告编码(基于时间戳) - import time - report_code = f"DMS-TEST-{int(time.time())}" - - # 报告标题 - elements.append(to_para("数据管理服务测试分析报告", title_style, escape=False)) - elements.append(Spacer(1, 15)) - - # 报告基本信息表格 - basic_info_data = [ - [to_para("报告编码", escape=False), to_para(report_code)], - [to_para("报告名称", escape=False), to_para("DMS领域数据服务测试分析报告")], - [to_para("申请日期", escape=False), to_para(datetime.datetime.now().strftime('%Y年%m月%d日'))], - [to_para("申请人", escape=False), to_para("系统管理员")], - [to_para("服务供应商名称", escape=False), to_para("数据管理系统(DMS)")], - ] - basic_info_table = Table(basic_info_data, colWidths=[120, '*']) - basic_info_table.setStyle(TableStyle([ - ('GRID', (0,0), (-1,-1), 1, colors.grey), - ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), - ('BACKGROUND', (0,0), (0,-1), colors.lightgrey) - ])) - elements.append(basic_info_table) - elements.append(Spacer(1, 20)) - - # 摘要部分 - elements.append(to_para("摘要", heading_style, escape=False)) - overall = summary_data.get('overall_summary', {}) - - # 从JSON提取并格式化时间 - try: - start_time_str = summary_data.get('start_time', 'N/A') - end_time_str = summary_data.get('end_time', 'N/A') - duration = summary_data.get('duration_seconds', summary_data.get('duration', 0.0)) - - start_time_formatted = datetime.datetime.fromisoformat(start_time_str).strftime('%Y-%m-%d %H:%M:%S') if start_time_str != 'N/A' else 'N/A' - end_time_formatted = datetime.datetime.fromisoformat(end_time_str).strftime('%Y-%m-%d %H:%M:%S') if end_time_str != 'N/A' else 'N/A' - except: - start_time_formatted = start_time_str - end_time_formatted = end_time_str - - # 摘要内容 - 安全计算跳过的数量 - def safe_subtract(total, passed, failed): - """安全地计算跳过的数量""" - try: - if isinstance(total, (int, float)) and isinstance(passed, (int, float)) and isinstance(failed, (int, float)): - return max(0, total - passed - failed) - else: - return 0 - except: - return 0 - - endpoints_tested = overall.get('endpoints_tested', 0) - endpoints_passed = overall.get('endpoints_passed', 0) - endpoints_failed = overall.get('endpoints_failed', 0) - endpoints_skipped = safe_subtract(endpoints_tested, endpoints_passed, endpoints_failed) - - test_cases_executed = overall.get('total_test_cases_executed', 0) - test_cases_passed = overall.get('test_cases_passed', 0) - test_cases_failed = overall.get('test_cases_failed', 0) - test_cases_skipped = safe_subtract(test_cases_executed, test_cases_passed, test_cases_failed) - - stages_executed = overall.get('total_stages_executed', 0) - stages_passed = overall.get('stages_passed', 0) - stages_failed = overall.get('stages_failed', 0) - stages_skipped = safe_subtract(stages_executed, stages_passed, stages_failed) - - summary_text = f"""本次测试针对DMS(数据管理系统)领域数据服务进行全面的合规性验证。 -测试时间:{start_time_formatted} 至 {end_time_formatted},总耗时 {float(duration):.2f} 秒。 -共测试 {endpoints_tested} 个API端点,其中 {endpoints_passed} 个通过,{endpoints_failed} 个失败,{endpoints_skipped} 个跳过,端点成功率为 {overall.get('endpoint_success_rate', 'N/A')}。 -执行 {test_cases_executed} 个测试用例,其中 {test_cases_passed} 个通过,{test_cases_failed} 个失败,{test_cases_skipped} 个跳过,测试用例成功率为 {overall.get('test_case_success_rate', 'N/A')}。 -执行 {stages_executed} 个流程测试,其中 {stages_passed} 个通过,{stages_failed} 个失败,{stages_skipped} 个跳过,流程测试成功率为 {overall.get('stage_success_rate', 'N/A')}。""" - - elements.append(to_para(summary_text, normal_style)) - elements.append(Spacer(1, 20)) - - # 测试内容包括 - API列表表格 - elements.append(to_para("测试内容包括", heading_style, escape=False)) - - # 从测试结果中提取API信息 - endpoint_results = summary_data.get('endpoint_results', []) - api_list_data = [ - [to_para("序号", escape=False), to_para("服务名称", escape=False), - to_para("服务功能描述", escape=False), to_para("服务参数描述", escape=False), - to_para("服务返回值描述", escape=False)] - ] - - for i, endpoint in enumerate(endpoint_results[:10], 1): # 限制显示前10个API - endpoint_name = endpoint.get('endpoint_name', 'N/A') - - # 简化的功能描述 - if 'Create' in endpoint_name: - func_desc = "提供数据创建服务" - elif 'List' in endpoint_name or 'Query' in endpoint_name: - func_desc = "提供数据查询和列表服务" - elif 'Read' in endpoint_name: - func_desc = "提供单条数据读取服务" - elif 'Update' in endpoint_name: - func_desc = "提供数据更新服务" - elif 'Delete' in endpoint_name: - func_desc = "提供数据删除服务" - else: - func_desc = "提供数据管理服务" - - api_list_data.append([ - to_para(str(i), small_style), - to_para(endpoint_name, small_style), - to_para(func_desc, small_style), - to_para("标准DMS参数格式", small_style), - to_para("标准DMS响应格式", small_style) - ]) - - api_list_table = Table(api_list_data, colWidths=[30, 80, 120, 80, 80]) - api_list_table.setStyle(TableStyle([ - ('GRID', (0,0), (-1,-1), 1, colors.grey), - ('BACKGROUND', (0,0), (-1,0), colors.lightgrey), - ('ALIGN', (0,0), (-1,-1), 'CENTER'), - ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), - ('FONTSIZE', (0,0), (-1,-1), 8) - ])) - elements.append(api_list_table) - elements.append(Spacer(1, 20)) - - # 测试用例列表 - 根据严格等级分为必须和非必须 - elements.append(to_para("测试用例列表", heading_style, escape=False)) - - # 定义严重性等级的数值映射 - severity_levels = { - 'CRITICAL': 5, - 'HIGH': 4, - 'MEDIUM': 3, - 'LOW': 2, - 'INFO': 1 - } - - strictness_value = severity_levels.get(strictness_level, 5) # 默认为CRITICAL - - # 收集所有测试用例(包括endpoint用例和stage用例) - all_test_cases = [] - failed_test_cases = [] # 专门收集失败的测试用例 - - # 1. 收集endpoint测试用例 - for endpoint_result in endpoint_results: - test_cases = endpoint_result.get('executed_test_cases', []) - for tc in test_cases: - tc_severity = tc.get('test_case_severity', 'MEDIUM') - tc_severity_value = severity_levels.get(tc_severity, 3) - tc_status = tc.get('status', 'N/A') - tc_message = tc.get('message', '') - - test_case_info = { - 'type': 'Endpoint', - 'endpoint': endpoint_result.get('endpoint_name', 'N/A'), - 'endpoint_id': endpoint_result.get('endpoint_id', 'N/A'), - 'case_name': tc.get('test_case_name', 'N/A'), - 'case_id': tc.get('test_case_id', 'N/A'), - 'status': tc_status, - 'message': tc_message, - 'severity': tc_severity, - 'severity_value': tc_severity_value, - 'is_required': tc_severity_value >= strictness_value, - 'duration': tc.get('duration_seconds', 0), - 'timestamp': tc.get('timestamp', '') - } - - all_test_cases.append(test_case_info) - - # 收集失败的测试用例 - if tc_status in ['失败', 'FAILED', '错误', 'ERROR']: - failed_test_cases.append(test_case_info) - - # 2. 收集stage测试用例 - stage_results = summary_data.get('stage_results', []) - for stage_result in stage_results: - stage_name = stage_result.get('stage_name', 'N/A') - stage_status = stage_result.get('overall_status', 'N/A') - stage_message = stage_result.get('message', stage_result.get('error_message', '')) - stage_severity = 'HIGH' # Stage用例通常是高优先级 - stage_severity_value = severity_levels.get(stage_severity, 4) - - # 将stage作为一个测试用例添加 - stage_case_info = { - 'type': 'Stage', - 'endpoint': f"Stage: {stage_name}", - 'endpoint_id': f"STAGE_{stage_name}", - 'case_name': stage_result.get('description', stage_name), - 'case_id': f"STAGE_{stage_name}", - 'status': stage_status, - 'message': stage_message, - 'severity': stage_severity, - 'severity_value': stage_severity_value, - 'is_required': stage_severity_value >= strictness_value, - 'duration': stage_result.get('duration_seconds', 0), - 'timestamp': stage_result.get('start_time', '') - } - - all_test_cases.append(stage_case_info) - - # 收集失败的stage用例 - if stage_status in ['失败', 'FAILED', '错误', 'ERROR']: - failed_test_cases.append(stage_case_info) - - # 分离必须和非必须的测试用例 - required_cases = [case for case in all_test_cases if case['is_required']] - optional_cases = [case for case in all_test_cases if not case['is_required']] - - # 创建分离的测试用例表格 - if all_test_cases: - # 添加严格等级说明 - strictness_text = f"当前严格等级:{strictness_level}。根据此等级,测试用例被分为必须执行和非必须执行两部分。" - elements.append(to_para(strictness_text, small_style)) - elements.append(Spacer(1, 10)) - - # 1. 必须的测试用例表格 - if required_cases: - elements.append(to_para("必须的测试用例(影响测试结果)", heading_style, escape=False)) - - required_table_data = [ - [to_para("序号", escape=False), to_para("类型", escape=False), - to_para("测试用例名称", escape=False), to_para("所属端点/阶段", escape=False), - to_para("优先级", escape=False), to_para("执行结果", escape=False)] - ] - - for i, case in enumerate(required_cases, 1): - status_display = "通过" if case['status'] == "通过" else "失败" if case['status'] == "失败" else case['status'] - required_table_data.append([ - to_para(str(i), small_style), - to_para(case['type'], small_style), - to_para(case['case_name'], small_style), - to_para(case['endpoint'], small_style), - to_para(case['severity'], small_style), - to_para(status_display, small_style) - ]) - - required_table = Table(required_table_data, colWidths=[25, 35, 110, 90, 45, 45]) - required_table.setStyle(TableStyle([ - ('GRID', (0,0), (-1,-1), 1, colors.grey), - ('BACKGROUND', (0,0), (-1,0), colors.lightblue), # 使用浅蓝色突出必须用例 - ('ALIGN', (0,0), (-1,-1), 'CENTER'), - ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), - ('FONTSIZE', (0,0), (-1,-1), 8) - ])) - elements.append(required_table) - elements.append(Spacer(1, 15)) - - # 2. 非必须的测试用例表格 - if optional_cases: - elements.append(to_para("非必须的测试用例(不影响测试结果)", heading_style, escape=False)) - - optional_table_data = [ - [to_para("序号", escape=False), to_para("类型", escape=False), - to_para("测试用例名称", escape=False), to_para("所属端点/阶段", escape=False), - to_para("优先级", escape=False), to_para("执行结果", escape=False)] - ] - - for i, case in enumerate(optional_cases, 1): - status_display = "通过" if case['status'] == "通过" else "失败" if case['status'] == "失败" else case['status'] - optional_table_data.append([ - to_para(str(i), small_style), - to_para(case['type'], small_style), - to_para(case['case_name'], small_style), - to_para(case['endpoint'], small_style), - to_para(case['severity'], small_style), - to_para(status_display, small_style) - ]) - - optional_table = Table(optional_table_data, colWidths=[25, 35, 110, 90, 45, 45]) - optional_table.setStyle(TableStyle([ - ('GRID', (0,0), (-1,-1), 1, colors.grey), - ('BACKGROUND', (0,0), (-1,0), colors.lightgrey), # 使用浅灰色表示非必须用例 - ('ALIGN', (0,0), (-1,-1), 'CENTER'), - ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), - ('FONTSIZE', (0,0), (-1,-1), 8) - ])) - elements.append(optional_table) - elements.append(Spacer(1, 10)) - - # 添加用例统计信息 - total_cases = len(all_test_cases) - endpoint_cases = len([c for c in all_test_cases if c['type'] == 'Endpoint']) - stage_cases = len([c for c in all_test_cases if c['type'] == 'Stage']) - required_count = len(required_cases) - optional_count = len(optional_cases) - - stats_text = f"""测试用例统计: -总计 {total_cases} 个用例,其中端点用例 {endpoint_cases} 个,阶段用例 {stage_cases} 个。 -必须用例 {required_count} 个,非必须用例 {optional_count} 个。 -严格等级:{strictness_level}({severity_levels.get(strictness_level, 5)}级及以上为必须)。""" - - elements.append(to_para(stats_text, small_style)) - else: - elements.append(to_para("无测试用例执行记录。", normal_style)) - - elements.append(Spacer(1, 20)) - - # 失败用例详情部分 - if failed_test_cases: - elements.append(to_para("失败用例详情分析", heading_style, escape=False)) - elements.append(Spacer(1, 10)) - - # 按严重性分组失败用例 - critical_failures = [tc for tc in failed_test_cases if tc['severity'] == 'CRITICAL'] - high_failures = [tc for tc in failed_test_cases if tc['severity'] == 'HIGH'] - medium_failures = [tc for tc in failed_test_cases if tc['severity'] == 'MEDIUM'] - low_failures = [tc for tc in failed_test_cases if tc['severity'] == 'LOW'] - - failure_summary = f"""失败用例统计: -总计 {len(failed_test_cases)} 个失败用例,其中: -• 严重级别:{len(critical_failures)} 个 -• 高级别:{len(high_failures)} 个 -• 中级别:{len(medium_failures)} 个 -• 低级别:{len(low_failures)} 个 - -以下是详细的失败原因分析:""" - - elements.append(to_para(failure_summary, normal_style)) - elements.append(Spacer(1, 15)) - - # 详细失败用例列表 - for i, failed_case in enumerate(failed_test_cases, 1): - # 用例标题 - case_title = f"{i}. {failed_case['case_name']}" - elements.append(to_para(case_title, ParagraphStyle('case_title', parent=normal_style, fontSize=11, textColor=colors.darkred, spaceAfter=5))) - - # 用例基本信息 - case_info = f"""• 用例ID:{failed_case['case_id']} -• 所属端点:{failed_case['endpoint']} -• 严重级别:{failed_case['severity']} -• 执行状态:{failed_case['status']}""" - - elements.append(to_para(case_info, ParagraphStyle('case_info', parent=small_style, leftIndent=15, spaceAfter=5))) - - # 失败原因 - failure_reason = failed_case.get('message', '无详细错误信息') - if failure_reason: - elements.append(to_para("失败原因:", ParagraphStyle('failure_label', parent=normal_style, fontSize=10, textColor=colors.darkblue, leftIndent=15))) - - # 处理长文本,确保在PDF中正确显示 - if len(failure_reason) > 200: - # 对于很长的错误信息,进行适当的分段 - failure_reason = failure_reason[:200] + "..." - - elements.append(to_para(failure_reason, ParagraphStyle('failure_reason', parent=small_style, leftIndent=30, rightIndent=20, spaceAfter=10, textColor=colors.red))) - - # 添加分隔线 - if i < len(failed_test_cases): - elements.append(HRFlowable(width="80%", thickness=0.5, color=colors.lightgrey)) - elements.append(Spacer(1, 10)) - - elements.append(Spacer(1, 20)) - - elements.append(Spacer(1, 20)) - - # 测试情况说明 - elements.append(to_para("测试情况说明", heading_style, escape=False)) - - test_situation_text = f"""本次测试是对DMS领域数据管理服务V1.0版本下的{overall.get('endpoints_tested', 'N/A')}个API进行验证测试。 -测试:累计发现缺陷{overall.get('test_cases_failed', 0)}个。 -测试执行时间:{start_time_formatted} 至 {end_time_formatted} -测试环境:开发测试环境 -测试方法:自动化API合规性测试""" - - elements.append(to_para(test_situation_text, normal_style)) - elements.append(Spacer(1, 20)) - - # 测试结论 - elements.append(to_para("测试结论", heading_style, escape=False)) - - # 根据测试结果生成结论 - success_rate = overall.get('test_case_success_rate', '0%') - success_rate_num = float(success_rate.replace('%', '')) if success_rate != 'N/A' else 0 - - if success_rate_num >= 90: - conclusion_status = "通过" - conclusion_text = f"""本套领域数据服务已通过环境验证,系统可以正常运行。验收测试通过标准关于用例执行、DMS业务流相关文档等两个方面分析,该项目通过验收测试。 -测试用例成功率达到{success_rate},符合验收标准。""" - elif success_rate_num >= 70: - conclusion_status = "基本通过" - conclusion_text = f"""本套领域数据服务基本满足验收要求,但存在部分问题需要修复。测试用例成功率为{success_rate},建议修复失败用例后重新测试。""" - else: - conclusion_status = "不通过" - conclusion_text = f"""本套领域数据服务未达到验收标准,存在较多问题需要修复。测试用例成功率仅为{success_rate},需要全面检查和修复后重新测试。""" - - elements.append(to_para(conclusion_text, normal_style)) - elements.append(Spacer(1, 20)) - - # 检测依据 - elements.append(to_para("检测依据", heading_style, escape=False)) - - detection_basis_text = """集成开发应用支撑系统开放数据生态数据共享要求和评价第1部分:关于DMS领域数据服务的接口要求和测试细则。 -参考标准: -1. DMS数据管理系统API规范V1.0 -2. RESTful API设计规范 -3. 数据安全和隐私保护要求 -4. 系统集成测试标准""" - - elements.append(to_para(detection_basis_text, normal_style)) - elements.append(Spacer(1, 20)) - - # 报告生成信息 - elements.append(to_para("报告生成信息", heading_style, escape=False)) - generation_info_data = [ - [to_para("生成时间", escape=False), to_para(datetime.datetime.now().strftime('%Y年%m月%d日 %H:%M:%S'))], - [to_para("生成工具", escape=False), to_para("DMS合规性测试工具")], - [to_para("工具版本", escape=False), to_para("V1.0.0")], - [to_para("测试结论", escape=False), to_para(f"{conclusion_status}", escape=False)], - ] - generation_info_table = Table(generation_info_data, colWidths=[120, '*']) - generation_info_table.setStyle(TableStyle([ - ('GRID', (0,0), (-1,-1), 1, colors.grey), - ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), - ('BACKGROUND', (0,0), (0,-1), colors.lightgrey) - ])) - elements.append(generation_info_table) - - # 构建PDF - doc.build(elements) - logger.info(f"PDF报告已成功生成: {output_path}") - except Exception as e: - logger.error(f"构建PDF文档时出错: {e}", exc_info=True) - -@app.post("/run", - summary="执行API合规性测试", - description=""" - 执行API合规性测试的主要端点。 - - 支持三种API定义源: - - YAPI: 基于YAPI定义文件 - - Swagger/OpenAPI: 基于OpenAPI规范文件 - - DMS: 动态发现DMS服务的API - - 分页支持 - 对于DMS测试,支持分页获取API列表,避免内存溢出: - - `page_size`: 每页获取的API数量(默认1000) - - 返回详细的分页统计信息 - - LLM集成 - 可选择使用大语言模型生成测试数据: - - 智能生成请求体、路径参数、查询参数等 - - 提高测试覆盖率和数据多样性 - """, - response_model=TestResponse, - responses={ - 200: {"description": "测试执行成功"}, - 400: {"description": "请求参数错误", "model": ErrorResponse}, - 500: {"description": "服务器内部错误", "model": ErrorResponse} - }) -async def run_api_tests(config: TestConfig): - """ - 执行API合规性测试 - - - config: 测试配置,包含API定义源、测试参数等 - - returns: 测试结果,包含摘要信息和分页信息(如适用) - """ - try: - logger.info(f"Starting test run with configuration: {config.model_dump()}") - - # Convert Pydantic model to dict for compatibility - config_dict = config.model_dump(exclude_none=True) - - # Add hidden parameters with default values - hidden_defaults = { - "categories": [], - "tags": [], - "ignore_ssl": True, - "output": "./test_reports", - "generate_pdf": True, - "custom_test_cases_dir": "./custom_testcases", - "stages_dir": "./custom_stages", - "llm_api_key": "sk-lbGrsUPL1iby86h554FaE536C343435dAa9bA65967A840B2", - "llm_base_url": "https://aiproxy.petrotech.cnpc/v1", - "llm_model_name": "deepseek-v3", - "use_llm_for_request_body": False, - "use_llm_for_path_params": False, - "use_llm_for_query_params": False, - "use_llm_for_headers": False, - "verbose": False - } - - # Merge hidden defaults with config - config_dict.update(hidden_defaults) - - result = run_tests_logic(config_dict) - - if result['status'] == 'error': - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=result - ) - - return result - - except ValueError as e: - logger.error(f"Validation error: {e}") - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail={ - "status": "error", - "message": str(e) - } - ) - except Exception as e: - logger.error(f"An error occurred in the API endpoint: {e}", exc_info=True) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={ - "status": "error", - "message": str(e), - "traceback": traceback.format_exc() - } - ) - -@app.get("/reports/{report_id}", - summary="下载测试报告", - description="根据报告ID下载对应的测试报告文件") -async def download_report(report_id: str, file_type: str = "summary.json"): - """ - 下载测试报告文件 - - - report_id: 报告ID(通常是时间戳) - - file_type: 文件类型,可选值:summary.json, api_call_details.md - """ - try: - report_dir = Path("./test_reports") / report_id - file_path = report_dir / file_type - - if not file_path.exists(): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Report file not found: {file_type}" - ) - - return FileResponse( - path=str(file_path), - filename=file_type, - media_type='application/octet-stream' - ) - - except Exception as e: - logger.error(f"Error downloading report: {e}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Error downloading report: {str(e)}" - ) - -@app.get("/reports", - summary="列出所有测试报告", - description="获取所有可用的测试报告列表") -async def list_reports(): - """列出所有可用的测试报告""" - try: - reports_dir = Path("./test_reports") - if not reports_dir.exists(): - return {"reports": []} - - reports = [] - for report_dir in reports_dir.iterdir(): - if report_dir.is_dir(): - summary_file = report_dir / "summary.json" - if summary_file.exists(): - try: - with open(summary_file, 'r', encoding='utf-8') as f: - summary = json.load(f) - - reports.append({ - "id": report_dir.name, - "timestamp": report_dir.name, - "path": str(report_dir), - "summary": { - "endpoints_total": summary.get("endpoints_total", 0), - "endpoints_passed": summary.get("endpoints_passed", 0), - "endpoints_failed": summary.get("endpoints_failed", 0), - "test_cases_total": summary.get("test_cases_total", 0) - } - }) - except Exception as e: - logger.warning(f"Error reading summary for {report_dir.name}: {e}") - - # Sort by timestamp (newest first) - reports.sort(key=lambda x: x["timestamp"], reverse=True) - - return {"reports": reports} - - except Exception as e: - logger.error(f"Error listing reports: {e}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Error listing reports: {str(e)}" - ) - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description="DMS合规性测试工具 FastAPI服务器") - parser.add_argument("--host", default="0.0.0.0", help="服务器主机地址") - parser.add_argument("--port", type=int, default=5050, help="服务器端口") - parser.add_argument("--reload", action="store_true", help="启用自动重载(开发模式)") - parser.add_argument("--workers", type=int, default=1, help="工作进程数") - - args = parser.parse_args() - - logger.info(f"Starting FastAPI server on {args.host}:{args.port}") - logger.info(f"API文档地址: http://{args.host}:{args.port}/docs") - logger.info(f"ReDoc文档地址: http://{args.host}:{args.port}/redoc") - - uvicorn.run( - "fastapi_server:app", - host=args.host, - port=args.port, - reload=args.reload, - workers=args.workers if not args.reload else 1, - log_level="info" - ) diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/history_viewer.py b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/history_viewer.py deleted file mode 100644 index c0e1de9..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/history_viewer.py +++ /dev/null @@ -1,315 +0,0 @@ -import os -import sys -import json -import logging -import sqlite3 -from pathlib import Path -from datetime import timedelta -from werkzeug.security import generate_password_hash, check_password_hash -from flask import Flask, request, jsonify, send_from_directory, session, redirect, url_for, render_template, g, flash, get_flashed_messages, abort -from flask_cors import CORS -from functools import wraps -import markdown - -# --- PyInstaller Path Helpers --- -# For data files that should persist outside the bundle (e.g., database, reports) -if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): - # Running in a PyInstaller bundle - APP_ROOT = os.path.dirname(sys.executable) -else: - # Running in a normal Python environment - APP_ROOT = os.path.dirname(os.path.abspath(__file__)) - -template_dir = os.path.join(APP_ROOT, 'templates') -static_dir = os.path.join(APP_ROOT, 'static') -app = Flask(__name__, static_folder=static_dir, template_folder=template_dir) -CORS(app) - -# --- 基本配置 --- -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__name__) - -DATABASE = os.path.join(APP_ROOT, 'users.db') -REPORTS_DIR = os.path.join(APP_ROOT, 'test_reports') - -app.config['SECRET_KEY'] = os.urandom(24) -app.config['DATABASE'] = DATABASE -app.config['REPORTS_DIR'] = REPORTS_DIR -app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) - -os.makedirs(app.config['REPORTS_DIR'], exist_ok=True) - -# --- 数据库 Schema 和辅助函数 (与 flask_app.py 相同) --- -DB_SCHEMA = ''' -DROP TABLE IF EXISTS user; -CREATE TABLE user ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT UNIQUE NOT NULL, - password_hash TEXT NOT NULL -); -''' - -def get_db(): - db = getattr(g, '_database', None) - if db is None: - db = g._database = sqlite3.connect(app.config['DATABASE']) - db.row_factory = sqlite3.Row - return db - -@app.teardown_appcontext -def close_connection(exception): - db = getattr(g, '_database', None) - if db is not None: - db.close() - -def init_db(force_create=False): - if force_create or not os.path.exists(app.config['DATABASE']): - with app.app_context(): - db = get_db() - db.cursor().executescript(DB_SCHEMA) - db.commit() - logger.info("数据库已初始化!") - create_default_user() - else: - logger.info("数据库已存在。") - -def create_default_user(username="admin", password="7#Xq9$Lm*2!Pw@5"): - with app.app_context(): - db = get_db() - user = db.execute('SELECT * FROM user WHERE username = ?', (username,)).fetchone() - if user is None: - db.execute("INSERT INTO user (username, password_hash) VALUES (?, ?)", (username, generate_password_hash(password))) - db.commit() - logger.info(f"已创建默认用户: {username}") - else: - logger.info(f"默认用户 {username} 已存在。") - -@app.cli.command('init-db') -def init_db_command(): - init_db(force_create=True) - print("已初始化数据库。") - -# --- 用户认证 (与 flask_app.py 相同) --- -@app.route('/login', methods=('GET', 'POST')) -def login(): - if g.user: - return redirect(url_for('list_history')) - if request.method == 'POST': - username = request.form['username'] - password = request.form['password'] - db = get_db() - error = None - user = db.execute('SELECT * FROM user WHERE username = ?', (username,)).fetchone() - if user is None: - error = '用户名不存在。' - elif not check_password_hash(user['password_hash'], password): - error = '密码错误。' - if error is None: - session.clear() - session['user_id'] = user['id'] - session['username'] = user['username'] - session.permanent = True - return redirect(url_for('list_history')) - flash(error) - return render_template('login.html') - -@app.route('/logout') -def logout(): - session.clear() - flash('您已成功登出。') - return redirect(url_for('login')) - -def login_required(view): - @wraps(view) - def wrapped_view(**kwargs): - # if g.user is None: - # return redirect(url_for('login')) - return view(**kwargs) - return wrapped_view - -@app.before_request -def load_logged_in_user(): - user_id = session.get('user_id') - if user_id is None: - g.user = None - else: - g.user = get_db().execute('SELECT * FROM user WHERE id = ?', (user_id,)).fetchone() - -# --- LLM配置视图 --- -CRITERIA_FILE_PATH = os.path.join(APP_ROOT, 'custom_testcases', 'llm', 'compliance_criteria.json') - -@app.route('/llm-config', methods=['GET', 'POST']) -@login_required -def llm_config(): - criteria_for_template = [] - file_exists = os.path.exists(CRITERIA_FILE_PATH) - - if request.method == 'POST': - # 从表单获取所有名为'criteria'的输入项,作为一个列表 - criteria_list = request.form.getlist('criteria') - # 过滤掉用户可能提交的空规则 - criteria_list = [item.strip() for item in criteria_list if item.strip()] - - try: - # 将规则列表格式化为美观的JSON并保存 - pretty_content = json.dumps(criteria_list, indent=2, ensure_ascii=False) - with open(CRITERIA_FILE_PATH, 'w', encoding='utf-8') as f: - f.write(pretty_content) - flash('LLM合规性标准已成功保存!', 'success') - except Exception as e: - flash(f'保存文件时发生未知错误: {e}', 'error') - - # 无论是GET还是POST请求后,都重新从文件中读取最新的规则列表用于显示 - if file_exists: - try: - with open(CRITERIA_FILE_PATH, 'r', encoding='utf-8') as f: - criteria_for_template = json.load(f) - # 确保文件内容确实是一个列表 - if not isinstance(criteria_for_template, list): - flash('配置文件格式错误:内容应为JSON数组。已重置为空列表。', 'error') - criteria_for_template = [] - except Exception as e: - flash(f'读取配置文件时出错: {e}', 'error') - criteria_for_template = [] - - # 准备一个用于页面展示的示例API信息 - example_api_info = { - "path_template": "/api/dms/instance/v1/message/push/myschema/1.0", - "method": "POST", - "title": "数据推送接口", - "description": "用于向系统推送标准格式的数据。", - "schema_request_body": {"...": "... (此处为请求体Schema定义)"}, - "instance_url": "http://example.com/api/dms/instance/v1/message/push/myschema/1.0", - "instance_request_headers": {"X-Tenant-ID": "tenant-001", "...": "..."}, - "instance_request_body": {"id": "123", "data": "example"}, - "instance_response_status": 200, - "instance_response_body": {"code": 0, "message": "success", "data": True} - } - - return render_template('llm_config.html', criteria=criteria_for_template, file_exists=file_exists, example_api_info=json.dumps(example_api_info, indent=2, ensure_ascii=False)) - -# --- 文件下载路由 --- -@app.route('/download//') -@login_required -def download_report(run_id, filename): - """安全地提供指定运行记录中的报告文件下载。""" - # 清理输入,防止目录遍历攻击 - run_id_safe = Path(run_id).name - filename_safe = Path(filename).name - - reports_dir = Path(app.config['REPORTS_DIR']).resolve() - run_dir = (reports_dir / run_id_safe).resolve() - - # 安全检查:确保请求的目录是REPORTS_DIR的子目录 - if not run_dir.is_dir() or run_dir.parent != reports_dir: - abort(404, "找不到指定的测试记录或权限不足。") - - return send_from_directory(run_dir, filename_safe, as_attachment=True) - -# --- 新增:PDF文件预览路由 --- -@app.route('/view_pdf/') -@login_required -def view_pdf_report(run_id): - """安全地提供PDF报告文件以内联方式查看。""" - run_id_safe = Path(run_id).name - filename_safe = "report_cn.pdf" - - reports_dir = Path(app.config['REPORTS_DIR']).resolve() - run_dir = (reports_dir / run_id_safe).resolve() - - # 安全检查 - if not run_dir.is_dir() or run_dir.parent != reports_dir: - abort(404, "找不到指定的测试记录或权限不足。") - - pdf_path = run_dir / filename_safe - if not pdf_path.exists(): - abort(404, "未找到PDF报告文件。") - - return send_from_directory(run_dir, filename_safe) - -# --- 历史记录视图 --- -@app.route('/') -@login_required -def list_history(): - history = [] - reports_path = Path(app.config['REPORTS_DIR']) - if not reports_path.is_dir(): - flash('报告目录不存在。') - return render_template('history.html', history=[]) - - # 获取所有子目录(即测试运行记录) - run_dirs = [d for d in reports_path.iterdir() if d.is_dir()] - # 按名称(时间戳)降序排序 - run_dirs.sort(key=lambda x: x.name, reverse=True) - - for run_dir in run_dirs: - summary_path = run_dir / 'summary.json' - details_path = run_dir / 'api_call_details.md' - run_info = {'id': run_dir.name, 'summary': None, 'has_details': details_path.exists()} - - if summary_path.exists(): - try: - with open(summary_path, 'r', encoding='utf-8') as f: - summary_data = json.load(f) - run_info['summary'] = summary_data - except (json.JSONDecodeError, IOError) as e: - logger.error(f"无法读取或解析摘要文件 {summary_path}: {e}") - run_info['summary'] = {'error': '无法加载摘要'} - - history.append(run_info) - - return render_template('history.html', history=history) - -@app.route('/details/') -@login_required -def show_details(run_id): - run_id = Path(run_id).name # Sanitize input - run_dir = Path(app.config['REPORTS_DIR']) / run_id - - if not run_dir.is_dir(): - return "找不到指定的测试记录。", 404 - - summary_path = run_dir / 'summary.json' - details_path = run_dir / 'api_call_details.md' - pdf_path = run_dir / 'report_cn.pdf' # 新增PDF路径 - - summary_content = "{}" - details_content = "### 未找到API调用详情报告" - - has_pdf_report = pdf_path.exists() # 检查PDF是否存在 - has_md_report = details_path.exists() # 检查MD报告是否存在 - - if summary_path.exists(): - try: - with open(summary_path, 'r', encoding='utf-8') as f: - summary_data = json.load(f) - summary_content = json.dumps(summary_data, indent=2, ensure_ascii=False) - except Exception as e: - summary_content = f"加载摘要文件出错: {e}" - - if has_md_report: - try: - with open(details_path, 'r', encoding='utf-8') as f: - # 将Markdown转换为HTML - details_content = markdown.markdown(f.read(), extensions=['fenced_code', 'tables', 'def_list', 'attr_list']) - except Exception as e: - details_content = f"加载详情文件出错: {e}" - - return render_template('history_detail.html', - run_id=run_id, - summary_content=summary_content, - details_content=details_content, - has_pdf_report=has_pdf_report, - has_md_report=has_md_report) - - -# --- 根路径重定向 --- -@app.route('/index') -def index_redirect(): - return redirect(url_for('list_history')) - -if __name__ == '__main__': - # 首次运行时确保数据库和用户存在 - init_db() - # 使用5051端口避免与api_server.py冲突 - app.run(debug=True, host='0.0.0.0', port=5051) \ No newline at end of file diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/requirements.txt b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/requirements.txt deleted file mode 100644 index 90e83ac..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -pydantic>=2.0.0,<3.0.0 -PyYAML>=6.0,<7.0 -jsonschema>=4.0.0,<5.0.0 -requests>=2.20.0,<3.0.0 -flask>=2.0.0,<3.0.0 # 用于模拟服务器 -numpy>=1.20.0,<2.0.0 # 用于数值计算 - -# 用于 OpenAPI/Swagger 解析 (可选, 如果输入解析器需要) -openapi-spec-validator>=0.5.0,<0.6.0 -prance[osv]>=23.0.0,<24.0.0 - -# 用于 API Linting (可选, 如果规则库需要集成 Spectral-like 功能) -# pyaml-env>=1.0.0,<2.0.0 # 如果 linting 规则是 yaml 且用到了环境变量 - -# 测试框架 (可选, 推荐) -# pytest>=7.0.0,<8.0.0 -# pytest-cov>=4.0.0,<5.0.0 -# httpx>=0.20.0,<0.28.0 # for testing API calls - -Flask-Cors>=3.0 -markdown -reportlab>=3.6.0 # For PDF report generation \ No newline at end of file diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/set-permissions.sh b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/set-permissions.sh deleted file mode 100644 index 934aaff..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/set-permissions.sh +++ /dev/null @@ -1,3 +0,0 @@ -#/bin/bash -chmod +x *.sh -echo "Permissions set for shell scripts" diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.bat b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.bat deleted file mode 100644 index 8662b23..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off -echo Starting DMS Compliance Tool... -echo Loading pre-built Docker image... -docker load -i docker-image.tar -echo Starting services... -docker compose up -d -echo Services started -echo FastAPI Server: http://localhost:5050 -echo History Viewer: http://localhost:5051 -pause diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.sh b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.sh deleted file mode 100644 index 08957d5..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/start.sh +++ /dev/null @@ -1,10 +0,0 @@ -#/bin/bash - -echo "Starting DMS Compliance Tool..." -echo "Loading pre-built Docker image..." -docker load -i docker-image.tar -echo "Starting services..." -docker compose up -d -echo "Services started" -echo "FastAPI Server: http://localhost:5050" -echo "History Viewer: http://localhost:5051" diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.bat b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.bat deleted file mode 100644 index ae513aa..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -docker compose down -echo Services stopped. -pause diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.sh b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.sh deleted file mode 100644 index 9b33606..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/stop.sh +++ /dev/null @@ -1,5 +0,0 @@ -#/bin/bash - -echo "Stopping DMS Compliance Tool..." -docker compose down -echo "Services stopped." diff --git a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/supervisord.conf b/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/supervisord.conf deleted file mode 100644 index 3fc5c54..0000000 --- a/dms-compliance-dual-amd64-windows-0周三/0/2-165956/2-165956/supervisord.conf +++ /dev/null @@ -1,20 +0,0 @@ -[supervisord] -nodaemon=true -logfile=/var/log/supervisor/supervisord.log -user=root - -[program:api_server] -command=python -m uvicorn fastapi_server:app --host 0.0.0.0 --port 5050 -directory=/app -autostart=true -autorestart=true -redirect_stderr=true -stdout_logfile=/var/log/supervisor/api_server.log - -[program:history_viewer] -command=python history_viewer.py -directory=/app -autostart=true -autorestart=true -redirect_stderr=true -stdout_logfile=/var/log/supervisor/history_viewer.log diff --git a/fix-existing-package.bat b/fix-existing-package.bat deleted file mode 100644 index 9dbda80..0000000 --- a/fix-existing-package.bat +++ /dev/null @@ -1,139 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -echo === Fix Existing FastAPI Package === -echo This script fixes the missing assets directory issue in existing packages -echo. - -REM Find the most recent package directory -for /f "delims=" %%i in ('dir /b /ad /o-d dms-compliance-fastapi-amd64-windows-* 2^>nul') do ( - set "PACKAGE_DIR=%%i" - goto :found -) - -echo [ERROR] No package directory found matching pattern: dms-compliance-fastapi-amd64-windows-* -echo Please make sure you have a generated package directory. -pause -exit /b 1 - -:found -echo [INFO] Found package directory: %PACKAGE_DIR% -echo. - -REM Check if assets directory exists in current location -if not exist "assets" ( - echo [ERROR] Assets directory not found in current location - echo Please run this script from the same directory where you ran the build script - pause - exit /b 1 -) - -echo [INFO] Assets directory found, proceeding with fix... -echo. - -REM Stop any running containers -echo [INFO] Stopping any running containers... -cd /d "%PACKAGE_DIR%" -docker compose down >nul 2>&1 -cd /d ".." - -REM Load the existing image to check if it exists -echo [INFO] Checking existing Docker image... -cd /d "%PACKAGE_DIR%" -if exist "docker-image.tar" ( - docker load -i docker-image.tar >nul 2>&1 - echo [INFO] Existing image loaded -) else ( - echo [WARNING] No docker-image.tar found -) -cd /d ".." - -REM Create a new build directory with assets -echo [INFO] Creating fixed build directory... -set "FIX_DIR=%PACKAGE_DIR%-fixed" -if exist "%FIX_DIR%" rmdir /s /q "%FIX_DIR%" -mkdir "%FIX_DIR%" - -REM Copy everything from the original package -echo [INFO] Copying original package files... -robocopy "%PACKAGE_DIR%" "%FIX_DIR%" /E /XF docker-image.tar >nul 2>&1 - -REM Copy the assets directory -echo [INFO] Adding missing assets directory... -robocopy "assets" "%FIX_DIR%\assets" /E >nul 2>&1 - -REM Copy other required files that might be missing -if exist "ddms_compliance_suite" robocopy "ddms_compliance_suite" "%FIX_DIR%\ddms_compliance_suite" /E /XD __pycache__ /XF *.pyc >nul 2>&1 -if exist "custom_stages" robocopy "custom_stages" "%FIX_DIR%\custom_stages" /E /XD __pycache__ /XF *.pyc >nul 2>&1 -if exist "custom_testcases" robocopy "custom_testcases" "%FIX_DIR%\custom_testcases" /E /XD __pycache__ /XF *.pyc >nul 2>&1 - -REM Rebuild the Docker image -echo [INFO] Rebuilding Docker image with assets... -cd /d "%FIX_DIR%" - -REM Check if Dockerfile exists -if not exist "Dockerfile" ( - echo [ERROR] Dockerfile not found in package directory - cd /d ".." - pause - exit /b 1 -) - -echo [INFO] Building fixed Docker image... -set DOCKER_SCOUT_NO_ANALYTICS=true -set DOCKER_BUILDX_NO_DEFAULT_ATTESTATIONS=true - -docker build -t compliance-dms-windows:latest . 2>&1 -if errorlevel 1 ( - echo [ERROR] Docker build failed - cd /d ".." - pause - exit /b 1 -) - -echo [SUCCESS] Docker image rebuilt successfully! - -REM Save the new image -echo [INFO] Saving fixed Docker image... -docker save compliance-dms-windows:latest -o docker-image.tar - -REM Test the fixed image -echo [INFO] Testing the fixed image... -docker run --rm -d -p 5052:5050 --name test-fixed-container compliance-dms-windows:latest -timeout /t 10 /nobreak >nul - -REM Check if container is running and test the API -docker ps | findstr test-fixed-container >nul -if errorlevel 1 ( - echo [WARNING] Test container failed to start -) else ( - echo [SUCCESS] Test container is running - - REM Test if assets are accessible - docker exec test-fixed-container ls -la /app/assets/doc/dms/ >nul 2>&1 - if errorlevel 1 ( - echo [WARNING] Assets directory not found in container - ) else ( - echo [SUCCESS] Assets directory found in container - docker exec test-fixed-container cat /app/assets/doc/dms/domain.json | head -5 - ) -) - -REM Clean up test container -docker stop test-fixed-container >nul 2>&1 -docker rm test-fixed-container >nul 2>&1 - -cd /d ".." - -echo. -echo === Fix Complete === -echo [SUCCESS] Fixed package created: %FIX_DIR% -echo. -echo To use the fixed package: -echo 1. cd %FIX_DIR% -echo 2. start.bat -echo 3. Test with: curl -X POST http://localhost:5050/run -H "Content-Type: application/json" -d "{\"mode\":\"dms\",\"base_url\":\"http://127.0.0.1:5001\"}" -echo. -echo The fixed package should now be able to find the assets/doc/dms/domain.json file. -echo. -pause diff --git a/fix-line-endings.sh b/fix-line-endings.sh deleted file mode 100644 index 090d0cb..0000000 --- a/fix-line-endings.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash - -# 修复脚本行结束符问题 -# 将Windows格式的CRLF转换为Unix格式的LF - -echo "=== 修复脚本行结束符工具 ===" -echo "" - -# 检查是否有dos2unix命令 -if command -v dos2unix &> /dev/null; then - echo "[信息] 使用dos2unix工具修复行结束符" - - # 修复原始脚本 - if [[ -f "create-compose-package-multiplatform.sh" ]]; then - echo "[信息] 修复 create-compose-package-multiplatform.sh" - dos2unix create-compose-package-multiplatform.sh - chmod +x create-compose-package-multiplatform.sh - fi - - # 修复WSL版本脚本 - if [[ -f "create-compose-package-wsl.sh" ]]; then - echo "[信息] 修复 create-compose-package-wsl.sh" - dos2unix create-compose-package-wsl.sh - chmod +x create-compose-package-wsl.sh - fi - -elif command -v sed &> /dev/null; then - echo "[信息] 使用sed工具修复行结束符" - - # 使用sed移除\r字符 - if [[ -f "create-compose-package-multiplatform.sh" ]]; then - echo "[信息] 修复 create-compose-package-multiplatform.sh" - sed -i 's/\r$//' create-compose-package-multiplatform.sh - chmod +x create-compose-package-multiplatform.sh - fi - - if [[ -f "create-compose-package-wsl.sh" ]]; then - echo "[信息] 修复 create-compose-package-wsl.sh" - sed -i 's/\r$//' create-compose-package-wsl.sh - chmod +x create-compose-package-wsl.sh - fi - -elif command -v tr &> /dev/null; then - echo "[信息] 使用tr工具修复行结束符" - - # 使用tr删除\r字符 - if [[ -f "create-compose-package-multiplatform.sh" ]]; then - echo "[信息] 修复 create-compose-package-multiplatform.sh" - tr -d '\r' < create-compose-package-multiplatform.sh > temp_file && mv temp_file create-compose-package-multiplatform.sh - chmod +x create-compose-package-multiplatform.sh - fi - - if [[ -f "create-compose-package-wsl.sh" ]]; then - echo "[信息] 修复 create-compose-package-wsl.sh" - tr -d '\r' < create-compose-package-wsl.sh > temp_file && mv temp_file create-compose-package-wsl.sh - chmod +x create-compose-package-wsl.sh - fi - -else - echo "[警告] 未找到合适的工具来修复行结束符" - echo "[提示] 请安装 dos2unix 工具: sudo apt-get install dos2unix" - echo "" - echo "或者手动运行以下命令:" - echo "sed -i 's/\r$//' create-compose-package-multiplatform.sh" - echo "sed -i 's/\r$//' create-compose-package-wsl.sh" - echo "chmod +x *.sh" - exit 1 -fi - -echo "" -echo "[成功] 行结束符修复完成" -echo "" -echo "现在可以运行以下脚本:" -echo "- ./create-compose-package-multiplatform.sh (原始多平台版本)" -echo "- ./create-compose-package-wsl.sh (WSL优化版本)" -echo "- create-compose-package-windows.bat (Windows批处理版本)" -echo "" - -# 验证脚本是否可执行 -echo "=== 验证脚本 ===" -for script in create-compose-package-multiplatform.sh create-compose-package-wsl.sh; do - if [[ -f "$script" ]]; then - if [[ -x "$script" ]]; then - echo "[✓] $script 可执行" - else - echo "[✗] $script 不可执行" - chmod +x "$script" - echo "[修复] 已设置 $script 为可执行" - fi - - # 检查是否还有\r字符 - if grep -q $'\r' "$script" 2>/dev/null; then - echo "[警告] $script 仍包含Windows行结束符" - else - echo "[✓] $script 行结束符正常" - fi - fi -done - -echo "" -echo "修复完成!" diff --git a/run-demo.sh b/run-demo.sh deleted file mode 100644 index 6804c41..0000000 --- a/run-demo.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# 自动运行演示脚本 -echo "=== 自动运行WSL演示脚本 ===" - -# 使用expect或者直接传递输入 -{ - echo "1" # 选择FastAPI - echo "0" # 自动检测平台 - echo "y" # 确认构建 -} | bash demo-wsl-script.sh