配置好正式环境的nginx配置文件

This commit is contained in:
2025-07-28 00:25:57 +08:00
parent 33363aba78
commit 46febb97cb
6 changed files with 23 additions and 2081 deletions
-213
View File
@@ -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` 并使用对应的配置!
-429
View File
@@ -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环境中测试多客户端功能提供了完整的基础设施支持。
-422
View File
@@ -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-5177Nginx监听端口
- 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<string, Partial<ApiConfig>> => {
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<string, string> = {
'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 <PID>
```
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. **健康检查**:每个端口提供独立的健康检查端点
这个方案为生产环境的多客户端部署提供了完整的测试基础。
+26 -44
View File
@@ -1,48 +1,30 @@
server {
listen 5174;
server_name localhost;
# 基于 state 参数端口分发的 OAuth2 回调 Nginx 配置
# 只保留回调分发相关配置,其他内容全部删除
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 {
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;
}
}
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;
}
}
server {
listen 5177;
server_name localhost;
location / {
proxy_pass http://172.16.0.34:5173;
# 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 80;
server_name 10.79.97.17;
location /callback {
# 未匹配到允许端口直接返回 400
if ($target_port = "") {
return 400 "Invalid or unsupported state/port";
}
# 反向代理到本地对应端口的 /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;
-342
View File
@@ -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 <<EOF
/var/log/nginx/client-*-access.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 644 www-data www-data
postrotate
systemctl reload nginx
endscript
}
/var/log/nginx/client-*-error.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 644 www-data www-data
postrotate
systemctl reload nginx
endscript
}
EOF
log_success "日志配置完成"
}
# 配置防火墙
setup_firewall() {
log_info "配置防火墙规则..."
# 检查ufw是否安装
if command -v ufw &> /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
-634
View File
@@ -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