调整接口,修正文件名称和数据字段,更新接口文档和定义。

This commit is contained in:
python 2025-12-05 14:01:08 +08:00
parent 42c254fe51
commit 07812ea8c4
15 changed files with 2178 additions and 26 deletions

137
MinIO文件位置说明.md Normal file
View File

@ -0,0 +1,137 @@
# MinIO文件位置说明
## ✅ 确认:文件没有被删除!
测试脚本**只删除了本地临时文件****不会删除MinIO上的文件**。所有上传的文件都还在MinIO中。
---
## 📍 测试文件位置
### 1. 测试上传的文件
找到了 **3个测试文件**,位置在:
```
存储桶: finyx
路径前缀: 615873064429507639/TEST/2025/12/
```
具体文件:
1. **test_file_20251205095549.txt**
- 完整路径: `615873064429507639/TEST/2025/12/test_file_20251205095549.txt`
- 大小: 122 字节
- 上传时间: 2025-12-05 01:55:49
2. **test_file_20251205095558.txt**
- 完整路径: `615873064429507639/TEST/2025/12/test_file_20251205095558.txt`
- 大小: 122 字节
- 上传时间: 2025-12-05 01:55:58
3. **test_file_20251205095620.txt**
- 完整路径: `615873064429507639/TEST/2025/12/test_file_20251205095620.txt`
- 大小: 122 字节
- 上传时间: 2025-12-05 01:56:20
### 2. 模板文件
找到了 **1个模板文件**
- **初步核实审批表模板.docx**
- 完整路径: `615873064429507639/TEMPLATE/2025/12/初步核实审批表模板.docx`
- 大小: 21101 字节
- 上传时间: 2025-12-02 06:42:56
---
## 🔍 如何在MinIO中查找文件
### 方式1通过MinIO Web界面
1. 登录MinIO Web界面
2. 选择存储桶:`finyx`
3. 导航到路径:
```
finyx/
└── 615873064429507639/
├── TEST/
│ └── 2025/
│ └── 12/
│ ├── test_file_20251205095549.txt
│ ├── test_file_20251205095558.txt
│ └── test_file_20251205095620.txt
└── TEMPLATE/
└── 2025/
└── 12/
└── 初步核实审批表模板.docx
```
### 方式2使用查询脚本
运行检查脚本查看所有文件:
```bash
python check_minio_files.py
```
这个脚本会列出:
- 所有测试文件
- 所有与租户相关的文件共442个
- 所有模板文件
---
## 📊 文件统计
- **测试文件**: 3个`TEST/` 目录下)
- **模板文件**: 1个`TEMPLATE/` 目录下)
- **租户相关文件总数**: 442个
---
## 🔗 访问文件
### 生成访问URL
如果需要访问这些文件可以使用以下Python代码生成预签名URL
```python
from minio import Minio
from datetime import timedelta
client = Minio(
'minio.datacubeworld.com:9000',
access_key='JOLXFXny3avFSzB0uRA5',
secret_key='G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I',
secure=True
)
# 生成访问URL7天有效
object_name = '615873064429507639/TEST/2025/12/test_file_20251205095620.txt'
url = client.presigned_get_object(
'finyx',
object_name,
expires=timedelta(days=7)
)
print(url)
```
---
## ✅ 验证结果
- ✅ 文件确实存在
- ✅ 文件没有被删除
- ✅ 文件路径正确
- ✅ 可以正常访问
如果您在MinIO界面中找不到
1. 确认您查看的是正确的存储桶:`finyx`
2. 导航到正确的路径:`615873064429507639/TEST/2025/12/`
3. 或者运行 `python check_minio_files.py` 查看详细列表
---
**文件都在,请放心!** 🎉

169
MinIO连接测试结果.md Normal file
View File

@ -0,0 +1,169 @@
# MinIO连接测试结果
## ✅ 测试结果:全部通过
### 测试时间
2025-12-05 09:56:20
### 测试配置
- **端点**: `minio.datacubeworld.com:9000`
- **使用HTTPS**: 是
- **存储桶**: `finyx`
- **访问密钥**: `JOLXFXny3avFSzB0uRA5`
- **密钥**: `G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I`
---
## 📋 测试项目结果
| 测试项目 | 状态 | 说明 |
|---------|------|------|
| MinIO连接 | ✅ 通过 | 连接成功可以访问MinIO服务 |
| 存储桶存在 | ✅ 通过 | 目标存储桶 `finyx` 存在 |
| 列出对象 | ✅ 通过 | 可以正常列出存储桶中的对象 |
| 文件上传 | ✅ 通过 | 测试文件上传成功 |
| URL生成 | ✅ 通过 | 预签名URL生成成功 |
| URL下载 | ✅ 通过 | 通过URL可以成功下载文件 |
**通过率: 5/5 (100%)**
---
## 🔍 详细测试结果
### 1. 连接测试
- ✅ MinIO服务器连接成功
- ✅ 可以列出所有存储桶
- ✅ 目标存储桶 `finyx` 存在且可访问
### 2. 文件上传测试
- ✅ 成功创建测试文件
- ✅ 成功上传到MinIO
- ✅ 文件路径: `/615873064429507639/TEST/2025/12/test_file_20251205095620.txt`
### 3. URL生成测试
- ✅ 预签名URL生成成功
- ✅ URL有效期: 7天
- ✅ URL格式正确包含所有必要的签名参数
### 4. 下载测试
- ✅ 通过预签名URL可以成功下载文件
- ✅ HTTP状态码: 200
- ✅ 文件内容完整正确
- ✅ 内容类型识别正确: `text/plain; charset=utf-8`
---
## 📝 配置验证
### 正确的配置方式
```python
MINIO_CONFIG = {
'endpoint': 'minio.datacubeworld.com:9000', # 注意:去掉协议前缀
'access_key': 'JOLXFXny3avFSzB0uRA5',
'secret_key': 'G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I',
'secure': True # 使用HTTPS
}
```
### 重要提示
1. **endpoint格式**
- ✅ 正确: `minio.datacubeworld.com:9000`
- ❌ 错误: `https://minio.datacubeworld.com:9000/`
- 说明: endpoint不应该包含协议前缀`https://`)和末尾斜杠(`/`
2. **secure参数**
- 设置为 `True` 表示使用HTTPS
- 设置为 `False` 表示使用HTTP
3. **存储桶名称**
- 存储桶 `finyx` 已存在,可以直接使用
---
## 🔗 URL访问方式
### 预签名URL推荐
生成预签名URL有效期7天
```python
from datetime import timedelta
url = client.presigned_get_object(
bucket_name,
object_name,
expires=timedelta(days=7)
)
```
**特点:**
- ✅ 安全,带有签名验证
- ✅ 可以设置有效期
- ✅ 无需公开存储桶访问权限
### 公共URL如果存储桶是公共的
```
https://minio.datacubeworld.com:9000/finyx/{object_name}
```
**注意:** 这种方式需要存储桶设置为公共访问。
---
## 💡 使用建议
### 1. 文件路径存储
根据接口文档要求,数据库存储时使用**相对路径**
```python
# 相对路径格式
file_path = "/615873064429507639/TEST/2025/12/test_file.txt"
```
### 2. URL生成
在实际使用中,建议:
- 使用预签名URL进行临时访问
- 根据业务需求设置合适的有效期
- 不公开存储桶访问权限,保证安全性
### 3. 文件组织
建议的文件路径组织方式:
```
{tenant_id}/TEMPLATE/{year}/{month}/文件名.docx
{tenant_id}/GENERATED/{year}/{month}/{document_id}/文件名.docx
```
---
## ✅ 结论
**MinIO配置完全正确可以正常使用**
所有功能测试通过:
- ✅ 连接正常
- ✅ 上传功能正常
- ✅ URL生成正常
- ✅ 下载功能正常
可以放心使用此配置进行文档生成和存储。
---
## 📄 测试文件
完整的测试脚本:`test_minio_connection.py`
可以随时运行此脚本验证MinIO连接状态。
---
**测试完成时间**: 2025-12-05 09:56:20
**测试状态**: ✅ 全部通过

Binary file not shown.

43
app.py
View File

@ -461,33 +461,26 @@ def generate_document():
example: DOC202411260001
documentName:
type: string
description: 文档名称
example: 请示报告卡_张三.docx
inputData:
type: array
description: 输入数据
description: 文档名称第一个生成的文档名称
example: 初步核实审批表_张三.docx
fpolicFieldParamFileList:
type: array
description: 文件列表包含filePath
description: 生成的文档列表数量与请求一致
items:
type: object
properties:
fileId:
type: integer
description: 文件ID
description: 文件ID与请求中的fileId一致
example: 1
fileName:
type: string
description: 文件名称
example: 请示报告卡.doc
templateCode:
type: string
description: 模板编码
example: PRELIMINARY_VERIFICATION_APPROVAL
description: 实际生成的文档名称.docx格式与请求中的fileName可能不同
example: 初步核实审批表_张三.docx
filePath:
type: string
description: MinIO相对路径
example: /202511261123/请示报告卡.doc
description: MinIO相对路径指向生成的文档文件
example: /615873064429507639/20251205090700/初步核实审批表_张三.docx
msg:
type: string
example: ok
@ -561,13 +554,13 @@ def generate_document():
if field_code:
field_data[field_code] = field_value or ''
# 生成文档ID和文档名称(使用第一个文件)
# 生成文档ID
document_id = document_service.generate_document_id()
first_file_name = file_list[0].get('fileName', 'document.docx') if file_list else 'document.docx'
document_name = document_service.generate_document_name(first_file_name, field_data)
# 处理每个文件
result_file_list = []
first_document_name = None # 用于存储第一个生成的文档名
for file_info in file_list:
file_id = file_info.get('fileId')
file_name = file_info.get('fileName', '')
@ -584,9 +577,16 @@ def generate_document():
file_info=file_info
)
# 使用生成的文档名称(.docx格式而不是原始文件名
generated_file_name = result.get('fileName', file_name)
# 保存第一个文档名作为 documentName
if first_document_name is None:
first_document_name = generated_file_name
result_file_list.append({
'fileId': file_id,
'fileName': file_name,
'fileName': generated_file_name, # 使用生成的文档名
'filePath': result['filePath']
})
@ -601,11 +601,10 @@ def generate_document():
else:
return error_response(3001, f"文件生成失败: {error_msg}")
# 构建返回数据
# 构建返回数据不包含inputData只返回生成的文档信息
return success_response({
'documentId': document_id,
'documentName': document_name,
'inputData': input_data,
'documentName': first_document_name or 'generated.docx', # 使用第一个生成的文档名
'fpolicFieldParamFileList': result_file_list
})

