From 46febb97cb6ca692aaa1d0dcb919d22a4e0fc3e9 Mon Sep 17 00:00:00 2001 From: yorn <1057707203@qq.com> Date: Mon, 28 Jul 2025 00:25:57 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=A5=BD=E6=AD=A3=E5=BC=8F?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E7=9A=84nginx=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DYNAMIC-CLIENT-CONFIG.md | 213 ------------- NGINX-CONFIG-ANALYSIS.md | 429 ------------------------- UBUNTU-NGINX-TEST-GUIDE.md | 422 ------------------------ nginx.conf | 64 ++-- ubuntu-nginx-setup.sh | 342 -------------------- 交叉评查系统完整文档.md | 634 ------------------------------------- 6 files changed, 23 insertions(+), 2081 deletions(-) delete mode 100644 DYNAMIC-CLIENT-CONFIG.md delete mode 100644 NGINX-CONFIG-ANALYSIS.md delete mode 100644 UBUNTU-NGINX-TEST-GUIDE.md delete mode 100644 ubuntu-nginx-setup.sh delete mode 100644 交叉评查系统完整文档.md diff --git a/DYNAMIC-CLIENT-CONFIG.md b/DYNAMIC-CLIENT-CONFIG.md deleted file mode 100644 index f2c49ca..0000000 --- a/DYNAMIC-CLIENT-CONFIG.md +++ /dev/null @@ -1,213 +0,0 @@ -# 动态客户端配置使用指南 - -## 🎯 概述 - -本项目支持通过Nginx代理自动识别客户端,无需手动设置环境变量。Nginx会在请求头中传递 `X-Client-ID`,前端应用会自动读取并使用对应的配置。 - -## 🔧 工作原理 - -``` -用户访问 → Nginx代理 → 添加X-Client-ID头部 → 前端应用 → 动态获取配置 -``` - -### 端口映射 - -| 端口 | 客户端ID | 说明 | -|------|----------|------| -| 5174 | client-a | 客户端A | -| 5175 | client-b | 客户端B | -| 5176 | client-c | 客户端C | -| 5177 | client-d | 客户端D | - -## 📁 相关文件 - -### 核心配置文件 -- `app/config/api-config.ts` - API配置管理(已更新支持动态配置) -- `nginx-ubuntu-optimized.conf` - Nginx配置文件 - -### 新增工具文件 -- `app/utils/client-detection.ts` - 客户端检测工具函数 -- `app/examples/dynamic-config-usage.ts` - 使用示例 -- `app/routes/test.client-config.tsx` - 配置测试页面 - -## 🚀 使用方法 - -### 1. 在Remix Loader中使用 - -```typescript -import type { LoaderFunctionArgs } from '@remix-run/node'; -import { getApiConfig, getApiBaseUrl } from '~/config/api-config'; - -export async function loader({ request }: LoaderFunctionArgs) { - // 方法1: 获取完整配置 - const config = getApiConfig(request); - - // 方法2: 获取特定配置项 - const baseUrl = getApiBaseUrl(request); - - // 使用动态配置进行API调用 - const response = await fetch(`${baseUrl}/api/data`); - const data = await response.json(); - - return { data }; -} -``` - -### 2. 在Action中使用 - -```typescript -import type { ActionFunctionArgs } from '@remix-run/node'; -import { getUploadUrl } from '~/config/api-config'; - -export async function action({ request }: ActionFunctionArgs) { - const uploadUrl = getUploadUrl(request); - const formData = await request.formData(); - - const response = await fetch(uploadUrl, { - method: 'POST', - body: formData - }); - - return await response.json(); -} -``` - -### 3. OAuth重定向URL生成 - -```typescript -import { getOAuthConfig } from '~/config/api-config'; - -export function generateOAuthUrl(request: Request): string { - const oauthConfig = getOAuthConfig(request); - - const params = new URLSearchParams({ - client_id: oauthConfig.clientId, - redirect_uri: oauthConfig.redirectUri, - response_type: 'code' - }); - - return `${oauthConfig.serverUrl}/oauth/authorize?${params.toString()}`; -} -``` - -## 🧪 测试验证 - -### 1. 访问测试页面 - -通过不同端口访问测试页面来验证配置: - -```bash -# 客户端A -http://localhost:5174/test/client-config - -# 客户端B -http://localhost:5175/test/client-config - -# 客户端C -http://localhost:5176/test/client-config - -# 客户端D -http://localhost:5177/test/client-config -``` - -### 2. 检查配置状态 - -测试页面会显示: -- ✅ Nginx代理是否正常工作 -- ✅ 客户端识别是否成功 -- ✅ 配置匹配是否正确 - -### 3. 验证请求头 - -在浏览器开发者工具中检查网络请求,确认包含: -- `X-Client-ID`: 对应的客户端标识 -- `X-Original-Port`: 原始端口号 -- `X-Forwarded-Port`: 转发端口号 - -## 🔍 调试信息 - -### 查看Nginx日志 - -```bash -# 查看客户端A的访问日志 -sudo tail -f /var/log/nginx/client-a-access.log - -# 查看客户端D的访问日志 -sudo tail -f /var/log/nginx/client-d-access.log -``` - -### 使用调试工具 - -```typescript -import { detectClientFromRequest, getRequestDebugInfo } from '~/utils/client-detection'; - -export async function loader({ request }: LoaderFunctionArgs) { - const clientId = detectClientFromRequest(request); - const debugInfo = getRequestDebugInfo(request); - - console.log('🔧 调试信息:', { clientId, debugInfo }); - - // ... 其他逻辑 -} -``` - -## ⚠️ 注意事项 - -### 1. 兼容性 -- 保持了原有的静态配置导出,确保现有代码不受影响 -- 新的动态配置函数是可选的,可以逐步迁移 - -### 2. 环境要求 -- 需要Nginx正确配置并运行 -- 确保 `nginx-ubuntu-optimized.conf` 配置文件已应用 - -### 3. 错误处理 -- 如果无法检测到客户端ID,会回退到默认值 'main' -- 如果Nginx未正确配置,仍可通过环境变量设置客户端ID - -## 🔄 迁移指南 - -### 从环境变量迁移到动态配置 - -**之前(需要设置环境变量):** -```bash -# .env文件 -NEXT_PUBLIC_CLIENT_ID=client-d -``` - -**现在(自动检测):** -```typescript -// 在loader中 -const config = getApiConfig(request); // 自动从请求头获取客户端ID -``` - -### 更新现有代码 - -1. **替换静态配置调用:** - ```typescript - // 旧方式 - import { API_BASE_URL } from '~/config/api-config'; - - // 新方式 - import { getApiBaseUrl } from '~/config/api-config'; - const baseUrl = getApiBaseUrl(request); - ``` - -2. **在loader/action中传递request:** - ```typescript - // 确保所有需要配置的地方都能访问到request对象 - export async function loader({ request }: LoaderFunctionArgs) { - const config = getApiConfig(request); - // ... - } - ``` - -## 🎉 优势 - -1. **无需手动配置** - 不再需要为每个客户端设置环境变量 -2. **自动识别** - 通过访问端口自动识别客户端 -3. **动态切换** - 同一个应用实例支持多个客户端 -4. **向后兼容** - 现有代码无需修改即可继续工作 -5. **易于调试** - 提供完整的调试信息和测试页面 - -现在你可以直接通过 `http://localhost:5177` 访问应用,系统会自动识别为 `client-d` 并使用对应的配置! \ No newline at end of file diff --git a/NGINX-CONFIG-ANALYSIS.md b/NGINX-CONFIG-ANALYSIS.md deleted file mode 100644 index e9cb872..0000000 --- a/NGINX-CONFIG-ANALYSIS.md +++ /dev/null @@ -1,429 +0,0 @@ -# Nginx配置分析与优化建议 - -## 原始配置分析 - -### 当前配置文件:`nginx.conf` - -```nginx -server { - listen 5174; - server_name localhost; - - location / { - proxy_pass http://172.16.0.34:5173; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -# ... 其他三个相同的server块 -``` - -## 问题识别与分析 - -### 🔴 严重问题 - -#### 1. **缺少客户端标识机制** -**问题**:所有端口代理到同一个后端,但没有传递客户端标识信息 -**影响**:应用无法区分来自不同端口的请求,无法实现多客户端配置切换 -**解决方案**: -```nginx -# 添加客户端标识变量和头部 -set $client_id "client-a"; # 每个server块设置不同的client_id -proxy_set_header X-Client-ID $client_id; -proxy_set_header X-Original-Port $server_port; -``` - -#### 2. **缺少上游服务器配置** -**问题**:直接在proxy_pass中硬编码后端地址 -**影响**:无法实现负载均衡、健康检查、连接池等高级功能 -**解决方案**: -```nginx -upstream vite_dev_server { - server 172.16.0.34:5173; - keepalive 32; # 连接池 -} -``` - -#### 3. **缺少开发环境特殊配置** -**问题**:没有配置WebSocket支持,影响Vite热重载功能 -**影响**:开发时热重载可能不工作 -**解决方案**: -```nginx -proxy_set_header Upgrade $http_upgrade; -proxy_set_header Connection "upgrade"; -proxy_http_version 1.1; -``` - -### 🟡 重要问题 - -#### 4. **缺少日志配置** -**问题**:没有配置访问日志和错误日志 -**影响**:无法监控和调试请求 -**解决方案**: -```nginx -access_log /var/log/nginx/client-a-access.log; -error_log /var/log/nginx/client-a-error.log warn; -``` - -#### 5. **缺少超时配置** -**问题**:使用默认超时设置,可能导致请求超时 -**影响**:长时间请求可能被意外中断 -**解决方案**: -```nginx -proxy_connect_timeout 30s; -proxy_send_timeout 30s; -proxy_read_timeout 30s; -``` - -#### 6. **缺少缓冲控制** -**问题**:默认启用代理缓冲,影响实时性 -**影响**:开发环境下可能影响实时更新 -**解决方案**: -```nginx -proxy_buffering off; -proxy_cache off; -proxy_request_buffering off; -``` - -### 🟢 改进建议 - -#### 7. **缺少健康检查端点** -**建议**:添加健康检查端点便于监控 -**好处**:可以快速检测服务状态 -**实现**: -```nginx -location /health { - access_log off; - return 200 "Client A (Port 5174) - OK\n"; - add_header Content-Type text/plain; -} -``` - -#### 8. **缺少CORS配置** -**建议**:添加CORS头部支持跨域请求 -**好处**:支持前端开发时的跨域请求 -**实现**: -```nginx -add_header Access-Control-Allow-Origin * always; -add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; -``` - -#### 9. **缺少文件上传大小限制** -**建议**:设置合理的文件上传大小限制 -**好处**:防止大文件攻击,提高安全性 -**实现**: -```nginx -client_max_body_size 100M; -``` - -#### 10. **缺少安全头部** -**建议**:添加基本的安全头部 -**好处**:提高应用安全性 -**实现**: -```nginx -add_header X-Frame-Options SAMEORIGIN always; -add_header X-Content-Type-Options nosniff always; -add_header X-XSS-Protection "1; mode=block" always; -``` - -## 详细优化方案 - -### 1. 客户端标识传递机制 - -**原理**:通过Nginx变量和请求头传递客户端信息 - -```nginx -server { - listen 5174; - set $client_id "client-a"; # 设置客户端标识 - - location / { - proxy_pass http://vite_dev_server; - # 传递客户端标识 - proxy_set_header X-Client-ID $client_id; - proxy_set_header X-Original-Port $server_port; - proxy_set_header X-Forwarded-Port $server_port; - } -} -``` - -**应用端接收**: -```typescript -// 在Remix loader或action中 -export const loader: LoaderFunction = async ({ request }) => { - const clientId = request.headers.get('X-Client-ID') || 'main'; - const config = getConfigForClient(clientId); - return json({ config }); -}; -``` - -### 2. 上游服务器配置优化 - -**连接池配置**: -```nginx -upstream vite_dev_server { - server 172.16.0.34:5173; - keepalive 32; # 保持32个长连接 - keepalive_requests 100; # 每个连接最多处理100个请求 - keepalive_timeout 60s; # 连接空闲60秒后关闭 -} -``` - -**健康检查**(需要nginx-plus或第三方模块): -```nginx -upstream vite_dev_server { - server 172.16.0.34:5173 max_fails=3 fail_timeout=30s; - # 3次失败后标记为不可用,30秒后重试 -} -``` - -### 3. 开发环境特殊配置 - -**WebSocket支持**: -```nginx -location / { - proxy_pass http://vite_dev_server; - - # WebSocket支持(Vite HMR必需) - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_http_version 1.1; - - # 禁用缓冲以支持实时更新 - proxy_buffering off; - proxy_cache off; - proxy_request_buffering off; -} -``` - -**API请求特殊处理**: -```nginx -location /api/ { - proxy_pass http://vite_dev_server; - - # API请求不需要WebSocket支持 - proxy_set_header X-Client-ID $client_id; - - # API请求超时配置 - proxy_connect_timeout 10s; - proxy_send_timeout 60s; - proxy_read_timeout 60s; -} -``` - -### 4. 日志配置优化 - -**自定义日志格式**: -```nginx -log_format client_access '$remote_addr - $remote_user [$time_local] ' - '"$request" $status $body_bytes_sent ' - '"$http_referer" "$http_user_agent" ' - 'client_id="$client_id" original_port="$server_port" ' - 'response_time=$request_time'; - -access_log /var/log/nginx/client-a-access.log client_access; -``` - -**日志轮转配置**: -```bash -# /etc/logrotate.d/nginx-clients -/var/log/nginx/client-*-access.log { - daily - missingok - rotate 52 - compress - delaycompress - notifempty - create 644 www-data www-data - postrotate - systemctl reload nginx - endscript -} -``` - -### 5. 性能优化配置 - -**缓存配置**: -```nginx -# 静态资源缓存 -location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { - proxy_pass http://vite_dev_server; - proxy_cache_valid 200 1h; - add_header X-Cache-Status $upstream_cache_status; -} -``` - -**压缩配置**: -```nginx -# 在http块中 -gzip on; -gzip_vary on; -gzip_min_length 1024; -gzip_types - text/plain - text/css - application/json - application/javascript - text/xml - application/xml - application/xml+rss - text/javascript; -``` - -### 6. 安全配置 - -**基础安全头部**: -```nginx -# 开发环境相对宽松的安全配置 -add_header X-Frame-Options SAMEORIGIN always; -add_header X-Content-Type-Options nosniff always; -add_header X-XSS-Protection "1; mode=block" always; -add_header Referrer-Policy "strict-origin-when-cross-origin" always; -``` - -**请求限制**: -```nginx -# 限制请求频率(可选,开发环境通常不需要) -limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; - -location /api/ { - limit_req zone=api burst=20 nodelay; - proxy_pass http://vite_dev_server; -} -``` - -## 配置模板对比 - -### 原始配置(简化版) -```nginx -server { - listen 5174; - server_name localhost; - location / { - proxy_pass http://172.16.0.34:5173; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -### 优化后配置(完整版) -```nginx -upstream vite_dev_server { - server 172.16.0.34:5173; - keepalive 32; -} - -log_format client_access '$remote_addr - $remote_user [$time_local] ' - '"$request" $status $body_bytes_sent ' - 'client_id="$client_id" port="$server_port"'; - -server { - listen 5174; - server_name localhost 127.0.0.1; - - set $client_id "client-a"; - - access_log /var/log/nginx/client-a-access.log client_access; - error_log /var/log/nginx/client-a-error.log warn; - - location / { - proxy_pass http://vite_dev_server; - - # 基础代理头部 - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # 客户端标识头部 - proxy_set_header X-Client-ID $client_id; - proxy_set_header X-Original-Port $server_port; - - # WebSocket支持 - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_http_version 1.1; - - # 超时配置 - proxy_connect_timeout 30s; - proxy_send_timeout 30s; - proxy_read_timeout 30s; - - # 缓冲控制 - proxy_buffering off; - proxy_cache off; - - # 文件上传限制 - client_max_body_size 100M; - } - - location /health { - access_log off; - return 200 "Client A - OK"; - add_header Content-Type text/plain; - } -} -``` - -## 实施优先级 - -### 🔴 高优先级(必须实施) -1. **客户端标识传递** - 核心功能需求 -2. **WebSocket支持** - 开发体验必需 -3. **上游服务器配置** - 性能和可维护性 - -### 🟡 中优先级(建议实施) -4. **日志配置** - 监控和调试 -5. **超时配置** - 稳定性 -6. **健康检查** - 运维便利 - -### 🟢 低优先级(可选实施) -7. **CORS配置** - 开发便利 -8. **安全头部** - 安全加固 -9. **缓存配置** - 性能优化 - -## 测试验证 - -### 功能测试 -```bash -# 1. 测试客户端标识传递 -curl -H "Accept: application/json" http://localhost:5174/api/config - -# 2. 测试健康检查 -curl http://localhost:5174/health - -# 3. 测试WebSocket(需要浏览器) -# 访问 http://localhost:5174 并检查开发者工具中的WebSocket连接 -``` - -### 性能测试 -```bash -# 使用ab进行简单压力测试 -ab -n 1000 -c 10 http://localhost:5174/ - -# 使用wrk进行更详细的测试 -wrk -t12 -c400 -d30s http://localhost:5174/ -``` - -### 日志验证 -```bash -# 检查日志是否正确记录客户端信息 -sudo tail -f /var/log/nginx/client-a-access.log | grep client_id -``` - -## 总结 - -原始nginx配置虽然能够实现基本的反向代理功能,但缺少多客户端支持的关键特性。通过以上优化,可以实现: - -1. **完整的多客户端支持** - 通过客户端标识传递 -2. **更好的开发体验** - WebSocket和热重载支持 -3. **增强的监控能力** - 详细的日志记录 -4. **提升的性能表现** - 连接池和缓存优化 -5. **基础的安全保障** - 安全头部和请求限制 - -这些改进为在Ubuntu环境中测试多客户端功能提供了完整的基础设施支持。 \ No newline at end of file diff --git a/UBUNTU-NGINX-TEST-GUIDE.md b/UBUNTU-NGINX-TEST-GUIDE.md deleted file mode 100644 index b3fcf2d..0000000 --- a/UBUNTU-NGINX-TEST-GUIDE.md +++ /dev/null @@ -1,422 +0,0 @@ -# Ubuntu环境下Nginx多客户端测试指南 - -## 概述 - -本指南详细说明如何在Ubuntu环境中配置和测试Nginx反向代理多客户端功能,实现根据不同端口动态切换API配置的能力。 - -## 架构说明 - -``` -客户端访问端口 Nginx代理 开发服务器 -5174 (client-a) ──→ 反向代理 ──→ 172.16.0.34:5173 -5175 (client-b) ──→ 反向代理 ──→ 172.16.0.34:5173 -5176 (client-c) ──→ 反向代理 ──→ 172.16.0.34:5173 -5177 (client-d) ──→ 反向代理 ──→ 172.16.0.34:5173 -``` - -每个端口通过 `X-Client-ID` 头部传递客户端标识,应用根据此标识动态选择对应的API配置。 - -## 环境要求 - -### 系统要求 -- Ubuntu 18.04+ 或其他Linux发行版 -- Nginx 1.18+ -- Node.js 18+ -- 网络访问权限到 172.16.0.34:5173 - -### 端口要求 -- 5174-5177:Nginx监听端口 -- 5173:开发服务器端口(需要在172.16.0.34上运行) - -## 安装和配置步骤 - -### 1. 安装Nginx - -```bash -# 更新包管理器 -sudo apt update - -# 安装Nginx -sudo apt install nginx -y - -# 检查Nginx版本 -nginx -v - -# 启动Nginx服务 -sudo systemctl start nginx -sudo systemctl enable nginx -``` - -### 2. 配置Nginx - -#### 2.1 备份原配置 -```bash -# 备份默认配置 -sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup -sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.backup -``` - -#### 2.2 创建多客户端配置 -```bash -# 创建配置目录 -sudo mkdir -p /etc/nginx/conf.d - -# 复制优化后的配置文件 -sudo cp nginx-ubuntu-optimized.conf /etc/nginx/conf.d/multi-client.conf - -# 或者直接创建配置文件 -sudo nano /etc/nginx/conf.d/multi-client.conf -# 然后粘贴 nginx-ubuntu-optimized.conf 的内容 -``` - -#### 2.3 创建日志目录 -```bash -# 创建客户端专用日志目录 -sudo mkdir -p /var/log/nginx/clients - -# 设置权限 -sudo chown -R www-data:www-data /var/log/nginx/clients -sudo chmod -R 755 /var/log/nginx/clients -``` - -#### 2.4 修改主配置文件 -```bash -# 编辑主配置文件 -sudo nano /etc/nginx/nginx.conf -``` - -确保包含以下配置: -```nginx -http { - # 包含多客户端配置 - include /etc/nginx/conf.d/*.conf; - - # 日志格式(如果主配置中没有) - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - # 其他配置... -} -``` - -### 3. 验证配置 - -```bash -# 测试Nginx配置语法 -sudo nginx -t - -# 如果配置正确,重新加载Nginx -sudo systemctl reload nginx - -# 检查Nginx状态 -sudo systemctl status nginx -``` - -### 4. 防火墙配置 - -```bash -# 允许Nginx端口通过防火墙 -sudo ufw allow 5174 -sudo ufw allow 5175 -sudo ufw allow 5176 -sudo ufw allow 5177 - -# 或者允许端口范围 -sudo ufw allow 5174:5177/tcp - -# 检查防火墙状态 -sudo ufw status -``` - -## 应用配置修改 - -### 1. 修改api-config.ts - -需要在开发环境配置中添加第四个客户端: - -```typescript -const getClientConfigs = (env: string): Record> => { - if (env === 'development') { - return { - 'client-a': { - baseUrl: 'http://172.16.0.34:5174', - uploadUrl: 'http://172.16.0.34:5174/admin/documents', - // ... oauth配置 - }, - 'client-b': { - baseUrl: 'http://172.16.0.34:5175', - uploadUrl: 'http://172.16.0.34:5175/admin/documents', - // ... oauth配置 - }, - 'client-c': { - baseUrl: 'http://172.16.0.34:5176', - uploadUrl: 'http://172.16.0.34:5176/admin/documents', - // ... oauth配置 - }, - 'client-d': { - baseUrl: 'http://172.16.0.34:5177', - uploadUrl: 'http://172.16.0.34:5177/admin/documents', - // ... oauth配置 - } - }; - } - // ... -}; -``` - -### 2. 添加客户端检测逻辑 - -在应用中添加根据请求头自动检测客户端的逻辑: - -```typescript -// 在服务器端或中间件中 -const detectClientFromHeaders = (request: Request): string => { - // 从Nginx传递的头部获取客户端ID - const clientId = request.headers.get('X-Client-ID'); - const originalPort = request.headers.get('X-Original-Port'); - - if (clientId) { - return clientId; - } - - // 根据端口映射客户端ID - const portToClient: Record = { - '5174': 'client-a', - '5175': 'client-b', - '5176': 'client-c', - '5177': 'client-d' - }; - - return portToClient[originalPort || ''] || 'main'; -}; -``` - -## 测试步骤 - -### 1. 启动开发服务器 - -确保在172.16.0.34机器上启动开发服务器: -```bash -# 在项目目录中 -npm run dev -# 或 -pnpm dev -# 确保服务运行在5173端口 -``` - -### 2. 测试Nginx代理 - -```bash -# 测试各个端口的健康检查 -curl http://localhost:5174/health -curl http://localhost:5175/health -curl http://localhost:5176/health -curl http://localhost:5177/health - -# 测试代理功能 -curl -H "Accept: text/html" http://localhost:5174/ -curl -H "Accept: text/html" http://localhost:5175/ -``` - -### 3. 验证客户端标识传递 - -```bash -# 检查请求头传递 -curl -v http://localhost:5174/api/test 2>&1 | grep "X-Client-ID" - -# 查看Nginx访问日志 -sudo tail -f /var/log/nginx/client-a-access.log -sudo tail -f /var/log/nginx/client-b-access.log -``` - -### 4. 浏览器测试 - -在浏览器中访问: -- http://172.16.0.34:5174 (Client A) -- http://172.16.0.34:5175 (Client B) -- http://172.16.0.34:5176 (Client C) -- http://172.16.0.34:5177 (Client D) - -### 5. 环境变量测试 - -```bash -# 设置客户端ID环境变量测试 -export CLIENT_ID=client-a -npm run dev - -# 或在启动时指定 -CLIENT_ID=client-b npm run dev -``` - -## 监控和调试 - -### 1. 日志监控 - -```bash -# 实时监控所有客户端日志 -sudo tail -f /var/log/nginx/client-*-access.log - -# 监控错误日志 -sudo tail -f /var/log/nginx/client-*-error.log - -# 监控Nginx主错误日志 -sudo tail -f /var/log/nginx/error.log -``` - -### 2. 性能监控 - -```bash -# 检查Nginx进程状态 -sudo systemctl status nginx - -# 查看端口监听状态 -sudo netstat -tlnp | grep nginx - -# 检查连接数 -sudo ss -tuln | grep :517 -``` - -### 3. 调试工具 - -```bash -# 使用curl测试详细信息 -curl -v -H "X-Test: true" http://localhost:5174/api/config - -# 使用httpie(需要安装) -sudo apt install httpie -http GET localhost:5174/health X-Test:debug -``` - -## 故障排除 - -### 常见问题 - -1. **端口被占用** -```bash -# 检查端口占用 -sudo lsof -i :5174 -# 杀死占用进程 -sudo kill -9 -``` - -2. **权限问题** -```bash -# 检查Nginx用户权限 -sudo chown -R www-data:www-data /var/log/nginx/ -sudo chmod -R 755 /var/log/nginx/ -``` - -3. **配置语法错误** -```bash -# 详细检查配置 -sudo nginx -t -c /etc/nginx/nginx.conf -``` - -4. **网络连接问题** -```bash -# 测试到开发服务器的连接 -telnet 172.16.0.34 5173 -# 或使用nc -nc -zv 172.16.0.34 5173 -``` - -### 日志分析 - -```bash -# 分析访问模式 -sudo awk '{print $1, $7, $9}' /var/log/nginx/client-a-access.log | sort | uniq -c - -# 查找错误请求 -sudo grep "50[0-9]" /var/log/nginx/client-*-access.log - -# 统计客户端访问量 -sudo grep -o 'client_id="[^"]*"' /var/log/nginx/client-*-access.log | sort | uniq -c -``` - -## 性能优化建议 - -### 1. Nginx优化 - -```nginx -# 在http块中添加 -worker_processes auto; -worker_connections 1024; - -# 启用gzip压缩 -gzip on; -gzip_vary on; -gzip_min_length 1024; -gzip_types text/plain text/css application/json application/javascript; - -# 缓存配置 -proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g - inactive=60m use_temp_path=off; -``` - -### 2. 系统优化 - -```bash -# 增加文件描述符限制 -echo "* soft nofile 65535" | sudo tee -a /etc/security/limits.conf -echo "* hard nofile 65535" | sudo tee -a /etc/security/limits.conf - -# 优化内核参数 -echo "net.core.somaxconn = 65535" | sudo tee -a /etc/sysctl.conf -sudo sysctl -p -``` - -## 部署到生产环境 - -### 1. 安全加固 - -```nginx -# 隐藏Nginx版本 -server_tokens off; - -# 限制请求大小 -client_max_body_size 10M; - -# 添加安全头部 -add_header X-Frame-Options DENY; -add_header X-Content-Type-Options nosniff; -add_header X-XSS-Protection "1; mode=block"; -``` - -### 2. SSL配置 - -```nginx -# HTTPS配置示例 -server { - listen 443 ssl http2; - ssl_certificate /path/to/cert.pem; - ssl_certificate_key /path/to/key.pem; - - # SSL优化配置 - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; -} -``` - -### 3. 监控集成 - -```bash -# 集成Prometheus监控 -sudo apt install nginx-module-prometheus - -# 或使用日志分析工具 -sudo apt install goaccess -goaccess /var/log/nginx/client-a-access.log -o report.html --log-format=COMBINED -``` - -## 总结 - -通过以上配置,你可以在Ubuntu环境中成功测试Nginx多客户端反向代理功能。关键点包括: - -1. **客户端标识传递**:通过 `X-Client-ID` 头部 -2. **端口映射**:5174-5177映射到不同客户端 -3. **配置动态切换**:应用根据客户端ID选择对应配置 -4. **日志分离**:每个客户端独立的访问和错误日志 -5. **健康检查**:每个端口提供独立的健康检查端点 - -这个方案为生产环境的多客户端部署提供了完整的测试基础。 \ No newline at end of file diff --git a/nginx.conf b/nginx.conf index 7ac0c61..c24ff22 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,51 +1,33 @@ -server { - listen 5174; - server_name localhost; - - location / { - proxy_pass http://172.16.0.34:5173; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } +# 基于 state 参数端口分发的 OAuth2 回调 Nginx 配置 +# 只保留回调分发相关配置,其他内容全部删除 + +# 1. 端口白名单映射(只允许指定端口) +map $arg_state $target_port { + default ""; + ~^login(51703)_ 51703; + ~^login(51704)_ 51704; + ~^login(51705)_ 51705; + ~^login(51706)_ 51706; + ~^login(51707)_ 51707; + ~^login(51708)_ 51708; } +# 2. 统一回调入口,根据 state 分发到对应端口 server { - listen 5175; - server_name localhost; - - location / { - proxy_pass http://172.16.0.34:5173; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} + listen 80; + server_name 10.79.97.17; -server { - listen 5176; - server_name localhost; - - location / { - proxy_pass http://172.16.0.34:5173; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} + location /callback { + # 未匹配到允许端口直接返回 400 + if ($target_port = "") { + return 400 "Invalid or unsupported state/port"; + } -server { - listen 5177; - server_name localhost; - - location / { - proxy_pass http://172.16.0.34:5173; + # 反向代理到本地对应端口的 /callback + proxy_pass http://10.79.97.17:$target_port/callback$is_args$args; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/ubuntu-nginx-setup.sh b/ubuntu-nginx-setup.sh deleted file mode 100644 index d1caf07..0000000 --- a/ubuntu-nginx-setup.sh +++ /dev/null @@ -1,342 +0,0 @@ -#!/bin/bash - -# Ubuntu环境下Nginx多客户端配置快速部署脚本 -# 使用方法: chmod +x ubuntu-nginx-setup.sh && ./ubuntu-nginx-setup.sh - -set -e # 遇到错误立即退出 - -# 颜色定义 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# 日志函数 -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# 检查是否为root用户 -check_root() { - if [[ $EUID -eq 0 ]]; then - log_warning "检测到root用户,建议使用sudo执行此脚本" - fi -} - -# 检查系统要求 -check_requirements() { - log_info "检查系统要求..." - - # 检查操作系统 - if [[ ! -f /etc/os-release ]]; then - log_error "无法检测操作系统版本" - exit 1 - fi - - . /etc/os-release - log_info "操作系统: $PRETTY_NAME" - - # 检查网络连接 - if ! ping -c 1 172.16.0.34 &> /dev/null; then - log_warning "无法连接到开发服务器 172.16.0.34,请确保网络连接正常" - fi - - log_success "系统要求检查完成" -} - -# 安装Nginx -install_nginx() { - log_info "检查Nginx安装状态..." - - if command -v nginx &> /dev/null; then - NGINX_VERSION=$(nginx -v 2>&1 | cut -d' ' -f3 | cut -d'/' -f2) - log_info "Nginx已安装,版本: $NGINX_VERSION" - return 0 - fi - - log_info "安装Nginx..." - sudo apt update - sudo apt install -y nginx - - # 启动并启用Nginx服务 - sudo systemctl start nginx - sudo systemctl enable nginx - - log_success "Nginx安装完成" -} - -# 备份原始配置 -backup_config() { - log_info "备份原始Nginx配置..." - - BACKUP_DIR="/etc/nginx/backup-$(date +%Y%m%d-%H%M%S)" - sudo mkdir -p "$BACKUP_DIR" - - # 备份主要配置文件 - if [[ -f /etc/nginx/nginx.conf ]]; then - sudo cp /etc/nginx/nginx.conf "$BACKUP_DIR/" - fi - - if [[ -f /etc/nginx/sites-available/default ]]; then - sudo cp /etc/nginx/sites-available/default "$BACKUP_DIR/" - fi - - # 备份现有的conf.d配置 - if [[ -d /etc/nginx/conf.d ]]; then - sudo cp -r /etc/nginx/conf.d "$BACKUP_DIR/" - fi - - log_success "配置已备份到: $BACKUP_DIR" -} - -# 创建多客户端配置 -create_multi_client_config() { - log_info "创建多客户端Nginx配置..." - - # 确保conf.d目录存在 - sudo mkdir -p /etc/nginx/conf.d - - # 检查配置文件是否存在 - if [[ ! -f "nginx-ubuntu-optimized.conf" ]]; then - log_error "找不到nginx-ubuntu-optimized.conf文件,请确保文件在当前目录" - exit 1 - fi - - # 复制配置文件 - sudo cp nginx-ubuntu-optimized.conf /etc/nginx/conf.d/multi-client.conf - - # 设置正确的权限 - sudo chown root:root /etc/nginx/conf.d/multi-client.conf - sudo chmod 644 /etc/nginx/conf.d/multi-client.conf - - log_success "多客户端配置已创建" -} - -# 创建日志目录 -setup_logging() { - log_info "设置日志目录..." - - # 创建客户端日志目录 - sudo mkdir -p /var/log/nginx/clients - - # 设置权限 - sudo chown -R www-data:www-data /var/log/nginx - sudo chmod -R 755 /var/log/nginx - - # 创建日志轮转配置 - sudo tee /etc/logrotate.d/nginx-clients > /dev/null < /dev/null; then - # 允许Nginx端口 - sudo ufw allow 5174/tcp comment "Nginx Client A" - sudo ufw allow 5175/tcp comment "Nginx Client B" - sudo ufw allow 5176/tcp comment "Nginx Client C" - sudo ufw allow 5177/tcp comment "Nginx Client D" - - log_success "防火墙规则已配置" - else - log_warning "ufw未安装,跳过防火墙配置" - fi -} - -# 验证配置 -validate_config() { - log_info "验证Nginx配置..." - - # 测试配置语法 - if sudo nginx -t; then - log_success "Nginx配置语法正确" - else - log_error "Nginx配置语法错误,请检查配置文件" - exit 1 - fi -} - -# 重启Nginx服务 -restart_nginx() { - log_info "重启Nginx服务..." - - sudo systemctl reload nginx - - # 检查服务状态 - if sudo systemctl is-active --quiet nginx; then - log_success "Nginx服务运行正常" - else - log_error "Nginx服务启动失败" - sudo systemctl status nginx - exit 1 - fi -} - -# 测试端口监听 -test_ports() { - log_info "测试端口监听状态..." - - PORTS=(5174 5175 5176 5177) - - for port in "${PORTS[@]}"; do - if ss -tuln | grep -q ":$port "; then - log_success "端口 $port 监听正常" - else - log_error "端口 $port 未监听" - fi - done -} - -# 测试健康检查 -test_health_checks() { - log_info "测试健康检查端点..." - - PORTS=(5174 5175 5176 5177) - CLIENTS=("Client A" "Client B" "Client C" "Client D") - - for i in "${!PORTS[@]}"; do - port=${PORTS[$i]} - client=${CLIENTS[$i]} - - if curl -s "http://localhost:$port/health" | grep -q "OK"; then - log_success "$client (端口 $port) 健康检查通过" - else - log_warning "$client (端口 $port) 健康检查失败" - fi - done -} - -# 显示测试命令 -show_test_commands() { - log_info "测试命令示例:" - - echo -e "\n${YELLOW}1. 健康检查:${NC}" - echo " curl http://localhost:5174/health" - echo " curl http://localhost:5175/health" - echo " curl http://localhost:5176/health" - echo " curl http://localhost:5177/health" - - echo -e "\n${YELLOW}2. 测试代理功能:${NC}" - echo " curl -v http://localhost:5174/ 2>&1 | grep 'X-Client-ID'" - echo " curl -v http://localhost:5175/ 2>&1 | grep 'X-Client-ID'" - - echo -e "\n${YELLOW}3. 监控日志:${NC}" - echo " sudo tail -f /var/log/nginx/client-a-access.log" - echo " sudo tail -f /var/log/nginx/client-*-error.log" - - echo -e "\n${YELLOW}4. 浏览器测试:${NC}" - echo " http://$(hostname -I | awk '{print $1}'):5174" - echo " http://$(hostname -I | awk '{print $1}'):5175" - echo " http://$(hostname -I | awk '{print $1}'):5176" - echo " http://$(hostname -I | awk '{print $1}'):5177" - - echo -e "\n${YELLOW}5. 环境变量测试:${NC}" - echo " CLIENT_ID=client-a npm run dev" - echo " CLIENT_ID=client-b npm run dev" -} - -# 显示管理命令 -show_management_commands() { - log_info "管理命令:" - - echo -e "\n${YELLOW}Nginx服务管理:${NC}" - echo " sudo systemctl start nginx # 启动服务" - echo " sudo systemctl stop nginx # 停止服务" - echo " sudo systemctl restart nginx # 重启服务" - echo " sudo systemctl reload nginx # 重新加载配置" - echo " sudo systemctl status nginx # 查看状态" - - echo -e "\n${YELLOW}配置管理:${NC}" - echo " sudo nginx -t # 测试配置" - echo " sudo nginx -s reload # 重新加载" - - echo -e "\n${YELLOW}日志查看:${NC}" - echo " sudo tail -f /var/log/nginx/error.log" - echo " sudo tail -f /var/log/nginx/access.log" - - echo -e "\n${YELLOW}端口检查:${NC}" - echo " sudo ss -tuln | grep :517" - echo " sudo lsof -i :5174" -} - -# 主函数 -main() { - echo -e "${GREEN}======================================${NC}" - echo -e "${GREEN} Ubuntu Nginx多客户端配置部署脚本 ${NC}" - echo -e "${GREEN}======================================${NC}" - echo - - check_root - check_requirements - install_nginx - backup_config - create_multi_client_config - setup_logging - setup_firewall - validate_config - restart_nginx - - echo - log_success "多客户端Nginx配置部署完成!" - echo - - test_ports - test_health_checks - - echo - show_test_commands - show_management_commands - - echo - log_info "部署完成!现在可以开始测试多客户端功能。" - log_info "请确保开发服务器在 172.16.0.34:5173 上运行。" - echo -} - -# 脚本入口 -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main "$@" -fi \ No newline at end of file diff --git a/交叉评查系统完整文档.md b/交叉评查系统完整文档.md deleted file mode 100644 index 098b4ef..0000000 --- a/交叉评查系统完整文档.md +++ /dev/null @@ -1,634 +0,0 @@ -# 交叉评查系统完整文档 - -## 📋 目录 - -1. [系统概述](#系统概述) -2. [核心概念](#核心概念) -3. [业务流程](#业务流程) -4. [数据模型](#数据模型) -5. [API接口文档](#api接口文档) -6. [业务逻辑详解](#业务逻辑详解) -7. [测试用例](#测试用例) -8. [部署说明](#部署说明) - -## 🎯 系统概述 - -交叉评查系统是一个基于FastAPI和PostgreSQL的分布式评查协作平台,支持多用户对文档评查结果进行异议提案和投票表决,通过民主化的方式确保评查结果的准确性和公正性。 - -### 主要特性 - -- ✅ **任务分配管理** - 支持管理员分配评查任务给多个评查员 -- ✅ **异议提案机制** - 评查员可对系统评分提出修改建议 -- ✅ **民主投票表决** - 通过投票机制形成共识 -- ✅ **自动仲裁逻辑** - 基于投票结果自动确定提案状态 -- ✅ **撤销机制** - 支持提案和投票的撤销操作 -- ✅ **进度跟踪** - 实时监控任务完成进度 -- ✅ **软删除设计** - 保证数据完整性和可追溯性 - -## 🔑 核心概念 - -### 评查任务 (Cross Examination Task) -- **定义**: 一次评查工作的容器,包含需要评查的文档和负责评查的评查员 -- **状态**: `in_progress`(进行中) → `completed`(已完成) -- **作用**: 定义评查的范围和参与者 - -### 权威参与者 (Authoritative Participants) -- **定义**: 针对特定文档被分配参与评查的所有用户集合 -- **计算**: 通过`cross_task_document_mapping`表确定 -- **重要性**: 是投票和仲裁逻辑的基础 - -### 评分提案 (Scoring Proposal) -- **定义**: 评查员对系统自动评分的修改建议 -- **状态**: `pending`(待处理) → `approved`(已批准) / `rejected`(已拒绝) -- **特点**: 创建时自动为提案人投同意票 - -### 批准阈值 (Approval Threshold) -- **计算公式**: `floor(N / 2) + 1`,其中N是权威参与者总数 -- **作用**: 确定提案通过所需的最少同意票数 -- **示例**: 6个参与者的阈值为4票 - -## 🔄 业务流程 - -### 完整流程图 - -```mermaid -graph TD - subgraph "任务分配阶段" - A["管理员选择文档和评查员"] --> B["调用 POST /tasks/assign"] - B --> C["创建 cross_examination_tasks 记录"] - C --> D["创建 cross_task_document_mapping 记录"] - D --> E["任务分配完成"] - end - - subgraph "提案创建阶段" - E --> F["评查员审查系统评分"] - F --> G{"发现异议?"} - G -->|是| H["调用 POST /proposals"] - G -->|否| I["评查完成"] - H --> J["创建 cross_scoring_proposals 记录"] - J --> K["自动为提案人投同意票"] - K --> L["触发状态检查"] - end - - subgraph "投票与仲裁阶段" - L --> M["其他评查员收到通知"] - M --> N["调用 POST /proposals/votes"] - N --> O["创建/更新 cross_opinion_votes 记录"] - O --> P["触发自动仲裁逻辑"] - P --> Q{"计算投票结果"} - Q -->|同意票达到阈值| R["提案状态: approved"] - Q -->|反对票达到阈值| S["提案状态: rejected"] - Q -->|票数不足| T["提案状态: pending"] - R --> U["更新评查结果分数"] - S --> V["通知所有参与者"] - T --> W["等待更多投票"] - U --> V - W --> N - end - - subgraph "任务完成阶段" - V --> X["检查所有提案状态"] - X --> Y{"所有提案已处理?"} - Y -->|是| Z["任务状态: completed"] - Y -->|否| AA["任务继续进行"] - Z --> BB["流程结束"] - AA --> M - end - - subgraph "撤销机制" - H --> CC["调用 DELETE /proposals"] - CC --> DD["软删除提案和投票"] - N --> EE["调用 POST /votes 撤销投票"] - EE --> FF["软删除投票记录"] - DD --> P - FF --> P - end -``` - -### 详细流程说明 - -#### 阶段1: 任务分配 -1. **管理员操作**: 选择文档和评查员 -2. **系统处理**: 创建任务记录和映射关系 -3. **结果**: 建立文档-评查员的关联关系 - -#### 阶段2: 提案创建 -1. **评查员审查**: 检查系统自动评分结果 -2. **发现异议**: 对某个评查点的分数有不同意见 -3. **创建提案**: 提交新的分数和理由 -4. **自动投票**: 系统为提案人自动投同意票 - -#### 阶段3: 投票与仲裁 -1. **投票参与**: 其他评查员对提案进行投票 -2. **实时仲裁**: 每次投票后触发状态检查 -3. **状态确定**: 根据投票结果确定提案状态 -4. **结果处理**: 更新评查结果或通知参与者 - -#### 阶段4: 任务完成 -1. **状态检查**: 检查所有提案是否已处理 -2. **任务完成**: 所有提案处理完毕后标记任务完成 -3. **流程结束**: 整个评查流程结束 - -## 🗄️ 数据模型 - -### 核心表结构 - -#### 1. cross_examination_tasks (评查任务表) -```sql -CREATE TABLE cross_examination_tasks ( - id SERIAL PRIMARY KEY, - user_ids INTEGER[], -- 参与评查的用户ID数组 - assigner_id INTEGER, -- 分配任务的管理员ID - task_status VARCHAR DEFAULT 'in_progress', -- 任务状态 - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - deleted_at TIMESTAMP WITH TIME ZONE -- 软删除时间戳 -); -``` - -#### 2. cross_task_document_mapping (任务文档映射表) -```sql -CREATE TABLE cross_task_document_mapping ( - task_id INTEGER NOT NULL, -- 任务ID - document_id INTEGER NOT NULL, -- 文档ID - audit_status INTEGER DEFAULT 0, -- 审核状态 (0:待审核, 1:已完成) - deleted_at TIMESTAMP WITH TIME ZONE, -- 软删除时间戳 - PRIMARY KEY (task_id, document_id) -); -``` - -#### 3. cross_scoring_proposals (评分提案表) -```sql -CREATE TABLE cross_scoring_proposals ( - id SERIAL PRIMARY KEY, - evaluation_result_id INTEGER, -- 评查结果ID - document_id INTEGER NOT NULL, -- 文档ID - evaluation_point_id INTEGER NOT NULL, -- 评查点ID - proposed_score DOUBLE PRECISION, -- 建议分数 - reason TEXT, -- 提案理由 - proposer_id INTEGER NOT NULL, -- 提案人ID - status VARCHAR DEFAULT 'pending', -- 提案状态 - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - deleted_at TIMESTAMP WITH TIME ZONE -- 软删除时间戳 -); -``` - -#### 4. cross_opinion_votes (意见投票表) -```sql -CREATE TABLE cross_opinion_votes ( - id SERIAL PRIMARY KEY, - proposal_id INTEGER NOT NULL, -- 提案ID - voter_id INTEGER NOT NULL, -- 投票人ID - vote_type VARCHAR NOT NULL, -- 投票类型 (agree/disagree) - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - deleted_at TIMESTAMP WITH TIME ZONE, -- 软删除时间戳 - UNIQUE(proposal_id, voter_id) -- 每个用户对每个提案只能投一票 -); -``` - -### 数据关系图 - -``` -cross_examination_tasks (1) ←→ (N) cross_task_document_mapping - ↓ - documents (1) ←→ (N) cross_scoring_proposals - ↓ - (1) ←→ (N) cross_opinion_votes -``` - -## 📚 API接口文档 - -### 基础信息 -- **Base URL**: `/admin/cross_review` -- **认证方式**: 暂时禁用 (测试阶段) -- **数据格式**: JSON - -### 1. 分配交叉评查任务 - -#### 请求 -```http -POST /admin/cross_review/tasks/assign -Content-Type: application/json - -{ - "document_ids": [1205, 1248, 1257], - "user_ids": [1, 2, 3, 4, 5, 6], - "assigner_id": 1 -} -``` - -#### 响应 -```json -{ - "message": "任务分配成功", - "task_id": 123 -} -``` - -#### 错误响应 -```json -{ - "detail": "文档ID列表和用户ID列表均不能为空" -} -``` - -### 2. 发起评分提案 - -#### 请求 -```http -POST /admin/cross_review/proposals -Content-Type: application/json - -{ - "document_id": 1205, - "evaluation_point_id": 123, - "proposed_score": -1.0, - "reason": "根据相关法规,此项应扣1分", - "proposer_id": 2, - "evaluation_result_id": 37290 -} -``` - -#### 响应 -```json -{ - "success": true, - "proposal": { - "id": 25, - "document_id": 1205, - "evaluation_point_id": 123, - "proposed_score": -1.0, - "reason": "根据相关法规,此项应扣1分", - "proposer_id": 2, - "status": "pending", - "created_at": "2024-01-01T10:00:00Z" - }, - "message": "评分提案创建成功" -} -``` - -### 3. 对提案进行投票 - -#### 请求 -```http -POST /admin/cross_review/proposals/{proposal_id}/votes -Content-Type: application/json - -{ - "vote_type": "agree", - "voter_id": 3 -} -``` - -#### 响应 -```json -{ - "success": true, - "message": "投票成功", - "proposal_status": "pending" -} -``` - -#### 投票类型说明 -- `agree`: 同意提案 -- `disagree`: 反对提案 -- `cancel`: 撤销投票 - -### 4. 获取提案详情列表 - -#### 请求 -```http -POST /admin/cross_review/proposals/details -Content-Type: application/json - -{ - "user_id": 2 -} -``` - -#### 响应 -```json -[ - { - "proposal_id": 25, - "evaluation_point_name": "事实认定准确性", - "proposer": "张三", - "proposed_score": -1.0, - "reason": "根据相关法规,此项应扣1分", - "agree_voters": ["李四", "王五"], - "disagree_voters": ["赵六"] - } -] -``` - -### 5. 撤销评分提案 - -#### 请求 -```http -DELETE /admin/cross_review/proposals/{proposal_id} -Content-Type: application/json - -{ - "user_id": 2 -} -``` - -#### 响应 -```json -{ - "success": true, - "message": "提案已成功撤销" -} -``` - -### 6. 获取任务进度 - -#### 请求 -```http -GET /admin/cross_review/tasks/{task_id}/progress -``` - -#### 响应 -```json -{ - "task_id": 123, - "total_documents": 3, - "completed_documents": 1, - "progress": 33.33 -} -``` - -### 7. 获取用户参与的所有任务及文档 - -#### 请求 -```http -POST /admin/cross_review/tasks/user_documents -Content-Type: application/json - -{ - "user_id": 2 -} -``` - -#### 响应 -```json -[ - { - "task_id": 1, - "task_status": "in_progress", - "documents": [ - { - "document_id": 1001, - "document_name": "无烟草专卖品准运证运输烟草专卖品.pdf", - "document_type_id": 2, - "document_type_name": "行政处罚卷宗" - }, - { - "document_id": 1002, - "document_name": "行政处罚决定书.pdf", - "document_type_id": 2, - "document_type_name": "行政处罚卷宗" - } - ] - }, - { - "task_id": 2, - "task_status": "completed", - "documents": [ - { - "document_id": 1003, - "document_name": "案件调查笔录.pdf", - "document_type_id": 3, - "document_type_name": "调查笔录" - } - ] - } -] -``` - -#### 功能说明 -- **用途**: 获取指定用户参与的所有评查任务及其下属文档的详细信息 -- **数据隔离**: 只返回该用户参与的任务组,未参与的任务不返回 -- **文档信息**: 包含文档ID、文档名称、文档类型ID、文档类型名称 -- **任务状态**: 显示每个任务的当前状态(如:in_progress、completed等) - -#### 字段说明 -| 字段名 | 类型 | 说明 | -|--------|------|------| -| task_id | integer | 任务ID | -| task_status | string | 任务状态(in_progress/completed等) | -| documents | array | 该任务下的文档列表 | -| document_id | integer | 文档ID | -| document_name | string | 文档名称 | -| document_type_id | integer | 文档类型ID | -| document_type_name | string | 文档类型名称 | - -#### 错误响应 -```json -{ - "detail": "获取用户任务及文档失败: 数据库连接错误" -} -``` - -## 🧠 业务逻辑详解 - -### 投票阈值计算 - -#### 计算公式 -```python -approval_threshold = (participant_count // 2) + 1 -``` - -#### 示例场景 -| 参与者数量 | 阈值 | 说明 | -|-----------|------|------| -| 3 | 2 | 需要2票同意 | -| 4 | 3 | 需要3票同意 | -| 5 | 3 | 需要3票同意 | -| 6 | 4 | 需要4票同意 | - -### 自动仲裁逻辑 - -#### 状态判断规则 -1. **提案通过**: `同意票数 >= 阈值` -2. **提案拒绝**: `反对票数 >= 阈值` -3. **提前拒绝**: `同意票数 + 剩余票数 < 阈值` -4. **继续等待**: 其他情况保持pending状态 - -#### 实现代码 -```python -async def _check_and_process_proposal_status(self, proposal_id: int): - # 获取参与者总数 - participant_count_n = len(task_info["user_ids"]) - - # 统计票数 - agree_votes_a = sum(1 for v in votes if v["vote_type"] == "agree") - disagree_votes_d = sum(1 for v in votes if v["vote_type"] == "disagree") - - # 计算阈值 - approval_threshold = (participant_count_n // 2) + 1 - - # 判断状态 - if agree_votes_a >= approval_threshold: - new_status = "approved" - elif (disagree_votes_d >= approval_threshold or - (agree_votes_a + (participant_count_n - len(votes))) < approval_threshold): - new_status = "rejected" - else: - new_status = "pending" -``` - -### 权限验证机制 - -#### 创建提案权限 -- 用户必须是任务的参与者 -- 用户不能为同一评查点重复创建提案 - -#### 投票权限 -- 用户必须是任务的参与者 -- 用户不能对自己的提案投票 -- 用户不能对已确定状态的提案投票 - -#### 撤销权限 -- 只有提案人可以撤销自己的提案 -- 只能撤销pending状态的提案 - -### 软删除机制 - -#### 设计原则 -- 使用`deleted_at`字段标记删除状态 -- 保留历史数据以便审计 -- 查询时自动过滤已删除记录 - -#### 实现方式 -```python -# 软删除提案 -await self.db.update( - "cross_scoring_proposals", - data={"deleted_at": datetime.utcnow().isoformat()}, - filters={"id": f"eq.{proposal_id}"} -) - -# 查询时过滤已删除记录 -filters={"deleted_at": "is.null"} -``` - -## 🧪 测试用例 - -### 测试数据准备 - -#### 文档数据 -```python -DOCUMENT_IDS = [1205, 1248, 1257] # 已评查的文档 -TEST_USER_IDS = [1, 2, 3, 4, 5, 6] # 测试用户 -ASSIGNER_ID = 1 # 管理员ID -``` - -#### 评查结果数据 -```python -DOC_EVAL_RESULTS = { - 1205: [37290, 37291, 37292, ...], # 55个评查结果ID - 1248: [38678, 38679, 38680, ...], # 55个评查结果ID - 1257: [38898, 38899, 38900, ...] # 55个评查结果ID -} -``` - -### 完整测试流程 - -#### 1. 任务分配测试 -```python -def test_assign_task(): - payload = { - "document_ids": [1205, 1248, 1257], - "user_ids": [1, 2, 3, 4, 5, 6], - "assigner_id": 1 - } - response = requests.post(f"{BASE_URL}/admin/cross_review/tasks/assign", json=payload) - assert response.status_code == 201 - assert "task_id" in response.json() -``` - -#### 2. 提案创建测试 -```python -def test_create_proposal(): - payload = { - "document_id": 1205, - "evaluation_point_id": 123, - "proposed_score": -1.0, - "reason": "测试提案理由", - "proposer_id": 2, - "evaluation_result_id": 37290 - } - response = requests.post(f"{BASE_URL}/admin/cross_review/proposals", json=payload) - assert response.status_code == 201 - assert response.json()["success"] == True -``` - -#### 3. 投票测试 -```python -def test_vote_on_proposal(): - payload = { - "vote_type": "agree", - "voter_id": 3 - } - response = requests.post(f"{BASE_URL}/admin/cross_review/proposals/25/votes", json=payload) - assert response.status_code == 201 - assert response.json()["success"] == True -``` - -#### 4. 自动仲裁测试 -```python -def test_auto_arbitration(): - # 模拟4票同意,达到阈值 - for user_id in [1, 2, 3, 4]: - vote_payload = {"vote_type": "agree", "voter_id": user_id} - response = requests.post(f"{BASE_URL}/admin/cross_review/proposals/25/votes", json=vote_payload) - - # 检查提案状态 - assert final_status == "approved" -``` - -### 测试结果验证 - -#### 成功指标 -- ✅ 任务分配成功率: 100% -- ✅ 提案创建成功率: 100% -- ✅ 投票成功率: 100% -- ✅ 自动仲裁准确率: 100% -- ✅ 权限验证有效性: 100% - - - -### 数据库初始化 -```sql --- 创建外键约束 -ALTER TABLE cross_opinion_votes -ADD CONSTRAINT fk_cross_opinion_votes_voter_id -FOREIGN KEY (voter_id) REFERENCES users(id); - --- 创建索引 -CREATE INDEX idx_cross_scoring_proposals_document_id ON cross_scoring_proposals(document_id); -CREATE INDEX idx_cross_opinion_votes_proposal_id ON cross_opinion_votes(proposal_id); -``` - -``` - -## 📊 总结 - -交叉评查系统通过完善的业务流程设计和技术实现,实现了: - -1. **高效的任务管理** - 支持批量分配和进度跟踪 -2. **民主的决策机制** - 通过投票形成共识 -3. **可靠的数据保护** - 软删除和事务保证 -4. **灵活的权限控制** - 多层次权限验证 -5. **完整的API接口** - RESTful设计和标准化响应 - -系统已通过完整的回归测试验证,可以稳定运行在生产环境中。 - ---- - -**文档版本**: v1.5 -**创建日期**: 2025-07-15 -**最后更新**: 2025-07-17 -**维护人员**: Wren \ No newline at end of file