diff --git a/backend/src/main/java/com/designhub/service/ThirdPartyDbService.java b/backend/src/main/java/com/designhub/service/ThirdPartyDbService.java index 231ae19..c5ae834 100644 --- a/backend/src/main/java/com/designhub/service/ThirdPartyDbService.java +++ b/backend/src/main/java/com/designhub/service/ThirdPartyDbService.java @@ -108,9 +108,13 @@ public class ThirdPartyDbService { switch (config.getDbType().toLowerCase()) { case "sqlserver": driverClass = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; - // 完全禁用SSL加密以支持旧版SQL Server,并显式设置TLS协议 - url = String.format("jdbc:sqlserver://%s:%d;databaseName=%s;encrypt=true;trustServerCertificate=true;sslProtocol=TLSv1", + // 使用TLS 1.0协议连接旧版SQL Server + // 需要确保java.security中已移除TLSv1的禁用 + url = String.format("jdbc:sqlserver://%s:%d;databaseName=%s;encrypt=true;trustServerCertificate=true;TrustServerCertificate=true;hostNameInCertificate=*", config.getHost(), config.getPort(), config.getDatabase()); + // 在连接前设置系统属性以启用TLS 1.0 + System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2"); + java.security.Security.setProperty("jdk.tls.disabledAlgorithms", "SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL"); break; case "mysql": driverClass = "com.mysql.cj.jdbc.Driver"; diff --git a/产品全案外部接口文档.md b/产品全案外部接口文档.md new file mode 100644 index 0000000..4ec6dbd --- /dev/null +++ b/产品全案外部接口文档.md @@ -0,0 +1,373 @@ +# 产品全案素材 - 外部接口文档 + +> 本文档供外部任务系统对接使用 + +## 概述 + +素材平台提供外部接口,允许任务系统通过任务号查询、下载和删除产品全案素材。 + +### 基础信息 + +- **接口地址**: `https://your-domain.com/api/external/` +- **鉴权方式**: API Key(请求头 `X-API-Key`) +- **数据格式**: JSON + +### 调用流程 + +``` +1. 任务系统生成任务号(如 TASK202601030184) +2. 员工在素材平台上传素材时填入任务号 +3. 员工在任务系统点击「完成」 +4. 任务系统调用查询接口获取素材详情和下载链接 +5. 任务系统通过下载链接获取文件 +6. (可选)任务完成后调用删除接口清理素材 +``` + +--- + +## 接口列表 + +### 1. 查询素材详情 + +通过任务号查询素材详情,获取下载链接。 + +**请求** + +``` +GET /api/external/product-assets?taskNo={任务号} +``` + +**请求头** + +| 参数 | 类型 | 必填 | 说明 | +|-----|------|-----|------| +| X-API-Key | string | 是 | API 访问密钥 | + +**请求参数** + +| 参数 | 类型 | 必填 | 说明 | +|-----|------|-----|------| +| taskNo | string | 是 | 任务号,格式:TASK + 12位数字 | + +**响应示例(成功)** + +```json +{ + "success": true, + "data": { + "taskNo": "TASK202601030184", + "productModel": "YYD-001", + "categoryName": "天猫旗舰店", + "sceneName": "客厅场景", + "status": "fetched", + "createdAt": "2026-01-03T10:00:00.000Z", + "updatedAt": "2026-01-03T10:30:00.000Z", + "assets": { + "main": [ + { + "id": "asset-uuid-1", + "url": "https://s3.yydd.ai/...", + "downloadUrl": "https://your-domain.com/api/external/download?token=xxx", + "fileName": "主图1.jpg", + "fileSize": 1024000 + } + ], + "sku": [ + { + "id": "asset-uuid-2", + "url": "https://s3.yydd.ai/...", + "downloadUrl": "https://your-domain.com/api/external/download?token=xxx", + "fileName": "SKU白色.jpg", + "fileSize": 512000 + } + ], + "detail": [ + { + "id": "asset-uuid-3", + "url": "https://s3.yydd.ai/...", + "downloadUrl": "https://your-domain.com/api/external/download?token=xxx", + "fileName": "详情1.jpg", + "fileSize": 2048000, + "sortOrder": 0 + }, + { + "id": "asset-uuid-4", + "url": "https://s3.yydd.ai/...", + "downloadUrl": "https://your-domain.com/api/external/download?token=xxx", + "fileName": "详情2.jpg", + "fileSize": 1536000, + "sortOrder": 1 + } + ], + "psd": [ + { + "id": "asset-uuid-5", + "downloadUrl": "https://your-domain.com/api/external/download?token=xxx&assetId=asset-uuid-5", + "fileName": "源文件1.psd", + "fileSize": 52428800 + }, + { + "id": "asset-uuid-6", + "downloadUrl": "https://your-domain.com/api/external/download?token=xxx&assetId=asset-uuid-6", + "fileName": "源文件2.psd", + "fileSize": 31457280 + } + ] + }, + "downloadUrls": { + "all": "https://your-domain.com/api/external/download?token=xxx", + "main": "https://your-domain.com/api/external/download?token=xxx", + "sku": "https://your-domain.com/api/external/download?token=xxx", + "detail": "https://your-domain.com/api/external/download?token=xxx", + "psd": "https://your-domain.com/api/external/download?token=xxx" + } + } +} +``` + +**响应字段说明** + +| 字段 | 类型 | 说明 | +|-----|------|------| +| taskNo | string | 任务号 | +| productModel | string | SPU型号 | +| categoryName | string | 店铺名称 | +| sceneName | string | 场景类别(可能为空) | +| status | string | 状态:completed/fetched | +| assets.main | array | 主图列表 | +| assets.sku | array | SKU图列表 | +| assets.detail | array | 详情图列表(按 sortOrder 排序) | +| assets.psd | array | PSD源文件列表(可能为空数组) | +| downloadUrls.all | string | 全部打包下载链接(不含PSD) | +| downloadUrls.main | string | 主图打包下载链接 | +| downloadUrls.sku | string | SKU图打包下载链接 | +| downloadUrls.detail | string | 详情图打包下载链接 | +| downloadUrls.psd | string/null | PSD批量下载链接(已废弃,请使用单个PSD文件的downloadUrl) | + +**响应示例(失败)** + +```json +{ + "success": false, + "error": { + "code": "TASK_NOT_FOUND", + "message": "任务号不存在" + } +} +``` + +--- + +### 2. 下载文件 + +通过下载链接获取文件。 + +**请求** + +``` +GET /api/external/download?token={下载令牌} +``` + +**请求参数** + +| 参数 | 类型 | 必填 | 说明 | +|-----|------|-----|------| +| token | string | 是 | 下载令牌(从查询接口获取) | + +**响应** + +- 成功:返回文件流(Content-Type 根据文件类型设置) +- 打包下载:返回 ZIP 文件 + +**响应头示例** + +``` +Content-Type: application/zip +Content-Disposition: attachment; filename="TASK202601030184-all.zip" +``` + +**错误响应** + +```json +{ + "success": false, + "error": { + "code": "TOKEN_EXPIRED", + "message": "下载链接已过期,请重新查询获取新链接" + } +} +``` + +--- + +### 3. 删除素材 + +删除指定任务号的素材(软删除)。 + +**请求** + +``` +DELETE /api/external/product-assets?taskNo={任务号} +``` + +**请求头** + +| 参数 | 类型 | 必填 | 说明 | +|-----|------|-----|------| +| X-API-Key | string | 是 | API 访问密钥 | + +**请求参数** + +| 参数 | 类型 | 必填 | 说明 | +|-----|------|-----|------| +| taskNo | string | 是 | 任务号 | + +**响应示例(成功)** + +```json +{ + "success": true, + "message": "素材已删除", + "data": { + "taskNo": "TASK202601030184", + "deletedAt": "2026-01-03T15:00:00.000Z" + } +} +``` + +--- + +## 错误码 + +| 错误码 | HTTP状态码 | 说明 | +|-------|-----------|------| +| INVALID_API_KEY | 401 | API Key 无效或缺失 | +| TASK_NOT_FOUND | 404 | 任务号不存在 | +| MATERIAL_DELETED | 410 | 素材已被删除 | +| TOKEN_EXPIRED | 401 | 下载令牌已过期(有效期24小时) | +| INVALID_TOKEN | 401 | 下载令牌无效 | +| INVALID_TASK_NO | 400 | 任务号格式错误 | +| SERVER_ERROR | 500 | 服务器内部错误 | + +--- + +## 注意事项 + +### 下载链接有效期 + +- 下载链接(downloadUrl)有效期为 **24小时** +- 过期后需重新调用查询接口获取新链接 +- 建议在下载前先调用查询接口获取最新链接 + +### 任务号格式 + +- 格式:`TASK` + 12位数字 +- 示例:`TASK202601030184` +- 任务号在系统中唯一,重复上传会报错 + +### 任务状态说明 + +| 状态 | 说明 | +|-----|------| +| completed | 素材上传完成,等待获取 | +| fetched | 已被外部系统查询过 | + +首次查询成功后,状态会从 `completed` 变为 `fetched`。 + +### 打包下载说明 + +- `downloadUrls.all` 包含:主图 + SKU图 + 详情图(**不含PSD**) +- 如需下载 PSD,请单独使用 `downloadUrls.psd` +- ZIP 文件内按文件夹分类:`main/`、`sku/`、`detail/` + +### 调用频率限制 + +- 建议单个 API Key 每分钟不超过 60 次请求 +- 超出限制可能返回 429 错误 + +--- + +## 示例代码 + +### Node.js + +```javascript +const axios = require('axios'); + +const API_BASE = 'https://your-domain.com/api/external'; +const API_KEY = 'your-api-key'; + +// 查询素材 +async function queryAssets(taskNo) { + const response = await axios.get(`${API_BASE}/product-assets`, { + params: { taskNo }, + headers: { 'X-API-Key': API_KEY } + }); + return response.data; +} + +// 下载文件 +async function downloadFile(downloadUrl, savePath) { + const response = await axios.get(downloadUrl, { + responseType: 'stream' + }); + const writer = fs.createWriteStream(savePath); + response.data.pipe(writer); + return new Promise((resolve, reject) => { + writer.on('finish', resolve); + writer.on('error', reject); + }); +} + +// 使用示例 +async function main() { + const result = await queryAssets('TASK202601030184'); + if (result.success) { + // 下载全部(不含PSD) + await downloadFile(result.data.downloadUrls.all, './assets.zip'); + // 下载PSD + if (result.data.downloadUrls.psd) { + await downloadFile(result.data.downloadUrls.psd, './source.psd'); + } + } +} +``` + +### Python + +```python +import requests + +API_BASE = 'https://your-domain.com/api/external' +API_KEY = 'your-api-key' + +def query_assets(task_no): + response = requests.get( + f'{API_BASE}/product-assets', + params={'taskNo': task_no}, + headers={'X-API-Key': API_KEY} + ) + return response.json() + +def download_file(download_url, save_path): + response = requests.get(download_url, stream=True) + with open(save_path, 'wb') as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + +# 使用示例 +result = query_assets('TASK202601030184') +if result['success']: + download_file(result['data']['downloadUrls']['all'], './assets.zip') +``` + +--- + +## 联系方式 + +如有问题,请联系素材平台管理员获取 API Key 或技术支持。 + +--- + +*文档版本: 1.0* +*更新日期: 2026-01-03*