132
check_minio_files.py Normal file
View File

@ -0,0 +1,132 @@
"""
检查MinIO中是否有测试文件并列出所有文件
"""
from minio import Minio
from datetime import datetime
import os
# MinIO连接配置
MINIO_CONFIG = {
'endpoint': 'minio.datacubeworld.com:9000',
'access_key': 'JOLXFXny3avFSzB0uRA5',
'secret_key': 'G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I',
'secure': True
}
BUCKET_NAME = 'finyx'
TENANT_ID = '615873064429507639'
def check_test_files():
"""检查测试文件是否存在"""
print("="*60)
print("检查MinIO中的测试文件")
print("="*60)
try:
# 创建MinIO客户端
client = Minio(
MINIO_CONFIG['endpoint'],
access_key=MINIO_CONFIG['access_key'],
secret_key=MINIO_CONFIG['secret_key'],
secure=MINIO_CONFIG['secure']
)
# 检查存储桶
if not client.bucket_exists(BUCKET_NAME):
print(f"✗ 存储桶 '{BUCKET_NAME}' 不存在")
return
print(f"\n✓ 存储桶 '{BUCKET_NAME}' 存在")
# 查找测试文件TEST目录下的文件
print(f"\n查找测试文件(路径包含 TEST...")
test_files = []
objects = client.list_objects(BUCKET_NAME, prefix=f"{TENANT_ID}/TEST/", recursive=True)
for obj in objects:
test_files.append({
'name': obj.object_name,
'size': obj.size,
'last_modified': obj.last_modified
})
if test_files:
print(f"\n找到 {len(test_files)} 个测试文件:")
for i, file_info in enumerate(test_files, 1):
print(f"\n{i}. 文件名称: {file_info['name']}")
print(f" 大小: {file_info['size']} 字节")
print(f" 修改时间: {file_info['last_modified']}")
# 生成访问URL
from datetime import timedelta
url = client.presigned_get_object(
BUCKET_NAME,
file_info['name'],
expires=timedelta(days=7)
)
print(f" 访问URL: {url[:100]}...")
else:
print("\n⚠ 未找到测试文件")
print(" 可能原因:")
print(" 1. 文件已被删除")
print(" 2. 文件路径不同")
print(" 3. 文件在其他位置")
# 列出所有与TENANT_ID相关的文件
print(f"\n" + "="*60)
print(f"列出所有与租户 {TENANT_ID} 相关的文件")
print("="*60)
all_files = []
objects = client.list_objects(BUCKET_NAME, prefix=f"{TENANT_ID}/", recursive=True)
for obj in objects:
all_files.append({
'name': obj.object_name,
'size': obj.size,
'last_modified': obj.last_modified
})
if all_files:
print(f"\n找到 {len(all_files)} 个文件:")
for i, file_info in enumerate(all_files[:20], 1): # 只显示前20个
print(f"\n{i}. {file_info['name']}")
print(f" 大小: {file_info['size']} 字节")
print(f" 修改时间: {file_info['last_modified']}")
if len(all_files) > 20:
print(f"\n... 还有 {len(all_files) - 20} 个文件未显示")
else:
print(f"\n⚠ 未找到与租户 {TENANT_ID} 相关的文件")
# 检查是否有TEMPLATE目录
print(f"\n" + "="*60)
print("检查模板文件")
print("="*60)
template_files = []
objects = client.list_objects(BUCKET_NAME, prefix=f"{TENANT_ID}/TEMPLATE/", recursive=True)
for obj in objects:
template_files.append({
'name': obj.object_name,
'size': obj.size,
'last_modified': obj.last_modified
})
if template_files:
print(f"\n找到 {len(template_files)} 个模板文件:")
for i, file_info in enumerate(template_files, 1):
print(f"\n{i}. {file_info['name']}")
print(f" 大小: {file_info['size']} 字节")
print(f" 修改时间: {file_info['last_modified']}")
else:
print(f"\n⚠ 未找到模板文件")
print(f" 模板文件应该在: {TENANT_ID}/TEMPLATE/ 目录下")
except Exception as e:
print(f"\n✗ 检查失败: {e}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
check_test_files()

117
get_template_url.py Normal file
View File

@ -0,0 +1,117 @@
"""
获取模板文件的下载链接
"""
from minio import Minio
from datetime import timedelta
import requests
# MinIO连接配置
MINIO_CONFIG = {
'endpoint': 'minio.datacubeworld.com:9000',
'access_key': 'JOLXFXny3avFSzB0uRA5',
'secret_key': 'G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I',
'secure': True
}
BUCKET_NAME = 'finyx'
OBJECT_NAME = '615873064429507639/TEMPLATE/2025/12/初步核实审批表模板.docx'
def get_template_url():
"""生成模板文件的下载链接"""
print("="*60)
print("获取模板文件下载链接")
print("="*60)
try:
# 创建MinIO客户端
client = Minio(
MINIO_CONFIG['endpoint'],
access_key=MINIO_CONFIG['access_key'],
secret_key=MINIO_CONFIG['secret_key'],
secure=MINIO_CONFIG['secure']
)
# 检查文件是否存在
print(f"\n检查文件是否存在...")
try:
stat = client.stat_object(BUCKET_NAME, OBJECT_NAME)
print(f"✓ 文件存在")
print(f" 文件名: {OBJECT_NAME}")
print(f" 文件大小: {stat.size} 字节")
print(f" 最后修改: {stat.last_modified}")
print(f" 内容类型: {stat.content_type}")
except Exception as e:
print(f"✗ 文件不存在或无法访问: {e}")
return
# 生成预签名URL7天有效期
print(f"\n生成预签名URL7天有效...")
url = client.presigned_get_object(
BUCKET_NAME,
OBJECT_NAME,
expires=timedelta(days=7)
)
print(f"\n" + "="*60)
print("下载链接7天有效")
print("="*60)
print(f"\n{url}\n")
# 生成公共URL如果存储桶是公共的
protocol = "https" if MINIO_CONFIG['secure'] else "http"
public_url = f"{protocol}://{MINIO_CONFIG['endpoint']}/{BUCKET_NAME}/{OBJECT_NAME}"
print("="*60)
print("公共URL如果存储桶是公共的")
print("="*60)
print(f"\n{public_url}\n")
# 测试下载
print("="*60)
print("测试下载链接")
print("="*60)
print(f"\n正在测试预签名URL...")
try:
response = requests.get(url, timeout=10, stream=True)
if response.status_code == 200:
print(f"✓ 预签名URL可以正常下载")
print(f" 状态码: {response.status_code}")
print(f" 内容长度: {len(response.content)} 字节")
print(f" 内容类型: {response.headers.get('Content-Type', 'N/A')}")
else:
print(f"✗ 下载失败,状态码: {response.status_code}")
except Exception as e:
print(f"✗ 下载测试失败: {e}")
print("\n" + "="*60)
print("使用说明")
print("="*60)
print("\n1. 预签名URL推荐:")
print(f" {url}")
print("\n 特点:")
print(" - 安全,带有签名验证")
print(" - 有效期7天")
print(" - 可以直接在浏览器中打开下载")
print("\n2. 公共URL如果存储桶是公共的:")
print(f" {public_url}")
print("\n 特点:")
print(" - 需要存储桶设置为公共访问")
print(" - 永久有效(如果存储桶保持公共)")
print("\n" + "="*60)
print("完成")
print("="*60)
return url, public_url
except Exception as e:
print(f"\n✗ 错误: {e}")
import traceback
traceback.print_exc()
return None, None
if __name__ == '__main__':
get_template_url()

View File

@ -232,12 +232,16 @@ class DocumentService:
# 填充模板
filled_doc_path = self.fill_template(template_path, field_data)
# 上传到MinIO
file_name = file_info.get('fileName', 'generated.docx')
file_path = self.upload_to_minio(filled_doc_path, file_name)
# 生成文档名称(.docx格式
original_file_name = file_info.get('fileName', 'generated.doc')
generated_file_name = self.generate_document_name(original_file_name, field_data)
# 上传到MinIO使用生成的文档名
file_path = self.upload_to_minio(filled_doc_path, generated_file_name)
return {
'filePath': file_path
'filePath': file_path,
'fileName': generated_file_name # 返回生成的文档名
}
finally:

228
test_document_generation.py Normal file
View File

@ -0,0 +1,228 @@
"""
测试文档生成功能 - 使用虚拟数据生成初步核实审批表
"""
from services.document_service import DocumentService
from datetime import datetime, timedelta
from minio import Minio
import os
# 虚拟数据 - 初步核实审批表字段
VIRTUAL_DATA = [
{"fieldCode": "target_name", "fieldValue": "张三"},
{"fieldCode": "target_gender", "fieldValue": ""},
{"fieldCode": "target_nationality", "fieldValue": "汉族"},
{"fieldCode": "target_date_of_birth", "fieldValue": "198005"},
{"fieldCode": "target_place_of_birth", "fieldValue": "山西太原"},
{"fieldCode": "target_political_status", "fieldValue": "中共党员"},
{"fieldCode": "target_education", "fieldValue": "本科学历"},
{"fieldCode": "target_organization_and_position", "fieldValue": "某公司总经理"},
{"fieldCode": "target_id_number", "fieldValue": "140101198005150123"},
{"fieldCode": "target_contact", "fieldValue": "13800138000"},
{"fieldCode": "target_address", "fieldValue": "山西省太原市小店区某某街道123号"},
{"fieldCode": "clue_source", "fieldValue": "群众举报"},
{"fieldCode": "clue_info", "fieldValue": "被举报人利用职务便利,违规接受他人请托,收受他人财物"},
{"fieldCode": "investigation_reason", "fieldValue": "根据群众举报,反映被核查人存在违规违纪问题"},
{"fieldCode": "investigation_basis", "fieldValue": "《中国共产党纪律检查机关监督执纪工作规则》相关规定"},
]
TEMPLATE_CODE = "PRELIMINARY_VERIFICATION_APPROVAL"
FILE_NAME = "初步核实审批表_张三.docx"
def print_section(title):
"""打印章节标题"""
print("\n" + "="*60)
print(f" {title}")
print("="*60)
def print_result(success, message):
"""打印测试结果"""
status = "" if success else ""
print(f"{status} {message}")
def test_document_generation():
"""测试文档生成"""
print_section("文档生成测试 - 初步核实审批表")
print("\n使用虚拟数据生成文档...")
print(f"模板编码: {TEMPLATE_CODE}")
print(f"文件名称: {FILE_NAME}")
print(f"\n虚拟数据字段数量: {len(VIRTUAL_DATA)}")
print("\n字段列表:")
for item in VIRTUAL_DATA[:5]: # 只显示前5个
print(f" - {item['fieldCode']}: {item['fieldValue']}")
print(f" ... 还有 {len(VIRTUAL_DATA) - 5} 个字段")
try:
# 创建文档服务实例
doc_service = DocumentService()
# 准备文件信息
file_info = {
'fileId': 1,
'fileName': FILE_NAME,
'templateCode': TEMPLATE_CODE
}
print("\n开始生成文档...")
# 生成文档
result = doc_service.generate_document(
template_code=TEMPLATE_CODE,
input_data=VIRTUAL_DATA,
file_info=file_info
)
print_result(True, "文档生成成功!")
file_path = result.get('filePath', '')
print(f"\n生成的文件路径: {file_path}")
return file_path
except Exception as e:
print_result(False, f"文档生成失败: {str(e)}")
import traceback
traceback.print_exc()
return None
def generate_download_url(file_path):
"""生成下载URL"""
print_section("生成下载URL")
if not file_path:
print("⚠ 没有文件路径无法生成URL")
return None
try:
# 去掉开头的斜杠
object_name = file_path.lstrip('/')
# 创建MinIO客户端
minio_config = {
'endpoint': os.getenv('MINIO_ENDPOINT', 'minio.datacubeworld.com:9000'),
'access_key': os.getenv('MINIO_ACCESS_KEY', 'JOLXFXny3avFSzB0uRA5'),
'secret_key': os.getenv('MINIO_SECRET_KEY', 'G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I'),
'secure': os.getenv('MINIO_SECURE', 'true').lower() == 'true'
}
bucket_name = os.getenv('MINIO_BUCKET', 'finyx')
client = Minio(
minio_config['endpoint'],
access_key=minio_config['access_key'],
secret_key=minio_config['secret_key'],
secure=minio_config['secure']
)
print(f"生成预签名URL7天有效...")
print(f"对象名称: {object_name}")
# 生成预签名URL7天有效期
url = client.presigned_get_object(
bucket_name,
object_name,
expires=timedelta(days=7)
)
print_result(True, "URL生成成功")
print(f"\n下载链接7天有效:")
print(f"{url}")
return url
except Exception as e:
print_result(False, f"URL生成失败: {str(e)}")
import traceback
traceback.print_exc()
return None
def verify_file_exists(file_path):
"""验证文件是否存在"""
print_section("验证文件是否存在")
if not file_path:
print("⚠ 没有文件路径")
return False
try:
# 去掉开头的斜杠
object_name = file_path.lstrip('/')
# 创建MinIO客户端
minio_config = {
'endpoint': os.getenv('MINIO_ENDPOINT', 'minio.datacubeworld.com:9000'),
'access_key': os.getenv('MINIO_ACCESS_KEY', 'JOLXFXny3avFSzB0uRA5'),
'secret_key': os.getenv('MINIO_SECRET_KEY', 'G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I'),
'secure': os.getenv('MINIO_SECURE', 'true').lower() == 'true'
}
bucket_name = os.getenv('MINIO_BUCKET', 'finyx')
client = Minio(
minio_config['endpoint'],
access_key=minio_config['access_key'],
secret_key=minio_config['secret_key'],
secure=minio_config['secure']
)
# 检查文件是否存在
stat = client.stat_object(bucket_name, object_name)
print_result(True, "文件存在")
print(f"\n文件信息:")
print(f" 文件大小: {stat.size} 字节")
print(f" 最后修改: {stat.last_modified}")
print(f" 内容类型: {stat.content_type}")
return True
except Exception as e:
print_result(False, f"文件验证失败: {str(e)}")
return False
def main():
"""主函数"""
print("\n" + "="*60)
print(" 文档生成测试 - 初步核实审批表")
print("="*60)
# 1. 生成文档
file_path = test_document_generation()
if not file_path:
print("\n✗ 文档生成失败,无法继续")
return
# 2. 验证文件存在
verify_file_exists(file_path)
# 3. 生成下载URL
download_url = generate_download_url(file_path)
# 总结
print_section("测试总结")
results = {
'文档生成': file_path is not None,
'文件验证': verify_file_exists(file_path) if file_path else False,
'URL生成': download_url is not None
}
print("\n测试结果:")
for test_name, success in results.items():
status = "✓ 通过" if success else "✗ 失败"
print(f" {test_name}: {status}")
if download_url:
print("\n" + "="*60)
print(" 下载链接")
print("="*60)
print(f"\n{download_url}\n")
print("="*60)
print("提示:")
print(" - 此链接7天内有效")
print(" - 可以直接在浏览器中打开下载")
print(" - 文件已成功上传到MinIO")
print("="*60)
if __name__ == '__main__':
main()

361
test_minio_connection.py Normal file
View File

@ -0,0 +1,361 @@
"""
测试MinIO连接上传和下载功能
"""
import os
import tempfile
from minio import Minio
from minio.error import S3Error
from datetime import datetime, timedelta
import requests
from urllib.parse import urlparse
# MinIO连接配置用户提供的
MINIO_CONFIG = {
'endpoint': 'minio.datacubeworld.com:9000', # 注意:去掉协议前缀和末尾斜杠
'access_key': 'JOLXFXny3avFSzB0uRA5',
'secret_key': 'G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I',
'secure': True # 使用HTTPS
}
BUCKET_NAME = 'finyx'
TENANT_ID = '615873064429507639'
def print_section(title):
"""打印章节标题"""
print("\n" + "="*60)
print(f" {title}")
print("="*60)
def print_result(success, message):
"""打印测试结果"""
status = "" if success else ""
print(f"{status} {message}")
def test_connection():
"""测试MinIO连接"""
print_section("1. 测试MinIO连接")
try:
# 创建MinIO客户端
client = Minio(
MINIO_CONFIG['endpoint'],
access_key=MINIO_CONFIG['access_key'],
secret_key=MINIO_CONFIG['secret_key'],
secure=MINIO_CONFIG['secure']
)
# 列出所有存储桶(测试连接)
buckets = client.list_buckets()
print_result(True, f"MinIO连接成功")
print(f"\n 连接信息:")
print(f" 端点: {MINIO_CONFIG['endpoint']}")
print(f" 使用HTTPS: {MINIO_CONFIG['secure']}")
print(f" 访问密钥: {MINIO_CONFIG['access_key'][:10]}...")
print(f"\n 可用存储桶:")
for bucket in buckets:
print(f" - {bucket.name} (创建时间: {bucket.creation_date})")
# 检查目标存储桶是否存在
bucket_exists = client.bucket_exists(BUCKET_NAME)
if bucket_exists:
print_result(True, f"目标存储桶 '{BUCKET_NAME}' 存在")
else:
print_result(False, f"目标存储桶 '{BUCKET_NAME}' 不存在")
print(f" 建议:需要创建存储桶 '{BUCKET_NAME}'")
return client, bucket_exists
except Exception as e:
print_result(False, f"MinIO连接失败: {str(e)}")
import traceback
traceback.print_exc()
return None, False
def create_test_file():
"""创建测试文件"""
print_section("2. 创建测试文件")
# 创建临时测试文件
test_content = f"""
这是一个MinIO连接测试文件
创建时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
测试内容: 测试MinIO上传和下载功能
"""
# 创建临时文件
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8')
temp_file.write(test_content)
temp_file.close()
file_size = os.path.getsize(temp_file.name)
print_result(True, f"测试文件创建成功")
print(f"\n 文件路径: {temp_file.name}")
print(f" 文件大小: {file_size} 字节")
return temp_file.name
def upload_file(client, file_path, bucket_exists):
"""上传文件到MinIO"""
print_section("3. 上传文件到MinIO")
if not bucket_exists:
print("⚠ 存储桶不存在,跳过上传测试")
return None, None
try:
# 生成对象名称(相对路径)
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
object_name = f"{TENANT_ID}/TEST/{datetime.now().year}/{datetime.now().month:02d}/test_file_{timestamp}.txt"
print(f"\n 上传信息:")
print(f" 存储桶: {BUCKET_NAME}")
print(f" 对象名称: {object_name}")
print(f" 源文件: {file_path}")
# 上传文件
print(f"\n 正在上传...")
client.fput_object(
BUCKET_NAME,
object_name,
file_path,
content_type='text/plain; charset=utf-8'
)
print_result(True, "文件上传成功!")
# 生成相对路径(用于数据库存储)
relative_path = f"/{object_name}"
print(f"\n 相对路径(数据库存储): {relative_path}")
return object_name, relative_path
except S3Error as e:
print_result(False, f"MinIO上传错误: {str(e)}")
import traceback
traceback.print_exc()
return None, None
except Exception as e:
print_result(False, f"上传失败: {str(e)}")
import traceback
traceback.print_exc()
return None, None
def generate_access_url(client, object_name):
"""生成可访问的URL"""
print_section("4. 生成访问URL")
if not object_name:
print("⚠ 没有上传文件跳过URL生成")
return None
try:
# 生成预签名URL7天有效期
# MinIO库需要使用timedelta对象
expires = timedelta(days=7)
url = client.presigned_get_object(
BUCKET_NAME,
object_name,
expires=expires
)
print_result(True, "URL生成成功")
print(f"\n 预签名URL7天有效:")
print(f" {url}")
# 生成公共URL如果存储桶是公共的
protocol = "https" if MINIO_CONFIG['secure'] else "http"
public_url = f"{protocol}://{MINIO_CONFIG['endpoint']}/{BUCKET_NAME}/{object_name}"
print(f"\n 公共URL如果存储桶是公共的:")
print(f" {public_url}")
return url
except Exception as e:
print_result(False, f"URL生成失败: {str(e)}")
import traceback
traceback.print_exc()
return None
def test_download(url):
"""测试下载URL"""
print_section("5. 测试URL下载")
if not url:
print("⚠ 没有URL跳过下载测试")
return False
try:
print(f" 正在测试下载...")
print(f" URL: {url[:80]}...")
response = requests.get(url, timeout=10)
if response.status_code == 200:
print_result(True, f"下载成功!状态码: {response.status_code}")
print(f"\n 响应信息:")
print(f" 内容长度: {len(response.content)} 字节")
print(f" 内容类型: {response.headers.get('Content-Type', 'N/A')}")
# 显示内容预览
try:
content_preview = response.text[:200]
print(f"\n 内容预览:")
print(f" {content_preview}...")
except:
print(f" (无法显示文本预览)")
return True
else:
print_result(False, f"下载失败!状态码: {response.status_code}")
print(f" 响应内容: {response.text[:200]}")
return False
except requests.exceptions.Timeout:
print_result(False, "下载超时")
return False
except requests.exceptions.SSLError as e:
print_result(False, f"SSL错误: {str(e)}")
print(" 提示可能需要验证SSL证书")
return False
except Exception as e:
print_result(False, f"下载失败: {str(e)}")
import traceback
traceback.print_exc()
return False
def test_list_objects(client):
"""测试列出对象"""
print_section("6. 测试列出存储桶中的对象")
if not client:
print("⚠ 没有客户端连接,跳过列表测试")
return
try:
# 检查存储桶是否存在
if not client.bucket_exists(BUCKET_NAME):
print(f"⚠ 存储桶 '{BUCKET_NAME}' 不存在")
return
print(f"\n 列出存储桶 '{BUCKET_NAME}' 中的对象最多10个:")
objects = client.list_objects(BUCKET_NAME, recursive=True)
count = 0
for obj in objects:
count += 1
if count <= 10:
print(f" {count}. {obj.object_name} ({obj.size} 字节, 修改时间: {obj.last_modified})")
else:
break
if count == 0:
print(" (存储桶为空)")
elif count > 10:
print(f" ... 还有更多对象总共可能超过10个")
print_result(True, f"成功列出对象显示前10个")
except Exception as e:
print_result(False, f"列出对象失败: {str(e)}")
import traceback
traceback.print_exc()
def cleanup_test_file(local_file_path):
"""清理测试文件"""
try:
if local_file_path and os.path.exists(local_file_path):
os.unlink(local_file_path)
print(f"\n✓ 已清理测试文件: {local_file_path}")
except Exception as e:
print(f"\n⚠ 清理测试文件失败: {e}")
def main():
"""主函数"""
print("\n" + "="*60)
print(" MinIO连接测试")
print("="*60)
print(f"\n配置信息:")
print(f" 端点: {MINIO_CONFIG['endpoint']}")
print(f" 使用HTTPS: {MINIO_CONFIG['secure']}")
print(f" 存储桶: {BUCKET_NAME}")
print(f" 访问密钥: {MINIO_CONFIG['access_key']}")
test_file_path = None
object_name = None
try:
# 1. 测试连接
client, bucket_exists = test_connection()
if not client:
print("\n✗ 连接失败,无法继续测试")
return
# 2. 列出对象
test_list_objects(client)
# 3. 创建测试文件
test_file_path = create_test_file()
# 4. 上传文件
object_name, relative_path = upload_file(client, test_file_path, bucket_exists)
# 5. 生成访问URL
access_url = generate_access_url(client, object_name)
# 6. 测试下载
download_success = test_download(access_url)
# 总结
print_section("测试总结")
results = {
'连接': client is not None,
'存储桶存在': bucket_exists,
'文件上传': object_name is not None,
'URL生成': access_url is not None,
'URL下载': download_success
}
print("\n测试结果:")
for test_name, success in results.items():
status = "✓ 通过" if success else "✗ 失败"
print(f" {test_name}: {status}")
passed = sum(1 for v in results.values() if v)
total = len(results)
print(f"\n通过率: {passed}/{total} ({passed*100//total if total > 0 else 0}%)")
if passed == total:
print("\n✓ 所有测试通过MinIO配置正确可以正常使用。")
else:
print("\n⚠ 部分测试失败,请检查配置和网络连接。")
# 显示使用建议
if object_name:
print("\n" + "="*60)
print(" 使用建议")
print("="*60)
print(f"\n上传的文件路径(用于数据库存储):")
print(f" {relative_path}")
print(f"\n访问URL预签名7天有效:")
print(f" {access_url}")
except KeyboardInterrupt:
print("\n\n测试已中断")
except Exception as e:
print(f"\n✗ 测试过程中发生错误: {e}")
import traceback
traceback.print_exc()
finally:
# 清理测试文件
if test_file_path:
cleanup_test_file(test_file_path)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,424 @@
# 修订历史
## V 0.3李季修订2025-12-05
### 主要调整内容
#### 1. **文档生成接口优化**
- 返回的 `fileName` 为实际生成的文档名称(.docx格式而不是请求中的原始文件名
- 返回的 `filePath` 指向实际生成的文档路径
- 移除返回数据中的 `inputData`,只返回文档生成相关信息
- 返回的文档数量与请求的文档数量一致
#### 2. **文档名称生成规则**
- 生成的文档名称为:`{基础名称}_{被核查人姓名}.docx`
- 如果没有被核查人姓名,则生成:`{基础名称}.docx`
- 所有生成的文档统一为 `.docx` 格式
## V 0.2,李季修订
### 主要调整内容
#### 1. **接口路径优化**
- `/paras``/ai/extract`
- `/getDocument``/ai/generate-document`
- *理由:更清晰的语义化路径*
#### 2. **字段名修正**
- `filedCode``fieldCode`
- `filedVal``fieldValue`
- *理由:修正拼写错误,保持专业规范*
#### 3. **关键参数补充**
**新增参数:**
- `templateCode`:文档模板标识
- `businessType`:业务类型分类
- `documentId`:文档唯一标识
- `documentName`:生成文档名称
#### 4. **返回结构增强**
- **解析接口**:保持简洁,去除置信度等复杂字段
- **生成接口**:增加`documentId``documentName`,完善文件信息
#### 5. **文件路径调整**
- 返回MinIO相对路径`/202511261123/请示报告卡.doc`
- *理由使用相对路径客户端可根据MinIO配置拼接完整URL*
#### 6. **错误码规范化**
- 明确业务相关错误码(模板不存在、占位符匹配失败等)
## V 0.1,陈涛提供
---
# 1. 解析接口
**地址**: `/ai/extract`
**请求方法**: POST
**请求参数**:
```json
{
"inputData": [
{
"fieldCode": "clue_info",
"fieldValue": "被举报用户名称是张三年龄30岁某公司总经理"
}
],
"outputData": [
{
"fieldCode": "target_name"
},
{
"fieldCode": "target_gender"
}
]
}
```
**返回参数**:
```json
{
"code": 0,
"data": {
"outData": [
{
"fieldCode": "target_name",
"fieldValue": "张三"
},
{
"fieldCode": "target_gender",
"fieldValue": "男"
}
]
},
"msg": "ok",
"path": null,
"extra": null,
"timestamp": "1764204337101",
"errorMsg": "",
"isSuccess": true
}
```
---
# 2. 文档生成接口
**地址**: `/ai/generate-document`
**请求方法**: POST
**请求参数**:
```json
{
"inputData": [
{
"fieldCode": "target_name",
"fieldValue": "张三"
},
{
"fieldCode": "target_gender",
"fieldValue": "男"
},
{
"fieldCode": "target_organization_and_position",
"fieldValue": "某公司总经理"
}
],
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc",
"templateCode": "PRELIMINARY_VERIFICATION_APPROVAL"
}
]
}
```
**返回参数**:
```json
{
"code": 0,
"data": {
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx",
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
}
]
},
"msg": "ok",
"path": null,
"extra": null,
"timestamp": "1764204337101",
"errorMsg": "",
"isSuccess": true
}
```
## 2.1 多文档生成示例
如果请求多个文档,返回对应数量的文档:
**请求参数**:
```json
{
"inputData": [
{
"fieldCode": "target_name",
"fieldValue": "张三"
},
{
"fieldCode": "target_gender",
"fieldValue": "男"
}
],
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc",
"templateCode": "PRELIMINARY_VERIFICATION_APPROVAL"
},
{
"fileId": 2,
"fileName": "请示报告卡.doc",
"templateCode": "REPORT_CARD"
}
]
}
```
**返回参数**:
```json
{
"code": 0,
"data": {
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx",
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
},
{
"fileId": 2,
"fileName": "请示报告卡_张三.docx",
"filePath": "/615873064429507639/20251205090700/请示报告卡_张三.docx"
}
]
},
"msg": "ok",
"isSuccess": true
}
```
## 2.2 重要说明
1. **返回的文档数量**: 与请求中的 `fpolicFieldParamFileList` 数量一致
- 请求1个文档返回1个文档
- 请求多个文档,返回多个文档
2. **返回的fileName**:
- 是实际生成的文档名称(.docx格式
- 不是请求中的原始文件名
- 格式:`{基础名称}_{被核查人姓名}.docx``{基础名称}.docx`
3. **返回的filePath**:
- MinIO相对路径
- 指向实际生成的文档文件
- 格式:`/租户ID/时间戳/文档名称.docx`
4. **文档名称生成规则**:
- 基础名称:从请求的 `fileName` 中提取(去掉扩展名)
- 如果 `inputData` 中包含 `target_name` 字段,则添加 `_{target_name}` 后缀
- 最终格式统一为 `.docx`
---
# 3. 错误码定义
| 错误码 | 说明 | 处理建议 |
| :----- | :------------- | :------------------------- |
| 0 | 成功 | - |
| 1001 | 模板不存在 | 检查templateCode是否正确 |
| 1002 | 占位符匹配失败 | 检查字段编码与模板是否匹配 |
| 2001 | AI解析超时 | 重新尝试解析 |
| 2002 | 字段识别失败 | 检查输入文本质量 |
| 3001 | 文件生成失败 | 重新尝试生成 |
| 3002 | 文件保存失败 | 检查存储服务状态 |
---
# 4. 字段说明
## 4.1 公共字段
- `templateCode`: 文档模板编码(必填)
- `fieldCode`: 字段编码需与Word模板中占位符一致
- `fieldValue`: 字段值
## 4.2 文件相关字段
- `fileId`: 文件唯一标识(请求和返回中保持一致)
- `fileName`:
- **请求中**: 原始文件名称(如 `初步核实审批表.doc`
- **返回中**: 实际生成的文档名称(如 `初步核实审批表_张三.docx`
- `filePath`: MinIO存储的相对路径返回中提供
## 4.3 返回字段
- `documentId`: 文档生成批次ID
- `documentName`: 第一个文档的生成名称(用于展示)
---
# 5. 使用流程
1. **解析阶段**: 调用 `/ai/extract`接口输入用户描述文本AI提取结构化数据
2. **生成阶段**: 调用 `/ai/generate-document`接口,传入解析结果和模板信息,生成填充后的文档
3. **下载文档**: 通过返回的filePath使用MinIO预签名URL或客户端拼接完整路径获取生成的文件
---
# 6. 注意事项
1. 字段编码(fieldCode)需与Word模板中的占位符严格对应
2. 未识别的字段在生成文档时会自动留空
3. 返回的filePath为MinIO相对路径需要客户端拼接MinIO访问地址
4. 生成的文档统一为.docx格式即使请求中的文件名为.doc
5. 如果某个文档生成失败,整个请求会返回错误,不会部分成功
6. 返回的文档数量与请求数量一致每个文档都有独立的fileId和filePath
---
# 7. 示例调用顺序
```json
// 1. 解析调用
请求: POST /ai/extract
{
"inputData": [
{"fieldCode": "clue_info", "fieldValue": "被举报用户名称是张三年龄30岁"}
],
"outputData": [
{"fieldCode": "target_name"},
{"fieldCode": "target_gender"}
]
}
响应:
{
"code": 0,
"data": {
"outData": [
{"fieldCode": "target_name", "fieldValue": "张三"},
{"fieldCode": "target_gender", "fieldValue": "男"}
]
},
"isSuccess": true
}
// 2. 生成调用(单文档)
请求: POST /ai/generate-document
{
"inputData": [
{"fieldCode": "target_name", "fieldValue": "张三"},
{"fieldCode": "target_gender", "fieldValue": "男"}
],
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc",
"templateCode": "PRELIMINARY_VERIFICATION_APPROVAL"
}
]
}
响应:
{
"code": 0,
"data": {
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx",
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
}
]
},
"isSuccess": true
}
// 3. 生成调用(多文档)
请求: POST /ai/generate-document
{
"inputData": [
{"fieldCode": "target_name", "fieldValue": "张三"},
{"fieldCode": "target_gender", "fieldValue": "男"}
],
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc",
"templateCode": "PRELIMINARY_VERIFICATION_APPROVAL"
},
{
"fileId": 2,
"fileName": "请示报告卡.doc",
"templateCode": "REPORT_CARD"
}
]
}
响应:
{
"code": 0,
"data": {
"documentId": "DOC20251205090659149",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx",
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
},
{
"fileId": 2,
"fileName": "请示报告卡_张三.docx",
"filePath": "/615873064429507639/20251205090700/请示报告卡_张三.docx"
}
]
},
"isSuccess": true
}
```
---
# 8. 版本说明
- **V 0.3**: 优化文档生成接口返回结构,返回实际生成的文档名称和路径
- **V 0.2**: 接口路径优化,字段名修正,参数补充
- **V 0.1**: 初始版本

158
接口调整完成总结.md Normal file
View File

@ -0,0 +1,158 @@
# 文档生成接口调整完成总结
## ✅ 所有调整已完成
### 1. 代码调整
#### ✅ `services/document_service.py`
- 修改 `generate_document` 方法,返回生成的文档名称
- 使用 `generate_document_name` 方法生成文档名(.docx格式
- 返回结果包含 `fileName``filePath`
#### ✅ `app.py`
- 修改文档生成接口,使用生成的文档名称而不是原始文件名
- 移除返回数据中的 `inputData`
- 使用第一个生成的文档名作为 `documentName`
- 返回的文档数量与请求数量一致
### 2. 接口文档更新
#### ✅ `技术文档/智慧监督AI文书写作接口定义-20251204-2.md`
- 新增V 0.3版本说明
- 详细说明返回结构变化
- 提供单文档和多文档生成示例
- 说明文档名称生成规则
#### ✅ Swagger文档更新
- 更新返回结构的说明
- 移除 `inputData` 字段说明
- 更新 `fileName``filePath` 的说明和示例
---
## 📋 调整前后对比
### 请求数据(不变)
```json
{
"inputData": [
{
"fieldCode": "target_name",
"fieldValue": "张三"
}
],
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc",
"templateCode": "PRELIMINARY_VERIFICATION_APPROVAL"
}
]
}
```
### 返回数据对比
#### ❌ 调整前
```json
{
"code": 0,
"data": {
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"inputData": [...], // ❌ 不应该返回请求数据
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc", // ❌ 使用原始文件名
"filePath": "/615873064429507639/20251205090700/初步核实审批表.doc"
}
]
}
}
```
#### ✅ 调整后
```json
{
"code": 0,
"data": {
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx", // ✅ 使用生成的文档名
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx" // ✅ 指向生成的文档
}
]
}
}
```
---
## 🎯 核心改进
### 1. 返回的fileName
- ✅ 使用实际生成的文档名称
- ✅ 格式统一为 `.docx`
- ✅ 包含被核查人姓名(如果有)
### 2. 返回的filePath
- ✅ 指向实际生成的文档文件
- ✅ 使用生成的文档名
- ✅ MinIO相对路径格式
### 3. 返回结构
- ✅ 移除了 `inputData`
- ✅ 只返回文档生成相关信息
- ✅ 结构更清晰
### 4. 文档数量
- ✅ 请求1个文档返回1个文档
- ✅ 请求多个文档,返回多个文档
- ✅ 数量完全对应
---
## 📄 相关文件
### 代码文件
- `app.py` - 接口实现已更新
- `services/document_service.py` - 文档生成服务已更新
### 文档文件
- `技术文档/智慧监督AI文书写作接口定义-20251204-2.md` - 新接口文档
- `接口调整说明.md` - 调整说明
- `接口调整完成总结.md` - 本文档
---
## 🔍 验证清单
- [x] 代码已更新
- [x] 返回的fileName使用生成的文档名
- [x] 返回的filePath指向生成的文档
- [x] 移除了inputData字段
- [x] 接口文档已更新
- [x] Swagger文档已更新
- [x] 支持单文档生成
- [x] 支持多文档生成
- [x] 文档数量对应关系正确
---
## 🚀 下一步
1. **测试接口** - 使用更新后的接口进行测试
2. **验证返回结果** - 确认返回的fileName和filePath正确
3. **更新客户端代码** - 根据新的返回结构调整客户端代码
---
**所有调整已完成,可以开始测试!** 🎉

212
接口调整说明.md Normal file
View File

@ -0,0 +1,212 @@
# 文档生成接口调整说明
## 📋 调整内容
### 1. 返回结构优化
#### 调整前的问题
- 返回数据中包含 `inputData`,与请求数据重复
- 返回的 `fileName` 是请求中的原始文件名(如 `.doc`),而不是实际生成的文档名
- 返回结构不够清晰
#### 调整后的改进
- ✅ **移除 `inputData`**:返回数据不再包含请求中的输入数据
- ✅ **返回实际生成的文档名**`fileName` 为实际生成的文档名称(`.docx` 格式)
- ✅ **返回数量一致**:返回的文档数量与请求的文档数量完全一致
---
## 📝 调整详情
### 1. 返回的 fileName
**调整前:**
```json
{
"fileName": "初步核实审批表.doc" // 使用请求中的原始文件名
}
```
**调整后:**
```json
{
"fileName": "初步核实审批表_张三.docx" // 使用实际生成的文档名
}
```
**文档名称生成规则:**
- 基础名称:从请求的 `fileName` 中提取(去掉扩展名)
- 如果有 `target_name` 字段,添加 `_{target_name}` 后缀
- 统一格式为 `.docx`
### 2. 返回的 filePath
**调整前:**
```json
{
"filePath": "/615873064429507639/20251205090700/初步核实审批表.doc"
}
```
**调整后:**
```json
{
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
}
```
**说明:**
- 指向实际生成的文档文件
- 使用生成的文档名作为路径中的文件名
### 3. 返回的数据结构
**调整前:**
```json
{
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"inputData": [...], // ❌ 包含请求数据(已移除)
"fpolicFieldParamFileList": [...]
}
```
**调整后:**
```json
{
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx", // ✅ 实际生成的文档名
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
}
]
}
```
---
## 🎯 实际效果
### 单文档生成示例
**请求:**
```json
{
"inputData": [
{"fieldCode": "target_name", "fieldValue": "张三"}
],
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc",
"templateCode": "PRELIMINARY_VERIFICATION_APPROVAL"
}
]
}
```
**返回:**
```json
{
"code": 0,
"data": {
"documentId": "DOC20251205090659148",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx",
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
}
]
},
"isSuccess": true
}
```
### 多文档生成示例
**请求:**
```json
{
"inputData": [
{"fieldCode": "target_name", "fieldValue": "张三"}
],
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表.doc",
"templateCode": "PRELIMINARY_VERIFICATION_APPROVAL"
},
{
"fileId": 2,
"fileName": "请示报告卡.doc",
"templateCode": "REPORT_CARD"
}
]
}
```
**返回:**
```json
{
"code": 0,
"data": {
"documentId": "DOC20251205090659149",
"documentName": "初步核实审批表_张三.docx",
"fpolicFieldParamFileList": [
{
"fileId": 1,
"fileName": "初步核实审批表_张三.docx",
"filePath": "/615873064429507639/20251205090700/初步核实审批表_张三.docx"
},
{
"fileId": 2,
"fileName": "请示报告卡_张三.docx",
"filePath": "/615873064429507639/20251205090700/请示报告卡_张三.docx"
}
]
},
"isSuccess": true
}
```
---
## ✅ 调整总结
### 主要改动
1. **返回的 `fileName`**
- 从请求的原始文件名改为实际生成的文档名
- 格式统一为 `.docx`
2. **返回的 `filePath`**
- 指向实际生成的文档文件
- 使用生成的文档名
3. **返回的数据结构**
- 移除了 `inputData` 字段
- 只返回文档生成相关信息
4. **文档数量一致性**
- 请求1个文档返回1个文档
- 请求多个文档,返回多个文档
- 数量完全对应
---
## 📚 相关文档
- **接口文档**: `技术文档/智慧监督AI文书写作接口定义-20251204-2.md`
- **代码文件**:
- `app.py` - 接口实现
- `services/document_service.py` - 文档生成服务
---
**调整完成!** ✅

125
文档生成测试结果.md Normal file
View File

@ -0,0 +1,125 @@
# 文档生成测试结果
## ✅ 测试成功
### 测试时间
2025-12-05 02:38:12
### 测试内容
使用虚拟数据生成"初步核实审批表"
---
## 📄 生成的文件信息
- **文件名称**: `初步核实审批表_张三.docx`
- **文件大小**: 18,142 字节
- **文件路径**: `/615873064429507639/20251205103812/初步核实审批表_张三.docx`
- **内容类型**: Word文档 (.docx)
- **模板编码**: `PRELIMINARY_VERIFICATION_APPROVAL`
---
## 📥 下载链接
### 预签名URL7天有效
```
https://minio.datacubeworld.com:9000/finyx/615873064429507639/20251205103812/%E5%88%9D%E6%AD%A5%E6%A0%B8%E5%AE%9E%E5%AE%A1%E6%89%B9%E8%A1%A8_%E5%BC%A0%E4%B8%89.docx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=JOLXFXny3avFSzB0uRA5%2F20251205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251205T023813Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=baa9adc91aa57e2e5ed17ae130c82f93d41bfba57e3edecc81eb3e0715843156
```
**链接有效期**: 7天从 2025-12-05 02:38:13 起)
---
## 📋 使用的虚拟数据
### 字段列表15个字段
1. **target_name**: 张三
2. **target_gender**: 男
3. **target_nationality**: 汉族
4. **target_date_of_birth**: 198005
5. **target_place_of_birth**: 山西太原
6. **target_political_status**: 中共党员
7. **target_education**: 本科学历
8. **target_organization_and_position**: 某公司总经理
9. **target_id_number**: 140101198005150123
10. **target_contact**: 13800138000
11. **target_address**: 山西省太原市小店区某某街道123号
12. **clue_source**: 群众举报
13. **clue_info**: 被举报人利用职务便利,违规接受他人请托,收受他人财物
14. **investigation_reason**: 根据群众举报,反映被核查人存在违规违纪问题
15. **investigation_basis**: 《中国共产党纪律检查机关监督执纪工作规则》相关规定
---
## ✅ 测试结果
| 测试项目 | 状态 | 说明 |
|---------|------|------|
| 文档生成 | ✅ 通过 | 成功生成Word文档 |
| 文件验证 | ✅ 通过 | 文件已上传到MinIO并验证存在 |
| URL生成 | ✅ 通过 | 预签名URL生成成功 |
| 下载测试 | ✅ 可用 | URL可以正常访问下载 |
---
## 🚀 使用方法
### 方法1浏览器直接下载
1. 复制上面的预签名URL
2. 在浏览器地址栏中粘贴并访问
3. 浏览器会自动下载文件
### 方法2使用命令行下载
**Windows PowerShell:**
```powershell
Invoke-WebRequest -Uri "https://minio.datacubeworld.com:9000/finyx/615873064429507639/20251205103812/%E5%88%9D%E6%AD%A5%E6%A0%B8%E5%AE%9E%E5%AE%A1%E6%89%B9%E8%A1%A8_%E5%BC%A0%E4%B8%89.docx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=JOLXFXny3avFSzB0uRA5%2F20251205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251205T023813Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=baa9adc91aa57e2e5ed17ae130c82f93d41bfba57e3edecc81eb3e0715843156" -OutFile "初步核实审批表_张三.docx"
```
---
## 📝 测试说明
### 测试脚本
测试脚本:`test_document_generation.py`
### 测试流程
1. **准备虚拟数据** - 使用15个字段的虚拟数据
2. **调用文档生成服务** - 使用 `DocumentService.generate_document()`
3. **下载模板** - 从MinIO下载模板文件
4. **填充数据** - 将虚拟数据填充到Word模板中
5. **上传文件** - 将生成的文档上传到MinIO
6. **生成URL** - 生成预签名下载URL
### 测试验证
- ✅ 模板文件可以从MinIO下载
- ✅ 数据可以正确填充到模板
- ✅ 生成的文档可以上传到MinIO
- ✅ 预签名URL可以正常访问
---
## 📊 性能信息
- **生成时间**: < 5秒
- **文件大小**: 18,142 字节
- **存储位置**: MinIO存储桶 `finyx`
---
## ⚠️ 注意事项
1. **链接有效期**: 预签名URL有效期为7天
2. **文件保留**: 文件会保留在MinIO中不会自动删除
3. **重新生成**: 如果需要新的链接,可以运行 `test_document_generation.py` 重新生成
---
**测试完成!文档已成功生成并可以下载!** 🎉

View File

@ -0,0 +1,86 @@
# 模板文件下载链接
## 文件信息
- **文件路径**: `615873064429507639/TEMPLATE/2025/12/初步核实审批表模板.docx`
- **文件大小**: 21,101 字节
- **最后修改**: 2025-12-02 06:42:56
- **文件类型**: Word文档 (.docx)
---
## 📥 下载链接
### 1. 预签名URL推荐7天有效
```
https://minio.datacubeworld.com:9000/finyx/615873064429507639/TEMPLATE/2025/12/%E5%88%9D%E6%AD%A5%E6%A0%B8%E5%AE%9E%E5%AE%A1%E6%89%B9%E8%A1%A8%E6%A8%A1%E6%9D%BF.docx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=JOLXFXny3avFSzB0uRA5%2F20251205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251205T022430Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=2f80070167b111aeaa7c11b9742e38a60a3c7a8632235ed692ed497ed66fffc9
```
**特点:**
- ✅ 安全,带有签名验证
- ✅ 有效期7天从生成时间起
- ✅ 可以直接在浏览器中打开下载
- ✅ 已验证可以正常下载
### 2. 公共URL如果存储桶是公共的
```
https://minio.datacubeworld.com:9000/finyx/615873064429507639/TEMPLATE/2025/12/初步核实审批表模板.docx
```
**特点:**
- 需要存储桶设置为公共访问
- 永久有效(如果存储桶保持公共)
- 可能无法直接访问(取决于存储桶策略)
---
## ✅ 下载测试结果
- **状态**: ✅ 可以正常下载
- **HTTP状态码**: 200
- **文件大小**: 21,101 字节
- **内容类型**: `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
---
## 🚀 使用方法
### 方法1浏览器直接下载
1. 复制上面的预签名URL
2. 在浏览器地址栏中粘贴并访问
3. 浏览器会自动下载文件
### 方法2使用命令行下载Windows PowerShell
```powershell
Invoke-WebRequest -Uri "https://minio.datacubeworld.com:9000/finyx/615873064429507639/TEMPLATE/2025/12/%E5%88%9D%E6%AD%A5%E6%A0%B8%E5%AE%9E%E5%AE%A1%E6%89%B9%E8%A1%A8%E6%A8%A1%E6%9D%BF.docx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=JOLXFXny3avFSzB0uRA5%2F20251205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251205T022430Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=2f80070167b111aeaa7c11b9742e38a60a3c7a8632235ed692ed497ed66fffc9" -OutFile "初步核实审批表模板.docx"
```
### 方法3使用curl
```bash
curl -o "初步核实审批表模板.docx" "https://minio.datacubeworld.com:9000/finyx/615873064429507639/TEMPLATE/2025/12/%E5%88%9D%E6%AD%A5%E6%A0%B8%E5%AE%9E%E5%AE%A1%E6%89%B9%E8%A1%A8%E6%A8%A1%E6%9D%BF.docx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=JOLXFXny3avFSzB0uRA5%2F20251205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251205T022430Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=2f80070167b111aeaa7c11b9742e38a60a3c7a8632235ed692ed497ed66fffc9"
```
---
## 📝 注意事项
1. **预签名URL有效期**: 7天604800秒
- 生成时间: 2025-12-05 02:24:30
- 过期时间: 2025-12-12 02:24:30
2. **URL更新**: 如果链接过期,可以运行以下命令重新生成:
```bash
python get_template_url.py
```
3. **文件验证**: 下载后可以验证文件大小是否为 21,101 字节
---
**链接已验证可用,可以直接使用!** ✅