整理项目文档入口与历史资料
This commit is contained in:
parent
1f46544991
commit
25316809db
@ -1,3 +0,0 @@
|
||||
|
||||
# Don't index SpecStory auto-save files, but allow explicit context inclusion via @ references
|
||||
.specstory/**
|
||||
@ -1,23 +0,0 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"name": "功能完成单元测试",
|
||||
"description": "当完成一个功能或者修改一个功能完成时,自动执行单元测试并生成测试汇报,用中文回复",
|
||||
"version": "1",
|
||||
"when": {
|
||||
"type": "fileEdited",
|
||||
"patterns": [
|
||||
"app/**/*.tsx",
|
||||
"app/**/*.ts",
|
||||
"components/**/*.tsx",
|
||||
"components/**/*.ts",
|
||||
"lib/**/*.ts",
|
||||
"app/api/**/*.ts"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"type": "askAgent",
|
||||
"prompt": "检测到代码文件已修改。请执行以下操作:\n\n1. 分析修改的文件和相关功能\n2. 识别需要测试的功能模块\n3. 运行相关的单元测试(如果存在)\n4. 如果没有单元测试,建议创建测试用例\n5. 生成测试汇报,包括:\n - 测试覆盖的功能点\n - 测试结果(通过/失败)\n - 代码覆盖率\n - 发现的问题和建议\n - 测试执行时间\n\n请使用 npm test 或 jest 命令执行测试,并提供详细的测试报告。"
|
||||
},
|
||||
"workspaceFolderName": "flnxi_sucai",
|
||||
"shortName": "unit-test-on-completion"
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
# 用户偏好设置
|
||||
|
||||
## 🌏 语言规范
|
||||
|
||||
**所有内容使用中文**
|
||||
- AI 回复、对话、文档、代码注释使用中文
|
||||
- 文档文件名使用中文(如 `部署指南.md`)
|
||||
- 用户界面、错误消息、日志使用中文
|
||||
|
||||
**例外**
|
||||
- 代码变量名/函数名用英文(编程规范)
|
||||
- 配置文件名保持英文(`package.json` 等)
|
||||
- API 端点、数据库字段名用英文
|
||||
|
||||
## 🎨 UI 开发规范
|
||||
|
||||
**核心原则**: 遵循项目统一 UI 标准
|
||||
|
||||
**必须做到**
|
||||
1. 优先使用 `components/` 下的现有组件
|
||||
2. 遵循 `tailwind.config.ts` 设计系统
|
||||
3. 参考 `app/` 下现有页面实现
|
||||
4. 保持布局、交互、样式一致性
|
||||
|
||||
**示例**
|
||||
```tsx
|
||||
// ✅ 使用现有组件
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
// ❌ 自定义样式
|
||||
<button className="bg-blue-500...">
|
||||
```
|
||||
|
||||
## 🔧 开发环境
|
||||
|
||||
### Shell 配置(必须!)
|
||||
|
||||
**原因**: PowerShell 语法差,批量操作常失败,AI 生成的是 Bash 命令
|
||||
|
||||
**配置**: 在 `settings.json` 设置 Git Bash 为默认
|
||||
|
||||
```json
|
||||
{
|
||||
"terminal.integrated.defaultProfile.windows": "Git Bash",
|
||||
"terminal.integrated.profiles.windows": {
|
||||
"Git Bash": {
|
||||
"path": "C:\\Program Files\\Git\\bin\\bash.exe"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SSH 配置
|
||||
|
||||
**密钥**: `C:\Users\Mr_Wu01\.ssh\youyidian_prod_ed25519`
|
||||
|
||||
**服务器**: `root@45.249.208.118`
|
||||
|
||||
## 📝 文档规范
|
||||
|
||||
**原则**: 优先更新现有文档,避免文档泛滥
|
||||
|
||||
- 优先更新现有文档,而不是创建新文档
|
||||
- 只有特别重要的内容才创建新文档
|
||||
- 避免创建重复或冗余的文档
|
||||
|
||||
## ✅ 开发检查清单
|
||||
|
||||
- [ ] 使用中文交流和文档
|
||||
- [ ] 浏览现有 UI 组件
|
||||
- [ ] 遵循项目设计规范
|
||||
- [ ] 优先更新现有文档
|
||||
|
||||
---
|
||||
最后更新:2024-12-13
|
||||
@ -1,156 +0,0 @@
|
||||
# Design Document
|
||||
|
||||
## Overview
|
||||
|
||||
本设计文档描述了2025年12月BUG修复和新功能的技术实现方案,包括前端UI修复、权限控制优化、统计逻辑修正、音频素材支持等。
|
||||
|
||||
## Architecture
|
||||
|
||||
系统采用Next.js全栈架构:
|
||||
- 前端:React + TailwindCSS
|
||||
- 后端:Next.js API Routes
|
||||
- 数据库:PostgreSQL + Prisma ORM
|
||||
- 存储:S3兼容对象存储
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### 1. Favicon修复
|
||||
- 文件位置:`public/favicon.ico`
|
||||
- 无需代码修改,只需添加图标文件
|
||||
|
||||
### 2. Tag组件样式修复
|
||||
- 文件:`components/ui/Tag.tsx`
|
||||
- 修改:零素材状态的文字颜色和对比度
|
||||
|
||||
### 3. 素材删除权限API
|
||||
- 文件:`app/api/materials/[id]/route.ts`
|
||||
- 修改:添加素材归属验证逻辑
|
||||
|
||||
### 4. 筛选字段统计API
|
||||
- 文件:`app/api/home-filter-fields/route.ts`
|
||||
- 修改:统计该字段有值的素材数量而非总数
|
||||
|
||||
### 5. 浏览历史功能
|
||||
- 新增数据库表:`BrowseHistory`
|
||||
- 新增API:`app/api/user/browse-history/route.ts`
|
||||
- 新增组件:个人中心浏览历史Tab
|
||||
|
||||
### 6. 作者作品链接
|
||||
- 修改:榜单组件中作者名添加链接
|
||||
- 新增页面:`app/author/[authorName]/page.tsx`
|
||||
|
||||
### 7. 音频素材模板
|
||||
- 修改:`lib/upload-template-presets.ts` 添加音频模板配置
|
||||
- 修改:`lib/system-integrity.ts` 更新模板列表
|
||||
- 新增字段:音频时长、格式、采样率等
|
||||
|
||||
### 8. 删除AI工作流模板
|
||||
- 修改:从预制模板列表中移除ai-material
|
||||
- 迁移:将使用AI模板的素材迁移到other-material
|
||||
|
||||
## Data Models
|
||||
|
||||
### BrowseHistory表(新增)
|
||||
```prisma
|
||||
model BrowseHistory {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
materialId String
|
||||
viewedAt DateTime @default(now())
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, materialId])
|
||||
@@index([userId, viewedAt])
|
||||
}
|
||||
```
|
||||
|
||||
### 音频素材字段(扩展Material表)
|
||||
- `audioDuration`: 音频时长(秒)
|
||||
- `audioFormat`: 音频格式(MP3/WAV/FLAC/AAC)
|
||||
- `audioSampleRate`: 采样率(如44100Hz)
|
||||
- `audioBitRate`: 比特率(如320kbps)
|
||||
|
||||
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||
|
||||
### Property 1: Tag组件零素材状态文字可见性
|
||||
*For any* Tag组件在零素材状态下,文字颜色与背景色的对比度应满足WCAG AA标准(至少4.5:1)
|
||||
**Validates: Requirements 2.1, 2.2, 2.3**
|
||||
|
||||
### Property 2: 管理员素材删除权限验证
|
||||
*For any* 管理员用户和任意素材,删除操作应仅在素材属于该管理员时成功
|
||||
**Validates: Requirements 3.1, 3.2**
|
||||
|
||||
### Property 3: 超级管理员删除权限
|
||||
*For any* 超级管理员用户和任意素材,删除操作应始终成功
|
||||
**Validates: Requirements 3.3**
|
||||
|
||||
### Property 4: 筛选字段"全部"按钮统计准确性
|
||||
*For any* 筛选字段,"全部"按钮显示的数量应等于该字段有非空值的素材数量
|
||||
**Validates: Requirements 4.1, 4.2**
|
||||
|
||||
### Property 5: 筛选统计实时更新
|
||||
*For any* 素材的增删改操作,相关筛选字段的统计数量应在下次查询时正确反映变化
|
||||
**Validates: Requirements 4.3, 4.4**
|
||||
|
||||
### Property 6: 浏览历史记录创建
|
||||
*For any* 用户查看素材详情的操作,应在浏览历史中创建或更新对应记录
|
||||
**Validates: Requirements 5.1**
|
||||
|
||||
### Property 7: 浏览历史排序
|
||||
*For any* 用户的浏览历史列表,记录应按viewedAt字段降序排列
|
||||
**Validates: Requirements 5.3**
|
||||
|
||||
### Property 8: 浏览历史自动清理
|
||||
*For any* 用户的浏览历史超过100条时,最早的记录应被自动删除
|
||||
**Validates: Requirements 5.4**
|
||||
|
||||
### Property 9: 作者作品列表过滤
|
||||
*For any* 作者名,作品列表应仅包含该作者上传的素材
|
||||
**Validates: Requirements 6.3**
|
||||
|
||||
### Property 10: 音频格式支持
|
||||
*For any* 上传的音频文件,系统应接受MP3、WAV、FLAC、AAC格式
|
||||
**Validates: Requirements 7.3**
|
||||
|
||||
### Property 11: 音频元数据显示
|
||||
*For any* 音频素材,应正确显示时长、格式、采样率等元数据
|
||||
**Validates: Requirements 7.4**
|
||||
|
||||
### Property 12: AI模板移除
|
||||
*For any* 预制模板列表查询,结果不应包含ai-material模板
|
||||
**Validates: Requirements 8.2**
|
||||
|
||||
### Property 13: AI模板素材迁移
|
||||
*For any* 使用AI工作流模板的素材,迁移后应关联到other-material模板
|
||||
**Validates: Requirements 8.3**
|
||||
|
||||
## Error Handling
|
||||
|
||||
1. **权限错误**: 返回403状态码和明确的错误消息
|
||||
2. **资源不存在**: 返回404状态码
|
||||
3. **文件格式错误**: 返回400状态码和支持的格式列表
|
||||
4. **数据库错误**: 记录日志并返回500状态码
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### 单元测试
|
||||
- Tag组件样式测试
|
||||
- 权限验证函数测试
|
||||
- 统计逻辑测试
|
||||
- 音频格式验证测试
|
||||
|
||||
### 属性测试(Property-Based Testing)
|
||||
使用Jest + fast-check进行属性测试:
|
||||
- 权限验证属性测试
|
||||
- 统计准确性属性测试
|
||||
- 浏览历史排序属性测试
|
||||
|
||||
### 集成测试
|
||||
- API端点测试
|
||||
- 数据库操作测试
|
||||
- 文件上传测试
|
||||
@ -1,100 +0,0 @@
|
||||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
本文档定义了2025年12月发现的一系列BUG修复需求,包括前端显示问题、权限控制问题、统计逻辑问题以及新功能需求。这些问题需要在系统交付客户前彻底解决。
|
||||
|
||||
## Glossary
|
||||
|
||||
- **素材库系统 (Material Library System)**: 用于管理和展示3D素材、视频、PSD等资源的Web应用
|
||||
- **筛选字段 (Filter Field)**: 首页用于筛选素材的分类字段,如"平台分类"、"场景"等
|
||||
- **素材归属 (Material Ownership)**: 素材与上传者之间的所属关系
|
||||
- **浏览历史 (Browse History)**: 用户查看素材详情的历史记录
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1: 修复favicon.ico 404错误
|
||||
|
||||
**User Story:** As a 用户, I want 网站有正确的favicon图标, so that 浏览器标签页显示正常且不产生404错误
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户访问系统任意页面 THEN 系统 SHALL 返回有效的favicon.ico文件
|
||||
2. WHEN 浏览器请求/favicon.ico THEN 系统 SHALL 返回200状态码和图标文件
|
||||
|
||||
### Requirement 2: 修复PSD标签按钮样式问题
|
||||
|
||||
**User Story:** As a 用户, I want PSD标签按钮在所有状态下文字清晰可见, so that 我能正常使用筛选功能
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN PSD标签没有素材数量时 THEN 系统 SHALL 显示清晰可见的文字
|
||||
2. WHEN 鼠标悬停在PSD标签上时 THEN 系统 SHALL 保持文字对比度足够高
|
||||
3. WHEN 标签处于未选中状态时 THEN 系统 SHALL 确保文字在浅色和深色模式下都清晰可见
|
||||
|
||||
### Requirement 3: 修复管理员素材删除权限
|
||||
|
||||
**User Story:** As a 系统管理员, I want 只能删除自己上传的素材, so that 不会误删其他用户的素材
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 管理员尝试删除素材 THEN 系统 SHALL 验证素材是否属于该管理员
|
||||
2. WHEN 管理员尝试删除非自己上传的素材 THEN 系统 SHALL 拒绝删除并返回权限错误
|
||||
3. WHEN 超级管理员尝试删除任意素材 THEN 系统 SHALL 允许删除操作
|
||||
4. IF 管理员删除权限验证失败 THEN 系统 SHALL 返回明确的错误提示信息
|
||||
|
||||
### Requirement 4: 修复筛选框"全部"按钮数量统计
|
||||
|
||||
**User Story:** As a 用户, I want 筛选框"全部"按钮显示该字段实际有值的素材数量, so that 我能准确了解筛选范围
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 显示筛选字段的"全部"按钮 THEN 系统 SHALL 统计该字段有非空值的素材数量
|
||||
2. WHEN 某字段只有2个素材填写了值 THEN 系统 SHALL 在"全部"按钮显示2而非系统总素材数
|
||||
3. WHEN 筛选字段的选项数量变化 THEN 系统 SHALL 实时更新"全部"按钮的统计数量
|
||||
4. WHEN 素材被删除或新增 THEN 系统 SHALL 正确更新相关筛选字段的统计数量
|
||||
|
||||
### Requirement 5: 新增个人中心浏览历史功能
|
||||
|
||||
**User Story:** As a 用户, I want 在个人中心查看我的浏览历史, so that 我能快速找到之前看过的素材
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户查看素材详情 THEN 系统 SHALL 记录该浏览行为到用户的浏览历史
|
||||
2. WHEN 用户访问个人中心 THEN 系统 SHALL 显示浏览历史入口
|
||||
3. WHEN 用户查看浏览历史列表 THEN 系统 SHALL 按时间倒序显示最近浏览的素材
|
||||
4. WHEN 浏览历史超过100条 THEN 系统 SHALL 自动清理最早的记录
|
||||
5. WHEN 用户点击历史记录中的素材 THEN 系统 SHALL 跳转到该素材详情页
|
||||
|
||||
### Requirement 6: 新增榜单作者作品链接
|
||||
|
||||
**User Story:** As a 用户, I want 在榜单中点击作者名直接查看该作者的所有作品, so that 我能快速浏览感兴趣作者的作品集
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户在榜单页面看到作者名 THEN 系统 SHALL 将作者名显示为可点击链接
|
||||
2. WHEN 用户点击作者名 THEN 系统 SHALL 跳转到该作者的作品列表页
|
||||
3. WHEN 显示作者作品列表 THEN 系统 SHALL 展示该作者上传的所有素材
|
||||
4. WHEN 作者没有任何作品 THEN 系统 SHALL 显示友好的空状态提示
|
||||
|
||||
### Requirement 7: 新增音频素材模板
|
||||
|
||||
**User Story:** As a 用户, I want 上传音频类素材, so that 我能管理和分享音频资源
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 系统启动时 THEN 系统 SHALL 自动创建音频素材预制模板
|
||||
2. WHEN 用户选择上传音频素材 THEN 系统 SHALL 显示音频素材专用的上传表单
|
||||
3. WHEN 上传音频素材 THEN 系统 SHALL 支持常见音频格式(MP3、WAV、FLAC、AAC等)
|
||||
4. WHEN 显示音频素材 THEN 系统 SHALL 展示音频时长、格式、采样率等元数据
|
||||
5. WHEN 预览音频素材 THEN 系统 SHALL 提供音频播放器进行试听
|
||||
|
||||
### Requirement 8: 删除AI工作流预制模板
|
||||
|
||||
**User Story:** As a 系统管理员, I want 移除不再使用的AI工作流模板, so that 系统模板列表更加简洁
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 系统启动时 THEN 系统 SHALL 不再创建AI工作流预制模板
|
||||
2. WHEN 显示预制模板列表 THEN 系统 SHALL 不包含AI工作流模板
|
||||
3. WHEN 已有素材使用AI工作流模板 THEN 系统 SHALL 将这些素材迁移到"其他"模板
|
||||
@ -1,158 +0,0 @@
|
||||
# Implementation Plan
|
||||
|
||||
## BUG修复和新功能 - 2025年12月
|
||||
|
||||
- [x] 1. 修复favicon.ico 404错误
|
||||
|
||||
|
||||
- [x] 1.1 创建或复制favicon.ico文件到public目录
|
||||
|
||||
|
||||
- 使用项目logo生成favicon
|
||||
- 确保文件格式正确(ICO格式,包含16x16和32x32尺寸)
|
||||
- _Requirements: 1.1, 1.2_
|
||||
|
||||
- [x] 2. 修复PSD标签按钮样式问题
|
||||
|
||||
|
||||
- [x] 2.1 修改Tag组件零素材状态样式
|
||||
|
||||
|
||||
- 修改 `components/ui/Tag.tsx`
|
||||
- 确保零素材时文字颜色对比度足够
|
||||
- 适配浅色和深色模式
|
||||
- _Requirements: 2.1, 2.2, 2.3_
|
||||
- [x] 2.2 编写Tag组件属性测试
|
||||
|
||||
|
||||
- **Property 1: Tag组件零素材状态文字可见性**
|
||||
- **Validates: Requirements 2.1, 2.2, 2.3**
|
||||
|
||||
|
||||
|
||||
|
||||
- [x] 3. 修复筛选框"全部"按钮数量统计
|
||||
|
||||
- [x] 3.1 修改筛选字段API统计逻辑
|
||||
|
||||
|
||||
- 修改 `app/api/home-filter-fields/route.ts`
|
||||
- 统计该字段有非空值的素材数量
|
||||
- 而非系统总素材数
|
||||
- _Requirements: 4.1, 4.2_
|
||||
- [x] 3.2 编写统计逻辑属性测试
|
||||
|
||||
- **Property 4: 筛选字段"全部"按钮统计准确性**
|
||||
- **Property 5: 筛选统计实时更新**
|
||||
- **Validates: Requirements 4.1, 4.2, 4.3, 4.4**
|
||||
|
||||
|
||||
|
||||
- [x] 4. 修复管理员素材删除权限
|
||||
|
||||
- [x] 4.1 修改素材删除API权限验证
|
||||
|
||||
- 修改 `app/api/materials/[id]/route.ts`
|
||||
- 添加素材归属验证逻辑
|
||||
- 管理员只能删除自己上传的素材
|
||||
- 超级管理员可删除任意素材
|
||||
- _Requirements: 3.1, 3.2, 3.3, 3.4_
|
||||
- [x] 4.2 编写权限验证属性测试
|
||||
|
||||
|
||||
- **Property 2: 管理员素材删除权限验证**
|
||||
- **Property 3: 超级管理员删除权限**
|
||||
- **Validates: Requirements 3.1, 3.2, 3.3**
|
||||
|
||||
- [x] 5. Checkpoint - 确保BUG修复测试通过
|
||||
|
||||
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 6. 删除AI工作流预制模板
|
||||
|
||||
- [x] 6.1 从预制模板配置中移除ai-material
|
||||
- 修改 `lib/upload-template-presets.ts`
|
||||
- 修改 `lib/system-integrity.ts`
|
||||
- 修改相关测试文件
|
||||
- _Requirements: 8.1, 8.2_
|
||||
- [x] 6.2 创建AI模板素材迁移脚本
|
||||
- 创建 `scripts/migrate-ai-to-other-template.ts`
|
||||
- 将使用AI模板的素材迁移到other-material
|
||||
- _Requirements: 8.3_
|
||||
- [x] 6.3 编写模板移除属性测试
|
||||
- 创建 `lib/__tests__/template-migration.test.ts`
|
||||
- **Property 12: AI模板移除**
|
||||
- **Property 13: AI模板素材迁移**
|
||||
- **Validates: Requirements 8.2, 8.3**
|
||||
|
||||
- [x] 7. 新增音频素材模板
|
||||
- [x] 7.1 添加音频素材预制模板配置
|
||||
- 修改 `lib/upload-template-presets.ts` 添加audio-material
|
||||
- 修改 `lib/system-integrity.ts` 更新模板列表
|
||||
- 字段:productModel, categoryName, sceneName, audioDuration, audioFormat
|
||||
- _Requirements: 7.1, 7.2_
|
||||
- [x] 7.2 音频文件上传已支持
|
||||
- 系统已支持 MP3, WAV, FLAC, AAC, OGG, M4A 格式
|
||||
- _Requirements: 7.3, 7.4_
|
||||
- [x] 7.3 编写音频功能属性测试
|
||||
- 在 `lib/__tests__/template-migration.test.ts` 中添加
|
||||
- **Property 10: 音频格式支持**
|
||||
- **Property 11: 音频元数据显示**
|
||||
- **Validates: Requirements 7.3, 7.4**
|
||||
|
||||
- [x] 8. Checkpoint - 确保模板修改测试通过
|
||||
- 所有测试通过
|
||||
|
||||
- [x] 9. 新增个人中心浏览历史功能
|
||||
- [x] 9.1 创建BrowseHistory数据库表
|
||||
- 修改 `prisma/schema.prisma`
|
||||
- 添加 BrowseHistory 模型
|
||||
- _Requirements: 5.1_
|
||||
- [x] 9.2 创建浏览历史API
|
||||
- 创建 `app/api/user/history/route.ts`
|
||||
- GET: 获取浏览历史列表
|
||||
- POST: 记录浏览行为
|
||||
- DELETE: 清除历史记录
|
||||
- _Requirements: 5.1, 5.3, 5.4_
|
||||
- [x] 9.3 在素材详情页记录浏览历史
|
||||
- 修改 `app/share/[id]/page.tsx`
|
||||
- 用户查看时自动记录
|
||||
- _Requirements: 5.1_
|
||||
- [x] 9.4 在个人中心添加浏览历史Tab
|
||||
- 修改 `app/profile/page.tsx`
|
||||
- 添加浏览历史入口和列表展示
|
||||
- _Requirements: 5.2, 5.5_
|
||||
- [x] 9.5 编写浏览历史属性测试
|
||||
- 创建 `lib/__tests__/browse-history.test.ts`
|
||||
- **Property 6: 浏览历史记录创建**
|
||||
- **Property 7: 浏览历史排序**
|
||||
- **Property 8: 浏览历史自动清理**
|
||||
- **Validates: Requirements 5.1, 5.3, 5.4**
|
||||
|
||||
- [x] 10. 新增榜单作者作品链接
|
||||
- [x] 10.1 创建作者作品页面
|
||||
- 创建 `app/author/[authorName]/page.tsx`
|
||||
- 展示该作者的所有素材
|
||||
- 处理空状态
|
||||
- _Requirements: 6.3, 6.4_
|
||||
- [x] 10.2 修改榜单组件添加作者链接
|
||||
- 修改 `components/RankingPanel.tsx`
|
||||
- 修改 `components/MaterialPreviewModal.tsx`
|
||||
- 将作者名改为可点击链接
|
||||
- _Requirements: 6.1, 6.2_
|
||||
- [x] 10.3 编写作者作品属性测试
|
||||
- 创建 `lib/__tests__/author-works.test.ts`
|
||||
- **Property 9: 作者作品列表过滤**
|
||||
- **Validates: Requirements 6.3**
|
||||
|
||||
- [x] 11. Final Checkpoint - 确保所有测试通过
|
||||
- 所有693个测试通过
|
||||
|
||||
- [x] 12. 部署到生产环境
|
||||
|
||||
|
||||
- 运行数据库迁移
|
||||
- 执行AI模板素材迁移脚本
|
||||
- 部署新代码
|
||||
- 验证所有功能正常
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,236 +0,0 @@
|
||||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
本文档定义了 FLNXI 素材系统的许可证管理系统需求。该系统旨在实现软件的商业化分发,支持免费版、标准版和Pro版三个版本,通过联网验证和硬件指纹绑定实现防破解保护。系统包含两个主要部分:部署在客户端的许可证验证模块,以及部署在中央服务器的许可证验证服务和客户管理后台。
|
||||
|
||||
## Glossary
|
||||
|
||||
- **License(许可证)**: 授权客户使用软件特定版本和功能的凭证
|
||||
- **License Key(许可证密钥)**: 用于激活许可证的唯一字符串,格式为 XXXX-XXXX-XXXX-XXXX
|
||||
- **Hardware Fingerprint(硬件指纹)**: 基于客户服务器硬件特征生成的唯一标识符
|
||||
- **License Server(许可证服务器)**: 部署在 45.249.208.118 的中央验证服务
|
||||
- **Client Instance(客户端实例)**: 部署在客户 NAS/服务器上的 FLNXI 素材系统实例
|
||||
- **Heartbeat(心跳)**: 客户端定期向服务器发送的存活和验证请求
|
||||
- **Grace Period(宽限期)**: 网络断开后允许继续使用的时间窗口
|
||||
- **Admin Portal(管理后台)**: 用于管理客户和许可证的 Web 界面
|
||||
- **Code Obfuscation(代码混淆)**: 将源代码转换为难以阅读和理解的形式,增加逆向工程难度
|
||||
- **Agent(安装代理)**: 运行在客户 NAS 上的轻量级 Docker 容器,提供 Web 安装界面,帮助客户自助完成系统部署
|
||||
- **Semantic Versioning(语义化版本)**: 版本号格式为 v主版本.次版本.修订号(如 v1.2.3)
|
||||
- **Rollback(回滚)**: 将系统恢复到之前版本的操作
|
||||
- **Forced Update(强制更新)**: 必须完成才能继续使用系统的更新
|
||||
- **Optional Update(可选更新)**: 用户可自行决定是否执行的更新
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1: 许可证版本定义
|
||||
|
||||
**User Story:** As a 系统管理员, I want to 定义不同版本的功能限制, so that 可以实现差异化的产品定价策略。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE License_Server SHALL support three license types: FREE, STANDARD, and PRO
|
||||
2. WHEN a license type is FREE, THE System SHALL enforce a maximum of 10 users
|
||||
3. WHEN a license type is FREE, THE System SHALL enforce a maximum of 100 materials
|
||||
4. WHEN a license type is FREE, THE System SHALL disable custom template functionality
|
||||
5. WHEN a license type is FREE, THE System SHALL disable advanced system settings
|
||||
6. WHEN a license type is FREE, THE System SHALL disable department isolation functionality
|
||||
7. WHEN a license type is STANDARD or PRO, THE System SHALL allow unlimited users and materials
|
||||
8. WHEN a license type is PRO, THE System SHALL enable intranet penetration functionality
|
||||
|
||||
### Requirement 2: 许可证激活
|
||||
|
||||
**User Story:** As a 客户, I want to 使用许可证密钥激活系统, so that 可以解锁对应版本的功能。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a user first accesses the system without activation, THE System SHALL display an activation page
|
||||
2. WHEN a user enters a valid license key, THE System SHALL send an activation request to the License_Server
|
||||
3. WHEN the License_Server receives an activation request, THE System SHALL validate the license key format and existence
|
||||
4. WHEN the license key is valid and not already activated, THE License_Server SHALL bind the license to the hardware fingerprint
|
||||
5. WHEN activation succeeds, THE Client_Instance SHALL store the license information locally
|
||||
6. WHEN a user chooses "free trial" without a license key, THE System SHALL activate as FREE version automatically
|
||||
7. IF the license key is invalid or already activated on another device, THEN THE System SHALL display an appropriate error message
|
||||
8. WHEN activation completes, THE System SHALL redirect to the main application
|
||||
|
||||
### Requirement 3: 硬件指纹生成
|
||||
|
||||
**User Story:** As a 系统, I want to 生成稳定的硬件指纹, so that 许可证可以绑定到特定设备而不受常见硬件升级影响。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE System SHALL generate a hardware fingerprint using machine ID, hostname, and primary MAC address
|
||||
2. THE System SHALL NOT include RAM information in the hardware fingerprint
|
||||
3. THE System SHALL NOT include non-system disk information in the hardware fingerprint
|
||||
4. THE System SHALL generate a unique instance ID on first activation and store it persistently
|
||||
5. WHEN hardware components change, THE System SHALL use a weighted matching algorithm with a 60% threshold
|
||||
6. WHEN the fingerprint match score falls below 60%, THE System SHALL require re-activation
|
||||
|
||||
### Requirement 4: 许可证验证
|
||||
|
||||
**User Story:** As a 系统, I want to 定期验证许可证状态, so that 可以确保只有合法用户使用付费功能。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the system starts, THE Client_Instance SHALL verify the license with the License_Server
|
||||
2. WHILE the system is running, THE Client_Instance SHALL send heartbeat requests every hour
|
||||
3. WHEN the License_Server receives a verification request, THE System SHALL check license validity, expiration, and hardware fingerprint
|
||||
4. WHEN verification succeeds, THE License_Server SHALL return the current license configuration
|
||||
5. WHEN verification fails due to network issues, THE Client_Instance SHALL use cached license data
|
||||
6. WHEN the system operates offline, THE Client_Instance SHALL allow a grace period of 7 days
|
||||
7. IF the grace period expires without successful verification, THEN THE System SHALL enter read-only mode
|
||||
|
||||
### Requirement 5: 功能限制执行
|
||||
|
||||
**User Story:** As a 系统, I want to 根据许可证类型限制功能, so that 免费用户无法使用付费功能。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a FREE user attempts to create the 11th user, THE System SHALL block the operation and display an upgrade prompt
|
||||
2. WHEN a FREE user attempts to upload the 101st material, THE System SHALL block the operation and display an upgrade prompt
|
||||
3. WHEN a FREE user accesses custom template settings, THE System SHALL hide or disable the functionality
|
||||
4. WHEN a FREE user accesses department isolation settings, THE System SHALL hide or disable the functionality
|
||||
5. WHEN checking feature availability, THE System SHALL query the cached license configuration
|
||||
6. THE System SHALL display current usage statistics (users/materials count) in the settings page
|
||||
|
||||
### Requirement 6: 客户管理后台
|
||||
|
||||
**User Story:** As a 平台管理员, I want to 管理所有客户的许可证, so that 可以进行销售和客户服务。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Admin_Portal SHALL require authentication to access
|
||||
2. WHEN an admin views the dashboard, THE System SHALL display total customers count by license type
|
||||
3. WHEN an admin views the customer list, THE System SHALL display company name, license type, status, and expiration date
|
||||
4. WHEN an admin creates a new license, THE System SHALL generate a unique license key
|
||||
5. WHEN an admin upgrades a customer's license, THE System SHALL update the license type immediately
|
||||
6. WHEN an admin disables a license, THE System SHALL mark it as inactive and reject future verifications
|
||||
7. WHEN an admin resets hardware fingerprint, THE System SHALL allow the license to be activated on a new device
|
||||
8. THE Admin_Portal SHALL display usage statistics for each customer (user count, material count, last heartbeat)
|
||||
|
||||
### Requirement 7: 异常检测
|
||||
|
||||
**User Story:** As a 平台管理员, I want to 检测许可证滥用行为, so that 可以防止盗版和许可证共享。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the same license key sends heartbeats from multiple different hardware fingerprints within 24 hours, THE System SHALL flag it as suspicious
|
||||
2. WHEN suspicious activity is detected, THE Admin_Portal SHALL display an alert
|
||||
3. WHEN an admin reviews suspicious activity, THE System SHALL show the list of hardware fingerprints and their locations
|
||||
4. THE Admin_Portal SHALL allow admins to whitelist legitimate multi-device scenarios
|
||||
|
||||
### Requirement 8: 许可证信息展示
|
||||
|
||||
**User Story:** As a 客户, I want to 查看当前许可证状态, so that 可以了解使用情况和到期时间。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE System SHALL display license information in the system settings page
|
||||
2. THE System SHALL show license type, activation status, expiration date, and feature availability
|
||||
3. THE System SHALL show current usage (user count, material count) compared to limits
|
||||
4. WHEN the license is about to expire within 30 days, THE System SHALL display a renewal reminder
|
||||
5. THE System SHALL provide a link to upgrade or renew the license
|
||||
|
||||
### Requirement 9: 安全通信
|
||||
|
||||
**User Story:** As a 系统, I want to 确保许可证通信安全, so that 验证过程无法被篡改或伪造。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE System SHALL use HTTP with application-layer encryption for communication between Client_Instance and License_Server
|
||||
2. THE System SHALL encrypt request payloads using AES-256-GCM before transmission
|
||||
3. THE System SHALL sign all license data using RSA asymmetric encryption
|
||||
4. THE Client_Instance SHALL verify the signature of license data using the embedded public key
|
||||
5. THE System SHALL include timestamps in requests to prevent replay attacks
|
||||
6. IF signature verification fails, THEN THE System SHALL reject the license data and log the incident
|
||||
7. THE System SHALL support upgrading to HTTPS when SSL certificates are configured
|
||||
|
||||
### Requirement 10: 数据持久化
|
||||
|
||||
**User Story:** As a 系统, I want to 安全存储许可证数据, so that 系统重启后无需重新激活。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Client_Instance SHALL store license data in the database with encryption
|
||||
2. THE Client_Instance SHALL store the instance ID persistently across restarts
|
||||
3. THE License_Server SHALL store all customer and license data in a PostgreSQL database
|
||||
4. THE System SHALL backup license data regularly to prevent data loss
|
||||
5. WHEN license data is corrupted, THE System SHALL attempt to recover from the License_Server
|
||||
|
||||
### Requirement 11: 代码混淆保护
|
||||
|
||||
**User Story:** As a 系统, I want to 对关键代码进行混淆处理, so that 增加逆向工程和破解的难度。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Build_Process SHALL obfuscate license verification related source files during production build
|
||||
2. THE Build_Process SHALL obfuscate hardware fingerprint generation source files during production build
|
||||
3. THE Build_Process SHALL obfuscate cryptographic signature verification source files during production build
|
||||
4. THE Build_Process SHALL NOT obfuscate business logic components to maintain debuggability
|
||||
5. WHEN obfuscation is applied, THE System SHALL maintain identical functionality to non-obfuscated code
|
||||
6. THE Build_Process SHALL use control flow flattening and string encoding techniques
|
||||
7. THE Build_Process SHALL generate source maps separately for internal debugging purposes only
|
||||
8. WHEN the production build completes, THE System SHALL verify that obfuscated code executes correctly
|
||||
|
||||
### Requirement 12: 自助安装(Agent 方案)
|
||||
|
||||
**User Story:** As a 客户, I want to 通过简单的方式自助部署和更新系统, so that 无需手动配置 Docker 命令,也不需要联系技术支持。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE System SHALL provide a lightweight Agent Docker image (flnxi/flnxi-agent) on Docker Hub for customers to download
|
||||
2. THE Agent image SHALL be compatible with mainstream NAS systems including Synology, QNAP, and fnOS
|
||||
3. WHEN the Agent container starts, THE Agent SHALL provide a Web-based management interface on port 9000
|
||||
4. THE Agent Web interface SHALL allow customer to configure system port (default 4320)
|
||||
5. THE Agent SHALL NOT perform license validation (license activation is handled by the material system on first startup)
|
||||
6. WHEN customer clicks install, THE Agent SHALL pull the FLNXI material system image from harbor.flnxi.com/flnxi/material/flnxi-material
|
||||
7. THE Agent SHALL create and start the FLNXI material system container with configured settings
|
||||
8. THE Agent Web interface SHALL display installation progress with clear status messages
|
||||
9. WHEN installation completes, THE Agent SHALL display the access URL for the system
|
||||
10. IF installation fails, THEN THE Agent SHALL display error details and provide retry option
|
||||
11. THE Agent SHALL provide system update functionality to pull and deploy latest version
|
||||
12. THE Agent Web interface SHALL display current system version and available updates
|
||||
13. THE Agent SHALL NOT collect or transmit any customer usage data
|
||||
14. THE Agent container SHOULD be kept running for future system updates
|
||||
|
||||
### Requirement 13: 版本管理
|
||||
|
||||
**User Story:** As a 平台管理员, I want to 管理系统版本发布, so that 可以有序地向客户推送更新。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE System SHALL use semantic versioning format (vMAJOR.MINOR.PATCH) for all releases
|
||||
2. THE License_Server SHALL maintain version information including latest version and minimum required version
|
||||
3. WHEN a new version is released, THE Admin_Portal SHALL allow setting the minimum required version
|
||||
4. THE License_Server SHALL provide a version check API that returns latest version, minimum required version, and release notes
|
||||
5. THE System SHALL store version number in /app/VERSION.txt within the Docker image
|
||||
6. THE Harbor registry SHALL retain at least the 3 most recent versions for rollback purposes
|
||||
|
||||
### Requirement 14: 系统更新
|
||||
|
||||
**User Story:** As a 客户, I want to 方便地更新系统到最新版本, so that 可以获得新功能和安全修复。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the Agent checks for updates, THE Agent SHALL compare current version with latest version from License_Server
|
||||
2. WHEN an optional update is available, THE Agent SHALL display update notification with version number and release notes
|
||||
3. WHEN a forced update is required, THE System SHALL block access to main application until update is completed
|
||||
4. WHEN customer initiates update, THE Agent SHALL pull new image using Docker incremental layer download
|
||||
5. THE Agent SHALL display download progress and estimated time during update
|
||||
6. WHEN update completes, THE Agent SHALL restart the system container automatically
|
||||
7. IF update fails, THEN THE Agent SHALL automatically rollback to previous version
|
||||
8. THE Agent Web interface SHALL provide manual rollback option to any of the 3 most recent versions
|
||||
|
||||
### Requirement 15: 更新回滚机制
|
||||
|
||||
**User Story:** As a 客户, I want to 在更新失败时自动恢复, so that 系统不会因为更新问题而无法使用。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN starting an update, THE Agent SHALL backup current container configuration
|
||||
2. WHEN starting an update, THE Agent SHALL rename current container instead of deleting it
|
||||
3. WHEN new container starts, THE Agent SHALL perform health check by calling /api/health endpoint
|
||||
4. WHEN health check fails within 60 seconds, THE Agent SHALL trigger automatic rollback
|
||||
5. WHEN rollback is triggered, THE Agent SHALL stop new container, restore backup container, and restart it
|
||||
6. WHEN rollback completes, THE Agent SHALL display message indicating rollback reason and restored version
|
||||
7. THE Agent Web interface SHALL allow manual rollback with version selection
|
||||
@ -1,385 +0,0 @@
|
||||
# Implementation Plan
|
||||
|
||||
## Phase 1: 核心基础设施
|
||||
|
||||
- [x] 1. 设置许可证服务器项目结构
|
||||
- [x] 1.1 创建独立的 Next.js 项目 `license-server`
|
||||
- 初始化项目,配置 TypeScript、Prisma、PostgreSQL
|
||||
- 创建基础目录结构:`app/api/`, `lib/`, `prisma/`
|
||||
- _Requirements: 6.1, 10.3_
|
||||
- [x] 1.2 配置数据库 Schema
|
||||
- 创建 customers、licenses、activations、heartbeat_logs、alerts 表
|
||||
- 配置 Prisma 模型和关系
|
||||
- _Requirements: 10.3_
|
||||
- [x] 1.3 生成 RSA 密钥对
|
||||
- 创建密钥生成脚本
|
||||
- 配置环境变量存储私钥
|
||||
- 导出公钥供客户端使用
|
||||
- _Requirements: 9.3, 9.4_
|
||||
|
||||
- [x] 2. 实现加密服务模块
|
||||
- [x] 2.1 实现服务器端加密服务 `license-server/lib/crypto.ts`
|
||||
- RSA 签名生成函数
|
||||
- AES-256-GCM 加密/解密函数
|
||||
- 请求签名验证函数
|
||||
- _Requirements: 9.2, 9.3, 9.5_
|
||||
- [x] 2.3 实现客户端加密服务 `lib/license/crypto-service.ts`
|
||||
- RSA 签名验证函数(使用嵌入的公钥)
|
||||
- AES-256-GCM 加密/解密函数
|
||||
- 本地数据加密存储函数
|
||||
- _Requirements: 9.4, 10.1_
|
||||
|
||||
- [x] 3. Checkpoint - 确保所有测试通过
|
||||
- 测试通过:crypto-service.test.ts, license-types.test.ts, activate/route.test.ts
|
||||
|
||||
## Phase 2: 许可证类型和配置
|
||||
|
||||
- [x] 4. 实现许可证类型定义
|
||||
- [x] 4.1 创建许可证类型和配置接口 `lib/license/types.ts`
|
||||
- 定义 LicenseType 枚举(FREE, STANDARD, PRO)
|
||||
- 定义 LicenseConfig、SignedLicense、HardwareFingerprint 接口
|
||||
- 定义各版本的功能限制配置
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8_
|
||||
- [x] 4.2 实现许可证配置生成函数
|
||||
- 根据许可证类型生成对应的功能配置
|
||||
- 实现限制值计算逻辑
|
||||
- _Requirements: 1.1-1.8_
|
||||
|
||||
## Phase 3: 硬件指纹服务
|
||||
|
||||
- [x] 5. 实现硬件指纹服务
|
||||
- [x] 5.1 实现硬件指纹生成 `lib/license/fingerprint-service.ts`
|
||||
- 获取 machine-id
|
||||
- 获取 hostname
|
||||
- 获取主网卡 MAC 地址
|
||||
- 生成/获取持久化的 instance ID
|
||||
- _Requirements: 3.1, 3.2, 3.3, 3.4_
|
||||
- [x] 5.3 实现指纹匹配算法
|
||||
- 实现加权匹配分数计算
|
||||
- 实现 60% 阈值判断逻辑
|
||||
- _Requirements: 3.5, 3.6_
|
||||
|
||||
- [x] 6. Checkpoint - 确保所有测试通过
|
||||
- 测试通过 ✓
|
||||
|
||||
## Phase 4: 许可证服务器 API
|
||||
|
||||
- [x] 7. 实现许可证激活 API
|
||||
- [x] 7.1 实现激活接口 `license-server/app/api/license/activate/route.ts`
|
||||
- 验证许可证密钥格式和存在性
|
||||
- 检查是否已激活
|
||||
- 绑定硬件指纹
|
||||
- 生成签名后的许可证数据
|
||||
- _Requirements: 2.2, 2.3, 2.4_
|
||||
- [x] 7.2 实现许可证密钥生成函数
|
||||
- 生成 XXXX-XXXX-XXXX-XXXX 格式的唯一密钥
|
||||
- _Requirements: 6.4_
|
||||
|
||||
- [x] 8. 实现许可证验证 API
|
||||
- [x] 8.1 实现验证接口 `license-server/app/api/license/verify/route.ts`
|
||||
- 验证 instance ID 和硬件指纹
|
||||
- 检查许可证有效性和过期时间
|
||||
- 返回签名后的许可证配置
|
||||
- _Requirements: 4.3, 4.4_
|
||||
|
||||
- [x] 9. 实现心跳 API
|
||||
- [x] 9.1 实现心跳接口 `license-server/app/api/license/heartbeat/route.ts`
|
||||
- 记录心跳日志
|
||||
- 更新使用统计
|
||||
- 检测异常(多设备使用)
|
||||
- _Requirements: 4.2, 7.1_
|
||||
|
||||
- [x] 10. 实现版本检查 API
|
||||
- [x] 10.1 实现版本检查接口 `license-server/app/api/version/check/route.ts`
|
||||
- 返回最新版本和最低要求版本
|
||||
- _Requirements: 13.4, 14.1_
|
||||
|
||||
- [x] 11. Checkpoint - 确保所有测试通过
|
||||
- API 测试通过(activate, verify, heartbeat, version/check)
|
||||
|
||||
## Phase 5: 客户端许可证服务
|
||||
|
||||
- [x] 12. 实现客户端许可证服务
|
||||
- [x] 12.1 实现许可证服务 `lib/license/license-service.ts`
|
||||
- activate() - 激活许可证
|
||||
- verify() - 验证许可证
|
||||
- heartbeat() - 发送心跳
|
||||
- getLicense() - 获取当前配置
|
||||
- isFeatureEnabled() - 检查功能可用性
|
||||
- checkLimit() - 检查限制
|
||||
- _Requirements: 2.2, 2.5, 4.1, 4.2, 4.5, 4.6, 5.5_
|
||||
- [x] 12.2 实现本地许可证存储
|
||||
- 添加 Prisma 模型存储许可证数据
|
||||
- 实现加密存储和读取
|
||||
- _Requirements: 10.1, 10.2_
|
||||
- [x] 12.3 实现宽限期逻辑
|
||||
- 离线时使用缓存数据
|
||||
- 7 天宽限期计算
|
||||
- 宽限期过期进入只读模式
|
||||
- _Requirements: 4.5, 4.6, 4.7_
|
||||
|
||||
- [x] 13. 实现功能限制执行
|
||||
- [x] 13.1 实现用户数量限制检查
|
||||
- 在用户创建前检查限制 `app/api/auth/register/route.ts`
|
||||
- 显示升级提示
|
||||
- _Requirements: 5.1_
|
||||
- [x] 13.2 实现素材数量限制检查
|
||||
- 在素材上传前检查限制 `app/api/materials/upload/route.ts`
|
||||
- 显示升级提示
|
||||
- _Requirements: 5.2_
|
||||
- [x] 13.3 实现功能开关检查
|
||||
- 自定义模板功能检查 `app/api/admin/upload-templates/route.ts`
|
||||
- 部门隔离功能检查 `app/api/departments/route.ts`
|
||||
- 系统设置功能检查 `app/api/system-settings/route.ts`
|
||||
- _Requirements: 5.3, 5.4_
|
||||
|
||||
- [x] 14. Checkpoint - 确保所有测试通过
|
||||
- 测试通过 ✓
|
||||
|
||||
## Phase 6: 客户端 UI 集成
|
||||
|
||||
- [x] 15. 实现激活页面
|
||||
- [x] 15.1 创建激活页面 `app/activate/page.tsx`
|
||||
- 许可证密钥输入表单
|
||||
- 免费试用选项
|
||||
- 激活状态显示
|
||||
- 错误提示
|
||||
- _Requirements: 2.1, 2.6, 2.7, 2.8_
|
||||
- [x] 15.2 实现许可证中间件 `middleware.ts`
|
||||
- 检查激活状态(通过 cookie)
|
||||
- 未激活时重定向到激活页面
|
||||
- 激活成功后设置 cookie
|
||||
- _Requirements: 2.1_
|
||||
|
||||
- [x] 16. 实现许可证信息展示
|
||||
- [x] 16.1 创建许可证信息组件 `components/LicenseInfo.tsx`
|
||||
- 显示许可证类型和状态
|
||||
- 显示到期时间
|
||||
- 显示使用统计
|
||||
- 显示续期/升级链接
|
||||
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5_
|
||||
- [x] 16.2 集成到系统设置页面
|
||||
- 在设置页面添加许可证信息区域
|
||||
- 显示功能可用性状态
|
||||
- 支持暗色模式
|
||||
- _Requirements: 5.6, 8.1_
|
||||
|
||||
## Phase 7: 客户管理后台
|
||||
|
||||
- [x] 17. 实现管理后台认证
|
||||
- [x] 17.1 创建管理员登录页面 `license-server/app/admin/login/page.tsx`
|
||||
- 登录表单
|
||||
- JWT Session 管理
|
||||
- _Requirements: 6.1_
|
||||
- [x] 17.2 实现认证 API
|
||||
- POST /api/admin/auth/login - 登录
|
||||
- POST /api/admin/auth/logout - 登出
|
||||
- lib/auth.ts - 认证服务
|
||||
- _Requirements: 6.1_
|
||||
- [x] 17.3 创建初始化管理员脚本
|
||||
- scripts/init-admin.ts
|
||||
- _Requirements: 6.1_
|
||||
|
||||
- [x] 18. 实现客户管理功能
|
||||
- [x] 18.1 创建客户列表页面 `license-server/app/admin/customers/page.tsx`
|
||||
- 客户列表展示
|
||||
- 搜索和筛选
|
||||
- 添加客户弹窗
|
||||
- _Requirements: 6.3_
|
||||
- [x] 18.2 实现客户 CRUD API
|
||||
- GET/POST /api/admin/customers
|
||||
- _Requirements: 6.3_
|
||||
|
||||
- [x] 19. 实现许可证管理功能
|
||||
- [x] 19.1 实现许可证管理 API
|
||||
- GET/POST /api/admin/licenses - 列表和生成
|
||||
- POST /api/admin/licenses/:id/disable - 禁用许可证
|
||||
- POST /api/admin/licenses/:id/reset - 重置硬件绑定
|
||||
- _Requirements: 6.4, 6.5, 6.6, 6.7_
|
||||
- [x] 19.2 创建许可证管理界面 `license-server/app/admin/licenses/page.tsx`
|
||||
- 许可证列表
|
||||
- 生成新许可证表单
|
||||
- 许可证操作按钮
|
||||
- _Requirements: 6.4, 6.5, 6.6, 6.7_
|
||||
|
||||
- [x] 20. 实现仪表盘和监控
|
||||
- [x] 20.1 创建仪表盘页面 `license-server/app/admin/page.tsx`
|
||||
- 客户统计(按许可证类型)
|
||||
- 最近激活列表
|
||||
- 系统状态
|
||||
- _Requirements: 6.2, 6.8_
|
||||
- [x] 20.2 实现异常告警功能 `license-server/app/admin/alerts/page.tsx`
|
||||
- 告警列表页面
|
||||
- 告警详情
|
||||
- 处理/忽略操作
|
||||
- _Requirements: 7.2, 7.3, 7.4_
|
||||
|
||||
- [x] 21. Checkpoint - 确保所有测试通过
|
||||
- 管理后台功能测试通过 ✓
|
||||
|
||||
## Phase 8: 代码混淆
|
||||
|
||||
- [x] 22. 配置代码混淆
|
||||
- [x] 22.1 创建混淆配置 `scripts/obfuscate-config.js`
|
||||
- 配置混淆选项(控制流扁平化、字符串编码)
|
||||
- 指定需要混淆的文件列表
|
||||
- 配置保留名称和字符串
|
||||
- _Requirements: 11.1, 11.2, 11.3, 11.6_
|
||||
- [x] 22.2 创建混淆构建脚本 `scripts/build-obfuscated.js`
|
||||
- 构建后自动混淆敏感代码
|
||||
- 添加 `npm run build:prod` 命令
|
||||
- _Requirements: 11.4, 11.7_
|
||||
- [x] 22.3 安装依赖并验证
|
||||
- 安装 javascript-obfuscator ✓
|
||||
- 运行 `npm run build:prod` 混淆 74 个文件 ✓
|
||||
- 测试验证功能正确性 ✓
|
||||
- _Requirements: 11.5, 11.8_
|
||||
|
||||
## Phase 9: 自助安装 Agent
|
||||
|
||||
- [x] 23. 设置 Agent 项目
|
||||
- [x] 23.1 初始化 Agent 项目 `flnxi-agent/`
|
||||
- 创建 Node.js + Express + TypeScript 项目
|
||||
- 配置 dockerode、socket.io 依赖
|
||||
- 创建 Dockerfile(多架构支持)
|
||||
- _Requirements: 12.1, 12.2_
|
||||
- [x] 23.2 创建前端安装向导和管理面板
|
||||
- Welcome.tsx - 欢迎页面
|
||||
- Config.tsx - 端口配置
|
||||
- Installing.tsx - 安装进度
|
||||
- Complete.tsx - 完成页面
|
||||
- Dashboard.tsx - 管理面板(系统状态、更新、重启)
|
||||
- _Requirements: 12.3, 12.4, 12.8, 12.11, 12.12_
|
||||
|
||||
- [x] 24. 实现 Agent 后端功能
|
||||
- [x] 24.1 实现 Web 服务 `flnxi-agent/src/server.ts`
|
||||
- Express 静态文件服务
|
||||
- Socket.io 实时进度推送
|
||||
- _Requirements: 12.3, 12.8_
|
||||
- [x] 24.2 实现 Docker 操作 `flnxi-agent/src/installer.ts`
|
||||
- 拉取 FLNXI 素材系统镜像(带进度)
|
||||
- 拉取依赖镜像(postgres、minio 等)
|
||||
- 创建并启动容器
|
||||
- 许可证验证由素材系统首次启动时处理
|
||||
- _Requirements: 12.5, 12.6, 12.7_
|
||||
|
||||
- [x] 25. 实现安装和更新流程
|
||||
- [x] 25.1 实现安装 API `flnxi-agent/src/server.ts`
|
||||
- POST /api/install - 开始安装
|
||||
- WebSocket 推送安装进度
|
||||
- 支持从 Harbor 拉取镜像
|
||||
- _Requirements: 12.4, 12.8_
|
||||
- [x] 25.2 实现系统更新 API `flnxi-agent/src/system-manager.ts`
|
||||
- GET /api/status - 获取系统状态和版本
|
||||
- GET /api/check-update - 检查最新版本
|
||||
- POST /api/update - 更新系统(带回滚)
|
||||
- _Requirements: 12.12, 12.13, 14.1, 14.2, 14.4_
|
||||
- [x] 25.3 实现强制更新检测
|
||||
- 比较当前版本与 minRequiredVersion
|
||||
- Dashboard 显示更新提示
|
||||
- _Requirements: 14.3_
|
||||
- [x] 25.4 实现更新回滚机制
|
||||
- 更新前备份容器配置
|
||||
- 重命名旧容器而非删除
|
||||
- 健康检查失败时自动回滚
|
||||
- _Requirements: 15.1, 15.2, 15.3, 15.4_
|
||||
- [x] 25.5 实现错误处理
|
||||
- 安装/更新失败时显示错误详情
|
||||
- 提供重试选项
|
||||
- _Requirements: 12.11_
|
||||
|
||||
- [x] 26. 构建和发布
|
||||
- [x] 26.1 Agent 自更新功能
|
||||
- `flnxi-agent/src/system-manager.ts` - 添加 checkAgentUpdate, updateAgent 方法
|
||||
- `flnxi-agent/src/server.ts` - 添加 Agent 更新 API
|
||||
- `flnxi-agent/frontend/src/pages/Dashboard.tsx` - 添加 Agent 更新 UI
|
||||
- `license-server/app/api/version/agent/route.ts` - Agent 版本检查 API
|
||||
- `license-server/prisma/schema.prisma` - 添加 AgentVersion 模型
|
||||
- _Requirements: 12.12, 12.13_
|
||||
- [x] 26.2 发布管理工具
|
||||
- `scripts/发布管理.ps1` - 统一发布管理脚本
|
||||
- 支持测试版部署、正式版发布、Agent 发布
|
||||
- 正式版操作需要二次确认
|
||||
- _Requirements: 12.1, 12.2_
|
||||
- [x] 26.3 测试安装流程
|
||||
- 本地测试 Agent 构建和启动 ✓
|
||||
- API 测试:/api/status 正常 ✓
|
||||
- 前端页面加载正常 ✓
|
||||
- 待实际 NAS 设备测试(飞牛 fnOS、群晖)
|
||||
- _Requirements: 12.1-12.13_
|
||||
|
||||
## Phase 10: 部署和集成
|
||||
|
||||
- [x] 27. 实现版本管理 API
|
||||
- [x] 27.1 版本信息数据模型(已在 Phase 1 完成)
|
||||
- versions 表已在 prisma/schema.prisma 中定义
|
||||
- _Requirements: 13.1, 13.2_
|
||||
- [x] 27.2 版本检查 API(已在 Phase 4 完成)
|
||||
- GET /api/version/check
|
||||
- _Requirements: 13.4, 14.1_
|
||||
- [x] 27.3 实现版本管理后台 `license-server/app/admin/versions/page.tsx`
|
||||
- 版本列表页面
|
||||
- 发布新版本表单
|
||||
- _Requirements: 13.3_
|
||||
|
||||
- [x] 28. 部署许可证服务器配置
|
||||
- [x] 28.1 创建 Docker Compose 配置 `license-server/docker-compose.yml`
|
||||
- license-server 服务
|
||||
- PostgreSQL 数据库
|
||||
- _Requirements: 10.3_
|
||||
- [x] 28.2 创建 Dockerfile `license-server/Dockerfile`
|
||||
- 多阶段构建
|
||||
- _Requirements: 10.3_
|
||||
- [x] 28.3 部署到 45.249.208.118
|
||||
- 配置环境变量(RSA 私钥、数据库连接)
|
||||
- 启动服务(端口 9527,域名 lic.flnxi.com)
|
||||
- 验证 API 可访问 ✓
|
||||
- 创建管理员账号(admin/admin123)
|
||||
- _Requirements: 10.3_
|
||||
|
||||
- [x] 29. 集成到素材系统
|
||||
- [x] 29.1 更新素材系统 middleware.ts(已在 Phase 6 完成)
|
||||
- 添加许可证检查逻辑
|
||||
- 配置激活页面路由
|
||||
- _Requirements: 2.1_
|
||||
- [x] 29.2 集成功能限制检查
|
||||
- 用户注册接口添加限制检查 `app/api/auth/register/route.ts`
|
||||
- 素材上传接口添加限制检查 `app/api/materials/upload/route.ts`
|
||||
- 创建限制检查工具 `lib/license/check-limits.ts`
|
||||
- _Requirements: 5.1, 5.2, 5.3, 5.4_
|
||||
- [x] 29.3 配置心跳定时任务 `instrumentation.ts`
|
||||
- 启动时验证许可证
|
||||
- 每小时发送心跳
|
||||
- _Requirements: 4.1, 4.2_
|
||||
- [x] 29.4 添加版本文件
|
||||
- 在 Dockerfile 中添加 VERSION.txt
|
||||
- 构建时通过 ARG 写入版本号
|
||||
- _Requirements: 13.5_
|
||||
|
||||
- [x] 30. 同步 Runtime 镜像到 Harbor
|
||||
- [x] 30.1 创建镜像同步脚本 `scripts/sync-harbor-images.ps1`
|
||||
- postgres:16-alpine
|
||||
- minio/minio:latest
|
||||
- minio/mc:latest
|
||||
- redis:7-alpine
|
||||
- nginx:alpine
|
||||
- node:20-alpine
|
||||
- _Requirements: 12.7_
|
||||
- [x] 30.2 创建镜像构建脚本 `scripts/build-material-image.ps1`
|
||||
- 支持 dev/test/release 三种环境
|
||||
- 测试版推送到 material-dev/ 目录
|
||||
- 正式版推送到 material/ 目录
|
||||
- _Requirements: 13.6_
|
||||
- [x] 30.3 创建镜像管理文档 `docs/harbor-image-management.md`
|
||||
- 镜像目录结构说明
|
||||
- 工作流程指南
|
||||
- 版本管理规范
|
||||
- _Requirements: 13.6_
|
||||
- [x] 30.4 执行镜像同步(已完成)
|
||||
- Runtime 镜像已同步到 Harbor
|
||||
- _Requirements: 12.7_
|
||||
|
||||
- [x] 30. Final Checkpoint - 确保所有测试通过
|
||||
- 单元测试:56 passed
|
||||
- API 集成测试:全部通过
|
||||
- 许可证服务器部署验证:✓
|
||||
@ -1,909 +0,0 @@
|
||||
# 自定义上传模板系统优化 - 技术设计文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述了FLNXI素材库自定义上传模板系统的优化设计方案。该系统是整个平台的核心基础设施,负责定义素材的上传表单结构、元数据字段、分类筛选和共享展示。
|
||||
|
||||
### 设计目标
|
||||
|
||||
1. **稳定性**: 确保模板系统在各种场景下都能可靠运行,不会因为数据异常导致系统崩溃
|
||||
2. **性能**: 优化模板加载、字段同步、分类筛选等关键操作的性能
|
||||
3. **可维护性**: 提供清晰的代码结构和完善的错误处理,便于后续维护和扩展
|
||||
4. **向后兼容**: 确保优化不会破坏现有功能,支持旧数据的平滑迁移
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 系统架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 前端层 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ UploadModal │ TemplateManager │ CategoriesTab │ 首页 │
|
||||
│ (上传表单) │ (模板管理) │ (分类管理) │ (筛选) │
|
||||
└────────┬──────────────┬──────────────┬──────────────┬───────┘
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ API层 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ /api/materials/upload │
|
||||
│ /api/upload-templates │
|
||||
│ /api/admin/upload-templates │
|
||||
│ /api/template-categories │
|
||||
│ /api/category-fields │
|
||||
└────────┬──────────────┬──────────────┬──────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 业务逻辑层 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ template-validator.ts │ field-pool.ts │ upload-template-│
|
||||
│ (模板验证) │ (字段池管理) │ presets.ts │
|
||||
│ │ │ (内置模板) │
|
||||
└────────┬────────────────┬─────────────────┬─────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 数据访问层 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Prisma ORM │
|
||||
│ - UploadTemplate │
|
||||
│ - UploadField │
|
||||
│ - FieldDefinition │
|
||||
│ - FieldOption │
|
||||
│ - Material │
|
||||
└────────┬────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 数据库层 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ PostgreSQL │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 核心组件
|
||||
|
||||
#### 1. 模板验证器 (template-validator.ts)
|
||||
|
||||
**职责**: 统一处理内置模板和自定义模板的验证逻辑
|
||||
|
||||
**核心函数**:
|
||||
```typescript
|
||||
// 验证模板ID是否有效
|
||||
validateTemplateId(templateId: string | null): Promise<TemplateValidationResult>
|
||||
|
||||
// 检查是否为内置模板
|
||||
isBuiltinTemplate(templateId: string): boolean
|
||||
|
||||
// 提取内置模板key
|
||||
extractBuiltinTemplateKey(templateId: string): BuiltinTemplateKey | null
|
||||
|
||||
// 获取模板完整信息(包括字段)
|
||||
getTemplateWithFields(templateId: string): Promise<Template | null>
|
||||
```
|
||||
|
||||
**验证流程**:
|
||||
```
|
||||
输入: templateId
|
||||
│
|
||||
├─ 是否为空? ──Yes──> 返回 valid=true (向后兼容)
|
||||
│
|
||||
└─ No
|
||||
│
|
||||
├─ 是否以 "builtin-" 开头?
|
||||
│ │
|
||||
│ ├─ Yes (内置模板)
|
||||
│ │ │
|
||||
│ │ └─ 验证 key 是否在预设列表中
|
||||
│ │ │
|
||||
│ │ ├─ Yes ──> 返回 valid=true
|
||||
│ │ └─ No ──> 返回 valid=false, error="无效的内置模板"
|
||||
│ │
|
||||
│ └─ No (自定义模板)
|
||||
│ │
|
||||
│ └─ 从数据库查询
|
||||
│ │
|
||||
│ ├─ 存在且状态为active ──> 返回 valid=true
|
||||
│ └─ 不存在或状态非active ──> 返回 valid=false, error="模板不存在或未启用"
|
||||
```
|
||||
|
||||
#### 2. 字段池管理器 (field-pool.ts)
|
||||
|
||||
**职责**: 管理模板字段与字段池之间的同步
|
||||
|
||||
**核心函数**:
|
||||
```typescript
|
||||
// 从模板同步字段到字段池
|
||||
syncFieldPoolFromTemplates(): Promise<void>
|
||||
|
||||
// 获取所有字段池记录
|
||||
getFieldPoolRecords(): Promise<FieldDefinition[]>
|
||||
|
||||
// 获取已启用的筛选字段
|
||||
getEnabledFilterFields(): Promise<FilterField[]>
|
||||
|
||||
// 更新字段的启用状态
|
||||
updateFieldFilterState(fieldKey: string, enabled: boolean): Promise<void>
|
||||
|
||||
// 规范化字段选项值
|
||||
normalizeFieldOptionValue(option: string): string
|
||||
|
||||
// 解析静态选项
|
||||
parseOptionsStatic(raw: string): string[]
|
||||
|
||||
// 格式化字段选项值
|
||||
formatFieldOptionValues(optionRecords: FieldOption[]): string[]
|
||||
```
|
||||
|
||||
**同步流程**:
|
||||
```
|
||||
触发: 模板创建/更新/删除
|
||||
│
|
||||
├─ 1. 扫描所有模板的 addToCategory=true 字段
|
||||
│ │
|
||||
│ └─ 聚合相同key的字段
|
||||
│ │
|
||||
│ └─ 合并选项列表
|
||||
│
|
||||
├─ 2. 更新 FieldDefinition 表
|
||||
│ │
|
||||
│ ├─ 新增字段: 创建记录
|
||||
│ ├─ 已存在字段: 更新 sourceTemplateIds
|
||||
│ └─ 孤立字段: 删除记录
|
||||
│
|
||||
├─ 3. 同步 FieldOption 表
|
||||
│ │
|
||||
│ ├─ 清洗选项值 (去括号、trim、去重)
|
||||
│ ├─ 新增选项: 创建记录
|
||||
│ ├─ 已存在选项: 更新 orderIndex
|
||||
│ └─ 废弃选项: 删除记录
|
||||
│
|
||||
└─ 4. 返回结果
|
||||
```
|
||||
|
||||
#### 3. 内置模板预设 (upload-template-presets.ts)
|
||||
|
||||
**职责**: 定义和生成内置模板
|
||||
|
||||
**内置模板列表**:
|
||||
- `builtin-3dmax-material`: 3DMAX 素材模板
|
||||
- `builtin-psd-material`: PSD 素材模板
|
||||
- `builtin-ai-material`: AI 工作流
|
||||
- `builtin-video-material`: 视频素材模板
|
||||
- `builtin-model-material`: 模特素材模板
|
||||
- `builtin-other-material`: 其他素材模板
|
||||
|
||||
**标准字段**:
|
||||
```typescript
|
||||
{
|
||||
productModel: SPU型号 (text, required)
|
||||
categoryName: 使用店铺 (select, required, addToCategory)
|
||||
sceneName: 场景类别 (select, required, addToCategory)
|
||||
platformName: 平台分类 (text, addToCategory)
|
||||
renderer: 渲染器 (select, addToCategory)
|
||||
brandName: 品牌名称 (text)
|
||||
seriesName: 系列/产品线 (text)
|
||||
skuCode: SKU编码 (text)
|
||||
materialTexture: 材质/工艺 (text)
|
||||
renderTime: 渲染耗时 (text)
|
||||
imageResolution: 输出分辨率 (text)
|
||||
colorMode: 颜色模式 (text)
|
||||
authorName: 作者 (text)
|
||||
}
|
||||
```
|
||||
|
||||
## 数据模型
|
||||
|
||||
### UploadTemplate (上传模板)
|
||||
|
||||
```typescript
|
||||
{
|
||||
id: string // UUID
|
||||
name: string // 模板名称
|
||||
description: string? // 描述
|
||||
status: string // draft | active | archived
|
||||
materialType: string // 素材类型文案
|
||||
materialKind: string // 素材大类: IMAGE | VIDEO | OTHER
|
||||
version: int // 版本号(用于并发控制)
|
||||
fieldGroups: string? // 字段分组配置 (JSON)
|
||||
displayConfig: string? // 显示配置 (JSON)
|
||||
createdAt: DateTime
|
||||
updatedAt: DateTime
|
||||
fields: UploadField[] // 关联字段
|
||||
}
|
||||
```
|
||||
|
||||
### UploadField (上传字段)
|
||||
|
||||
```typescript
|
||||
{
|
||||
id: string // UUID
|
||||
templateId: string // 所属模板ID
|
||||
key: string // 字段键名
|
||||
label: string // 字段标签
|
||||
type: string // 字段类型: text | textarea | select | number | date | checkbox | file | image
|
||||
required: boolean // 是否必填
|
||||
orderIndex: int // 排序索引
|
||||
layoutWidth: string? // 布局宽度: full | half
|
||||
validations: string? // 验证规则 (JSON)
|
||||
optionsSource: string? // 选项来源
|
||||
optionsStatic: string? // 静态选项 (JSON字符串)
|
||||
allowedExtensions: string? // 允许的文件扩展名
|
||||
visibleOnHomeFilter: boolean // 首页筛选可见
|
||||
isSearchable: boolean // 可搜索
|
||||
isReadOnly: boolean // 只读
|
||||
addToCategory: boolean // 添加到分类管理
|
||||
conditions: string? // 显示条件 (JSON)
|
||||
createdAt: DateTime
|
||||
updatedAt: DateTime
|
||||
}
|
||||
```
|
||||
|
||||
### FieldDefinition (字段池定义)
|
||||
|
||||
```typescript
|
||||
{
|
||||
id: string // UUID
|
||||
key: string // 字段键名 (unique)
|
||||
label: string // 字段标签
|
||||
type: string // 字段类型
|
||||
description: string? // 描述
|
||||
source: string // 来源: standard | custom
|
||||
sourceTemplateIds: string? // 引用此字段的模板ID列表 (JSON数组)
|
||||
isStandard: boolean // 是否标准字段
|
||||
isFilterEnabled: boolean // 是否启用筛选
|
||||
filterDisplayName: string? // 筛选显示名称
|
||||
filterOrder: int // 筛选排序
|
||||
createdAt: DateTime
|
||||
updatedAt: DateTime
|
||||
options: FieldOption[] // 关联选项
|
||||
}
|
||||
```
|
||||
|
||||
### FieldOption (字段选项)
|
||||
|
||||
```typescript
|
||||
{
|
||||
id: string // UUID
|
||||
fieldId: string // 所属字段ID
|
||||
value: string // 选项值
|
||||
label: string // 选项标签
|
||||
orderIndex: int // 排序索引
|
||||
createdAt: DateTime
|
||||
updatedAt: DateTime
|
||||
|
||||
// 联合唯一约束: [fieldId, value]
|
||||
}
|
||||
```
|
||||
|
||||
### Material (素材)
|
||||
|
||||
```typescript
|
||||
{
|
||||
id: string // 素材ID
|
||||
templateId: string? // 关联的模板ID (可为null)
|
||||
metadata: string? // 自定义字段数据 (JSON)
|
||||
|
||||
// 标准字段
|
||||
productModel: string
|
||||
categoryName: string
|
||||
sceneName: string
|
||||
platformName: string
|
||||
materialType: string
|
||||
materialKind: string
|
||||
renderer: string
|
||||
brandName: string?
|
||||
seriesName: string?
|
||||
skuCode: string?
|
||||
materialTexture: string?
|
||||
renderTime: string?
|
||||
imageResolution: string?
|
||||
colorMode: string?
|
||||
authorName: string
|
||||
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
## 组件接口
|
||||
|
||||
### API接口
|
||||
|
||||
#### 1. 获取上传模板列表
|
||||
|
||||
```typescript
|
||||
GET /api/upload-templates
|
||||
|
||||
Response: {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
status: string
|
||||
materialType: string
|
||||
materialKind: string
|
||||
isBuiltin: boolean
|
||||
materialCount: number
|
||||
fields: UploadField[]
|
||||
}[]
|
||||
```
|
||||
|
||||
#### 2. 创建自定义模板
|
||||
|
||||
```typescript
|
||||
POST /api/admin/upload-templates
|
||||
|
||||
Request: {
|
||||
name: string
|
||||
description?: string
|
||||
status?: string
|
||||
materialType: string
|
||||
materialKind: string
|
||||
fieldGroups?: string
|
||||
displayConfig?: string
|
||||
fields: {
|
||||
key: string
|
||||
label: string
|
||||
type: string
|
||||
required: boolean
|
||||
orderIndex: number
|
||||
layoutWidth?: string
|
||||
validations?: string
|
||||
optionsStatic?: string
|
||||
visibleOnHomeFilter: boolean
|
||||
isSearchable: boolean
|
||||
isReadOnly: boolean
|
||||
addToCategory: boolean
|
||||
conditions?: string
|
||||
}[]
|
||||
}
|
||||
|
||||
Response: UploadTemplate
|
||||
```
|
||||
|
||||
#### 3. 更新自定义模板
|
||||
|
||||
```typescript
|
||||
PUT /api/admin/upload-templates/[id]
|
||||
|
||||
Request: {
|
||||
name?: string
|
||||
description?: string
|
||||
status?: string
|
||||
materialType?: string
|
||||
materialKind?: string
|
||||
fieldGroups?: string
|
||||
displayConfig?: string
|
||||
version: number // 用于并发控制
|
||||
fields?: UploadField[]
|
||||
}
|
||||
|
||||
Response: UploadTemplate
|
||||
|
||||
Error (409): {
|
||||
error: "模板已被其他人修改,请刷新后重试"
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 删除自定义模板
|
||||
|
||||
```typescript
|
||||
DELETE /api/admin/upload-templates/[id]
|
||||
|
||||
Response: {
|
||||
success: boolean
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. 获取分类字段
|
||||
|
||||
```typescript
|
||||
GET /api/template-categories
|
||||
|
||||
Response: {
|
||||
key: string
|
||||
label: string
|
||||
defaultLabel: string
|
||||
filterOrder: number
|
||||
options: string[]
|
||||
}[]
|
||||
```
|
||||
|
||||
#### 6. 更新分类字段
|
||||
|
||||
```typescript
|
||||
PUT /api/category-fields
|
||||
|
||||
Request: {
|
||||
fields: {
|
||||
key: string
|
||||
filterOrder: number
|
||||
filterDisplayName: string
|
||||
}[]
|
||||
}
|
||||
|
||||
Response: {
|
||||
success: boolean
|
||||
}
|
||||
```
|
||||
|
||||
#### 7. 上传素材
|
||||
|
||||
```typescript
|
||||
POST /api/materials/upload
|
||||
|
||||
Request (FormData): {
|
||||
templateId?: string
|
||||
templateFields?: string // JSON字符串
|
||||
previewImages: File[]
|
||||
displayImage?: File
|
||||
zipFile?: File
|
||||
|
||||
// 标准字段
|
||||
productModel: string
|
||||
categoryName: string
|
||||
sceneName: string
|
||||
platformName?: string
|
||||
materialType: string
|
||||
materialKind: string
|
||||
renderer?: string
|
||||
brandName?: string
|
||||
seriesName?: string
|
||||
skuCode?: string
|
||||
materialTexture?: string
|
||||
renderTime?: string
|
||||
imageResolution?: string
|
||||
colorMode?: string
|
||||
}
|
||||
|
||||
Response: {
|
||||
id: string
|
||||
previewUrl: string
|
||||
// ... 其他字段
|
||||
}
|
||||
|
||||
Error (400): {
|
||||
error: "模板不存在:builtin-xxx" | "无效的内置模板:builtin-xxx" | "模板未启用"
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 错误类型
|
||||
|
||||
1. **模板验证错误**
|
||||
- 内置模板key无效
|
||||
- 自定义模板不存在
|
||||
- 模板状态非active
|
||||
|
||||
2. **字段验证错误**
|
||||
- 字段key重复
|
||||
- 字段类型无效
|
||||
- 必填字段为空
|
||||
- 验证规则不满足
|
||||
|
||||
3. **数据完整性错误**
|
||||
- 版本冲突
|
||||
- 外键约束违反
|
||||
- 唯一约束违反
|
||||
|
||||
4. **字段池同步错误**
|
||||
- 选项解析失败
|
||||
- 数据库操作失败
|
||||
|
||||
### 错误处理策略
|
||||
|
||||
```typescript
|
||||
// 1. API层错误处理
|
||||
try {
|
||||
// 业务逻辑
|
||||
} catch (error) {
|
||||
console.error('[API] Error:', {
|
||||
endpoint: '/api/xxx',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
context: { /* 相关上下文 */ }
|
||||
})
|
||||
|
||||
// 返回用户友好的错误信息
|
||||
return NextResponse.json(
|
||||
{ error: getUserFriendlyMessage(error) },
|
||||
{ status: getStatusCode(error) }
|
||||
)
|
||||
}
|
||||
|
||||
// 2. 业务逻辑层错误处理
|
||||
async function validateTemplateId(templateId: string) {
|
||||
try {
|
||||
// 验证逻辑
|
||||
} catch (error) {
|
||||
console.error('[TemplateValidator] Error:', error)
|
||||
return {
|
||||
valid: false,
|
||||
error: `验证模板时发生错误:${error.message}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 字段池同步错误处理
|
||||
async function syncFieldPoolFromTemplates() {
|
||||
try {
|
||||
// 同步逻辑
|
||||
} catch (error) {
|
||||
console.error('[FieldPool] Sync error:', error)
|
||||
// 记录错误但不抛出,避免影响模板保存
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 测试策略
|
||||
|
||||
### 单元测试
|
||||
|
||||
**模板验证器测试**:
|
||||
```typescript
|
||||
describe('template-validator', () => {
|
||||
test('应该验证内置模板ID', async () => {
|
||||
const result = await validateTemplateId('builtin-video-material')
|
||||
expect(result.valid).toBe(true)
|
||||
expect(result.isBuiltin).toBe(true)
|
||||
})
|
||||
|
||||
test('应该拒绝无效的内置模板ID', async () => {
|
||||
const result = await validateTemplateId('builtin-invalid')
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain('无效的内置模板')
|
||||
})
|
||||
|
||||
test('应该验证自定义模板ID', async () => {
|
||||
// Mock数据库查询
|
||||
const result = await validateTemplateId('custom-template-id')
|
||||
expect(result.valid).toBe(true)
|
||||
expect(result.isBuiltin).toBe(false)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**字段池管理器测试**:
|
||||
```typescript
|
||||
describe('field-pool', () => {
|
||||
test('应该规范化字段选项值', () => {
|
||||
expect(normalizeFieldOptionValue('[[[选项]]]')).toBe('选项')
|
||||
expect(normalizeFieldOptionValue(' 选项 ')).toBe('选项')
|
||||
expect(normalizeFieldOptionValue('"选项"')).toBe('选项')
|
||||
})
|
||||
|
||||
test('应该解析静态选项', () => {
|
||||
const options = parseOptionsStatic('["选项1", "选项2"]')
|
||||
expect(options).toEqual(['选项1', '选项2'])
|
||||
})
|
||||
|
||||
test('应该去重选项', () => {
|
||||
const options = parseOptionsStatic('["选项1", "选项1", "选项2"]')
|
||||
expect(options).toEqual(['选项1', '选项2'])
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### 集成测试
|
||||
|
||||
**模板创建流程测试**:
|
||||
```typescript
|
||||
describe('Template Creation Flow', () => {
|
||||
test('应该创建自定义模板并同步字段池', async () => {
|
||||
// 1. 创建模板
|
||||
const template = await createTemplate({
|
||||
name: '测试模板',
|
||||
fields: [
|
||||
{ key: 'testField', label: '测试字段', addToCategory: true }
|
||||
]
|
||||
})
|
||||
|
||||
// 2. 验证模板创建成功
|
||||
expect(template.id).toBeDefined()
|
||||
|
||||
// 3. 验证字段池同步
|
||||
const fieldDef = await getFieldDefinition('testField')
|
||||
expect(fieldDef).toBeDefined()
|
||||
expect(fieldDef.sourceTemplateIds).toContain(template.id)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**素材上传流程测试**:
|
||||
```typescript
|
||||
describe('Material Upload Flow', () => {
|
||||
test('应该使用内置模板上传素材', async () => {
|
||||
const material = await uploadMaterial({
|
||||
templateId: 'builtin-video-material',
|
||||
productModel: '测试产品',
|
||||
categoryName: '测试分类',
|
||||
sceneName: '测试场景'
|
||||
})
|
||||
|
||||
expect(material.id).toBeDefined()
|
||||
expect(material.templateId).toBe('builtin-video-material')
|
||||
})
|
||||
|
||||
test('应该使用自定义模板上传素材', async () => {
|
||||
const template = await createTemplate({ /* ... */ })
|
||||
const material = await uploadMaterial({
|
||||
templateId: template.id,
|
||||
templateFields: { testField: '测试值' }
|
||||
})
|
||||
|
||||
expect(material.metadata).toContain('testField')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 数据库查询优化
|
||||
|
||||
**使用索引**:
|
||||
```sql
|
||||
-- Material表索引
|
||||
CREATE INDEX idx_material_template_id ON materials(templateId);
|
||||
CREATE INDEX idx_material_category_name ON materials(categoryName);
|
||||
CREATE INDEX idx_material_scene_name ON materials(sceneName);
|
||||
|
||||
-- FieldDefinition表索引
|
||||
CREATE INDEX idx_field_definition_key ON field_definitions(key);
|
||||
CREATE INDEX idx_field_definition_filter_enabled ON field_definitions(isFilterEnabled);
|
||||
CREATE INDEX idx_field_definition_filter_order ON field_definitions(filterOrder);
|
||||
|
||||
-- FieldOption表索引
|
||||
CREATE INDEX idx_field_option_field_id ON field_options(fieldId);
|
||||
CREATE INDEX idx_field_option_order ON field_options(orderIndex);
|
||||
```
|
||||
|
||||
**批量操作**:
|
||||
```typescript
|
||||
// ❌ 不好的做法:逐个插入
|
||||
for (const option of options) {
|
||||
await prisma.fieldOption.create({ data: option })
|
||||
}
|
||||
|
||||
// ✅ 好的做法:批量插入
|
||||
await prisma.fieldOption.createMany({
|
||||
data: options
|
||||
})
|
||||
```
|
||||
|
||||
**使用事务**:
|
||||
```typescript
|
||||
await prisma.$transaction(async (tx) => {
|
||||
// 1. 更新模板
|
||||
await tx.uploadTemplate.update({ /* ... */ })
|
||||
|
||||
// 2. 删除旧字段
|
||||
await tx.uploadField.deleteMany({ /* ... */ })
|
||||
|
||||
// 3. 创建新字段
|
||||
await tx.uploadField.createMany({ /* ... */ })
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 前端性能优化
|
||||
|
||||
**虚拟滚动**:
|
||||
```typescript
|
||||
// 当字段数量超过30个时使用虚拟滚动
|
||||
{fields.length > 30 ? (
|
||||
<VirtualList
|
||||
items={fields}
|
||||
itemHeight={60}
|
||||
renderItem={(field) => <FieldCard field={field} />}
|
||||
/>
|
||||
) : (
|
||||
fields.map(field => <FieldCard key={field.id} field={field} />)
|
||||
)}
|
||||
```
|
||||
|
||||
**防抖**:
|
||||
```typescript
|
||||
// 用户输入时使用防抖减少验证频率
|
||||
const debouncedValidate = useMemo(
|
||||
() => debounce((value) => validateField(value), 300),
|
||||
[]
|
||||
)
|
||||
```
|
||||
|
||||
**缓存**:
|
||||
```typescript
|
||||
// 缓存模板列表
|
||||
const { data: templates, isLoading } = useSWR(
|
||||
'/api/upload-templates',
|
||||
fetcher,
|
||||
{ revalidateOnFocus: false }
|
||||
)
|
||||
```
|
||||
|
||||
### 3. 字段池同步优化
|
||||
|
||||
**增量同步**:
|
||||
```typescript
|
||||
// 只同步变更的字段,而不是全量同步
|
||||
async function syncChangedFields(templateId: string, changedFields: string[]) {
|
||||
for (const fieldKey of changedFields) {
|
||||
await syncSingleField(fieldKey)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**异步同步**:
|
||||
```typescript
|
||||
// 模板保存后异步同步字段池,不阻塞响应
|
||||
await prisma.uploadTemplate.update({ /* ... */ })
|
||||
|
||||
// 异步同步
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
await syncFieldPoolFromTemplates()
|
||||
} catch (error) {
|
||||
console.error('[FieldPool] Async sync error:', error)
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(template)
|
||||
```
|
||||
|
||||
## 部署和迁移
|
||||
|
||||
### 数据库迁移
|
||||
|
||||
**添加版本控制字段**:
|
||||
```sql
|
||||
ALTER TABLE upload_templates ADD COLUMN version INTEGER DEFAULT 1;
|
||||
```
|
||||
|
||||
**添加字段池表**:
|
||||
```sql
|
||||
-- 已存在,无需迁移
|
||||
```
|
||||
|
||||
**数据迁移脚本**:
|
||||
```typescript
|
||||
// 迁移旧素材的templateId
|
||||
async function migrateOldMaterials() {
|
||||
const materials = await prisma.material.findMany({
|
||||
where: { templateId: null }
|
||||
})
|
||||
|
||||
for (const material of materials) {
|
||||
// 根据materialType推断templateId
|
||||
const templateId = inferTemplateId(material.materialType)
|
||||
await prisma.material.update({
|
||||
where: { id: material.id },
|
||||
data: { templateId }
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 部署步骤
|
||||
|
||||
1. **备份数据库**
|
||||
```bash
|
||||
pg_dump -U postgres -d flnxi > backup_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
2. **运行数据库迁移**
|
||||
```bash
|
||||
npm run prisma:migrate deploy
|
||||
```
|
||||
|
||||
3. **同步字段池**
|
||||
```bash
|
||||
npm run sync-field-pool
|
||||
```
|
||||
|
||||
4. **部署新代码**
|
||||
```bash
|
||||
./deploy-new-fixed.ps1
|
||||
```
|
||||
|
||||
5. **验证部署**
|
||||
```bash
|
||||
curl http://localhost:3000/api/health
|
||||
```
|
||||
|
||||
## 监控和日志
|
||||
|
||||
### 关键指标
|
||||
|
||||
1. **模板操作**
|
||||
- 模板创建/更新/删除次数
|
||||
- 模板验证失败次数
|
||||
- 平均响应时间
|
||||
|
||||
2. **字段池同步**
|
||||
- 同步成功/失败次数
|
||||
- 同步耗时
|
||||
- 字段数量变化
|
||||
|
||||
3. **素材上传**
|
||||
- 使用各模板的上传次数
|
||||
- 上传失败次数(按错误类型分类)
|
||||
- 平均上传时间
|
||||
|
||||
### 日志格式
|
||||
|
||||
```typescript
|
||||
// 结构化日志
|
||||
console.log(JSON.stringify({
|
||||
timestamp: new Date().toISOString(),
|
||||
level: 'info',
|
||||
module: 'template-validator',
|
||||
action: 'validate',
|
||||
templateId: 'builtin-video-material',
|
||||
result: 'success',
|
||||
duration: 15
|
||||
}))
|
||||
```
|
||||
|
||||
## 安全考虑
|
||||
|
||||
### 1. 权限控制
|
||||
|
||||
```typescript
|
||||
// 只有管理员可以创建/修改/删除模板
|
||||
async function requireAdmin(session: Session | null) {
|
||||
if (!session || session.user.role !== 'ADMIN') {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 输入验证
|
||||
|
||||
```typescript
|
||||
// 验证字段key格式
|
||||
const FIELD_KEY_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*$/
|
||||
|
||||
if (!FIELD_KEY_PATTERN.test(fieldKey)) {
|
||||
throw new Error('字段key只能包含字母、数字和下划线,且必须以字母开头')
|
||||
}
|
||||
```
|
||||
|
||||
### 3. SQL注入防护
|
||||
|
||||
```typescript
|
||||
// 使用Prisma ORM,自动防护SQL注入
|
||||
await prisma.uploadTemplate.findUnique({
|
||||
where: { id: templateId } // 参数化查询
|
||||
})
|
||||
```
|
||||
|
||||
### 4. XSS防护
|
||||
|
||||
```typescript
|
||||
// React自动转义,但对于动态HTML需要额外处理
|
||||
import DOMPurify from 'dompurify'
|
||||
|
||||
const sanitizedHtml = DOMPurify.sanitize(userInput)
|
||||
```
|
||||
|
||||
## 未来扩展
|
||||
|
||||
### 1. 模板版本历史
|
||||
|
||||
记录模板的每次修改,支持回滚到历史版本。
|
||||
|
||||
### 2. 模板市场
|
||||
|
||||
允许用户分享和下载模板配置。
|
||||
|
||||
### 3. 智能字段推荐
|
||||
|
||||
根据素材类型和历史数据,推荐常用字段。
|
||||
|
||||
### 4. 字段依赖关系
|
||||
|
||||
支持更复杂的字段显示条件和联动逻辑。
|
||||
|
||||
### 5. 多语言支持
|
||||
|
||||
支持字段标签和选项的多语言配置。
|
||||
@ -1,411 +0,0 @@
|
||||
# 自定义上传模板系统优化需求文档
|
||||
|
||||
## 介绍
|
||||
|
||||
本文档定义了对FLNXI素材库自定义上传模板系统的优化需求。
|
||||
|
||||
**核心价值**:自定义上传模板系统是整个FLNXI素材库的核心基础设施,它决定了:
|
||||
- 用户如何上传素材(表单结构)
|
||||
- 素材包含哪些元数据(字段定义)
|
||||
- 如何分类和检索素材(字段池集成)
|
||||
- 如何共享和展示素材(字段显示配置)
|
||||
|
||||
作为"上传-保存-共享"工作流的起点,模板系统的稳定性和灵活性直接影响整个系统的可用性。当前系统已经实现了基础功能,但需要进一步优化以提高稳定性、可靠性和用户体验,确保它能够支撑各种复杂的业务场景。
|
||||
|
||||
## 术语表
|
||||
|
||||
- **自定义模板 (Custom Template)**: 管理员创建的上传模板,存储在数据库中
|
||||
- **内置模板 (Builtin Template)**: 系统预设的模板,动态生成,不存储在数据库中
|
||||
- **模板字段 (Template Field)**: 模板中定义的表单字段,如文本框、下拉框等
|
||||
- **字段池 (Field Pool)**: 集中管理所有模板字段及其选项的系统
|
||||
- **模板验证 (Template Validation)**: 验证模板ID是否有效的过程
|
||||
- **字段同步 (Field Sync)**: 将模板字段同步到字段池的过程
|
||||
|
||||
## 需求
|
||||
|
||||
### 需求 1: 模板验证增强
|
||||
|
||||
**用户故事**: 作为系统开发者,我希望模板验证逻辑能够正确区分内置模板和自定义模板,以便用户可以使用任何有效的模板上传素材。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 用户使用内置模板ID(以 `builtin-` 开头)上传素材 THEN 系统应验证模板key是否在预设列表中
|
||||
2. WHEN 用户使用自定义模板ID上传素材 THEN 系统应从数据库查询验证模板是否存在
|
||||
3. WHEN 模板验证失败 THEN 系统应返回清晰的错误信息,说明是内置模板无效还是自定义模板不存在
|
||||
4. WHEN 模板ID为空或null THEN 系统应允许上传(向后兼容旧数据)
|
||||
|
||||
### 需求 2: 自定义模板字段处理
|
||||
|
||||
**用户故事**: 作为管理员,我希望创建的自定义模板字段能够正确保存和读取,以便用户上传时能看到正确的表单字段。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员创建自定义模板并添加字段 THEN 系统应正确保存所有字段属性(key, label, type, required等)
|
||||
2. WHEN 用户选择自定义模板上传素材 THEN 系统应正确读取并显示所有模板字段
|
||||
3. WHEN 用户填写自定义模板字段并提交 THEN 系统应正确保存字段值到 metadata 或对应的数据库字段
|
||||
4. WHEN 自定义模板字段类型为 select THEN 系统应正确解析和显示选项列表
|
||||
5. WHEN 自定义模板字段设置为必填 THEN 系统应在提交前验证该字段不为空
|
||||
|
||||
### 需求 3: 模板字段同步优化
|
||||
|
||||
**用户故事**: 作为管理员,我希望模板字段能够自动同步到字段池,以便在分类管理中使用这些字段。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员创建或更新模板并勾选字段的 `addToCategory` THEN 系统应自动将该字段同步到字段池
|
||||
2. WHEN 管理员删除模板 THEN 系统应清理该模板相关的字段池数据(如果没有其他模板使用)
|
||||
3. WHEN 多个模板使用相同的字段key THEN 系统应合并这些字段的选项
|
||||
4. WHEN 字段选项包含脏数据(如 `[[[xxx]]]`) THEN 系统应自动清洗并规范化选项值
|
||||
|
||||
### 需求 4: 模板版本控制
|
||||
|
||||
**用户故事**: 作为管理员,我希望在编辑模板时能够检测并发冲突,以避免覆盖其他管理员的修改。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员打开模板编辑页面 THEN 系统应记录当前模板版本号
|
||||
2. WHEN 管理员保存模板 THEN 系统应检查版本号是否与数据库一致
|
||||
3. WHEN 版本号不一致 THEN 系统应拒绝保存并提示"模板已被其他人修改,请刷新后重试"
|
||||
4. WHEN 保存成功 THEN 系统应自动递增版本号
|
||||
|
||||
### 需求 5: 模板数据完整性
|
||||
|
||||
**用户故事**: 作为系统开发者,我希望模板数据始终保持完整性,以避免数据损坏导致的系统错误。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 创建或更新模板 THEN 系统应验证所有必填字段(name, materialType, materialKind)
|
||||
2. WHEN 模板字段的 key 重复 THEN 系统应拒绝保存并提示错误
|
||||
3. WHEN 模板字段的 optionsStatic 为数组 THEN 系统应自动转换为JSON字符串
|
||||
4. WHEN 模板的 materialKind 不在允许列表中 THEN 系统应使用默认值 'IMAGE'
|
||||
5. WHEN 删除模板时有素材正在使用 THEN 系统应警告管理员(但允许删除,素材的 templateId 会变为 null)
|
||||
|
||||
### 需求 6: 错误处理和日志
|
||||
|
||||
**用户故事**: 作为系统开发者,我希望模板相关操作有完善的错误处理和日志记录,以便快速定位和解决问题。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 模板操作发生错误 THEN 系统应记录详细的错误日志(包括错误类型、堆栈、请求数据)
|
||||
2. WHEN Prisma 返回特定错误代码 THEN 系统应返回用户友好的错误信息
|
||||
3. WHEN 字段池同步失败 THEN 系统应记录错误但不影响模板保存
|
||||
4. WHEN 模板验证失败 THEN 系统应返回清晰的错误信息,包含失败原因和建议操作
|
||||
|
||||
### 需求 7: 性能优化
|
||||
|
||||
**用户故事**: 作为用户,我希望模板加载和保存速度快,以提高工作效率。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 获取模板列表 THEN 系统应在 500ms 内返回结果
|
||||
2. WHEN 保存模板 THEN 系统应在 2秒 内完成(包括字段池同步)
|
||||
3. WHEN 模板包含大量字段(>20个) THEN 系统应使用事务批量处理,避免多次数据库往返
|
||||
4. WHEN 字段池同步 THEN 系统应使用批量操作,避免逐个字段处理
|
||||
|
||||
### 需求 8: 向后兼容性
|
||||
|
||||
**用户故事**: 作为系统维护者,我希望新的优化不会破坏现有功能,以确保系统稳定运行。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 旧素材没有 templateId THEN 系统应正常显示和编辑
|
||||
2. WHEN 旧素材的 templateId 指向已删除的模板 THEN 系统应正常显示(templateId 为 null)
|
||||
3. WHEN 旧模板没有 materialKind 字段 THEN 系统应使用默认值 'IMAGE'
|
||||
4. WHEN 旧模板字段没有 addToCategory 属性 THEN 系统应默认为 false
|
||||
|
||||
### 需求 9: 模板字段类型支持
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够创建多种类型的字段,以满足不同素材的元数据需求。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员创建 text 类型字段 THEN 用户上传时应看到单行文本输入框
|
||||
2. WHEN 管理员创建 textarea 类型字段 THEN 用户上传时应看到多行文本输入框
|
||||
3. WHEN 管理员创建 select 类型字段 THEN 用户上传时应看到下拉选择框,并能看到所有选项
|
||||
4. WHEN 管理员创建 number 类型字段 THEN 用户上传时应看到数字输入框,只能输入数字
|
||||
5. WHEN 管理员创建 date 类型字段 THEN 用户上传时应看到日期选择器
|
||||
6. WHEN 管理员创建 checkbox 类型字段 THEN 用户上传时应看到复选框
|
||||
7. WHEN 管理员创建 file 类型字段 THEN 用户上传时应看到文件上传按钮
|
||||
8. WHEN 管理员创建 image 类型字段 THEN 用户上传时应看到图片上传按钮,并支持预览
|
||||
|
||||
### 需求 10: 模板字段验证规则
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够为字段设置验证规则,以确保用户输入的数据符合要求。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员设置字段为必填 THEN 用户提交时该字段不能为空
|
||||
2. WHEN 管理员为 text 字段设置最大长度 THEN 用户输入超过长度时应提示错误
|
||||
3. WHEN 管理员为 number 字段设置最小值和最大值 THEN 用户输入超出范围时应提示错误
|
||||
4. WHEN 管理员为字段设置正则表达式验证 THEN 用户输入不匹配时应提示错误
|
||||
5. WHEN 验证失败 THEN 系统应显示清晰的错误提示,说明哪个字段不符合要求
|
||||
|
||||
### 需求 11: 模板字段布局配置
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够配置字段的布局,以优化上传表单的用户体验。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员设置字段布局为 'full' THEN 该字段应占据整行
|
||||
2. WHEN 管理员设置字段布局为 'half' THEN 该字段应占据半行,可与其他半行字段并排显示
|
||||
3. WHEN 管理员拖拽调整字段顺序 THEN 上传表单应按新顺序显示字段
|
||||
4. WHEN 管理员设置字段为只读 THEN 用户上传时该字段应显示但不可编辑
|
||||
5. WHEN 管理员设置字段的显示条件 THEN 只有满足条件时该字段才显示
|
||||
|
||||
### 需求 12: 模板与素材的关联
|
||||
|
||||
**用户故事**: 作为用户,我希望能够看到素材使用的模板,以便了解素材的元数据结构。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 用户查看素材详情 THEN 系统应显示该素材使用的模板名称
|
||||
2. WHEN 用户编辑素材 THEN 系统应根据模板动态生成编辑表单
|
||||
3. WHEN 素材的模板被删除 THEN 素材应仍然可以查看和编辑(使用通用表单)
|
||||
4. WHEN 用户筛选素材 THEN 系统应支持按模板类型筛选
|
||||
5. WHEN 管理员查看模板 THEN 系统应显示使用该模板的素材数量
|
||||
|
||||
### 需求 13: 模板导入导出
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够导入导出模板配置,以便在不同环境间迁移或备份模板。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员导出模板 THEN 系统应生成包含所有字段配置的 JSON 文件
|
||||
2. WHEN 管理员导入模板 THEN 系统应验证 JSON 格式并创建新模板
|
||||
3. WHEN 导入的模板名称已存在 THEN 系统应提示管理员选择覆盖或重命名
|
||||
4. WHEN 导入的模板包含无效字段类型 THEN 系统应拒绝导入并提示错误
|
||||
5. WHEN 导出多个模板 THEN 系统应支持批量导出为单个 JSON 文件
|
||||
|
||||
### 需求 14: 模板使用统计
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够查看模板的使用情况,以便优化模板配置。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员查看模板列表 THEN 系统应显示每个模板的使用次数
|
||||
2. WHEN 管理员查看模板详情 THEN 系统应显示最近使用该模板上传的素材列表
|
||||
3. WHEN 管理员查看模板统计 THEN 系统应显示每个字段的填写率
|
||||
4. WHEN 某个字段填写率很低 THEN 系统应建议管理员考虑将其设为可选或删除
|
||||
5. WHEN 模板长期未使用 THEN 系统应建议管理员归档该模板
|
||||
|
||||
### 需求 15: 模板权限控制
|
||||
|
||||
**用户故事**: 作为超级管理员,我希望能够控制哪些管理员可以创建和修改模板,以确保模板配置的安全性。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 普通用户访问模板管理页面 THEN 系统应拒绝访问并提示权限不足
|
||||
2. WHEN 管理员访问模板管理页面 THEN 系统应允许查看和编辑模板
|
||||
3. WHEN 超级管理员访问模板管理页面 THEN 系统应允许所有操作(包括删除)
|
||||
4. WHEN 管理员尝试删除正在使用的模板 THEN 系统应警告但允许删除(素材不受影响)
|
||||
5. WHEN 管理员修改模板 THEN 系统应记录操作日志(谁在何时修改了什么)
|
||||
|
||||
### 需求 16: 自定义字段的创建和管理
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够灵活地创建和管理模板中的自定义字段,以适应不同类型素材的元数据需求。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员在模板编辑器中点击"添加字段" THEN 系统应显示字段配置表单
|
||||
2. WHEN 管理员输入字段key THEN 系统应实时验证key的唯一性和格式(只允许字母、数字、下划线)
|
||||
3. WHEN 管理员输入字段label THEN 系统应自动将其作为显示名称
|
||||
4. WHEN 管理员选择字段类型 THEN 系统应根据类型显示相应的配置选项(如select类型显示选项配置)
|
||||
5. WHEN 管理员拖拽字段调整顺序 THEN 系统应实时更新字段的 orderIndex
|
||||
6. WHEN 管理员删除字段 THEN 系统应确认操作并警告可能影响已有素材
|
||||
7. WHEN 管理员复制字段 THEN 系统应创建一个新字段,key自动添加后缀避免冲突
|
||||
|
||||
### 需求 17: 自定义字段的选项管理(select类型)
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够为下拉选择字段配置选项列表,并支持动态添加和编辑选项。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员为select字段添加选项 THEN 系统应支持逐行输入或JSON数组格式
|
||||
2. WHEN 管理员输入的选项包含特殊字符或空格 THEN 系统应自动清洗并规范化
|
||||
3. WHEN 管理员输入重复的选项 THEN 系统应自动去重
|
||||
4. WHEN 管理员输入嵌套数组格式(如 `[[[option]]]`) THEN 系统应自动展开为单层数组
|
||||
5. WHEN 管理员保存模板 THEN 系统应将选项数组转换为JSON字符串存储
|
||||
6. WHEN 用户上传素材时 THEN 系统应正确解析选项并显示在下拉框中
|
||||
7. WHEN 管理员修改选项列表 THEN 系统应保持已有素材的选项值不变(即使选项被删除)
|
||||
|
||||
### 需求 18: 自定义字段的验证配置
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够为自定义字段配置验证规则,确保用户输入的数据质量。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员勾选"必填" THEN 用户上传时该字段必须填写
|
||||
2. WHEN 管理员为text字段设置最大长度 THEN 系统应在前端和后端都进行验证
|
||||
3. WHEN 管理员为number字段设置范围 THEN 系统应验证输入值在范围内
|
||||
4. WHEN 管理员设置正则表达式验证 THEN 系统应在用户输入时实时验证并提示
|
||||
5. WHEN 管理员设置自定义错误提示 THEN 验证失败时应显示自定义消息
|
||||
6. WHEN 验证规则冲突(如最小值大于最大值) THEN 系统应在保存模板时提示错误
|
||||
7. WHEN 用户提交表单 THEN 系统应按字段顺序依次验证,并显示所有错误
|
||||
|
||||
### 需求 19: 自定义字段的条件显示
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够设置字段的显示条件,根据其他字段的值动态显示或隐藏字段。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员为字段设置显示条件 THEN 系统应提供可视化的条件编辑器
|
||||
2. WHEN 管理员设置"当字段A等于值X时显示" THEN 用户上传时应根据条件动态显示字段
|
||||
3. WHEN 管理员设置多个条件(AND/OR逻辑) THEN 系统应正确计算条件结果
|
||||
4. WHEN 条件字段的值改变 THEN 系统应实时重新计算并更新字段显示状态
|
||||
5. WHEN 隐藏的字段有值 THEN 系统应保留该值(不清空)
|
||||
6. WHEN 条件配置循环依赖(A依赖B,B依赖A) THEN 系统应在保存时检测并拒绝
|
||||
7. WHEN 条件字段被删除 THEN 系统应警告并清除相关的条件配置
|
||||
|
||||
### 需求 20: 自定义字段的数据存储和读取
|
||||
|
||||
**用户故事**: 作为系统开发者,我希望自定义字段的数据能够可靠地存储和读取,支持不同的存储策略。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 自定义字段key与Material模型的标准字段匹配 THEN 系统应直接存储到对应的数据库字段
|
||||
2. WHEN 自定义字段key不匹配标准字段 THEN 系统应将其存储到 metadata JSON字段中
|
||||
3. WHEN 用户上传素材 THEN 系统应正确解析templateFields并分配到对应的存储位置
|
||||
4. WHEN 用户查看素材详情 THEN 系统应从标准字段和metadata中合并读取所有字段值
|
||||
5. WHEN 用户编辑素材 THEN 系统应根据模板动态生成表单,并预填充现有值
|
||||
6. WHEN metadata字段超过数据库限制 THEN 系统应压缩或分片存储
|
||||
7. WHEN 模板字段被删除但素材中有该字段的值 THEN 系统应保留数据但不显示在编辑表单中
|
||||
|
||||
### 需求 21: 自定义字段的迁移和兼容性
|
||||
|
||||
**用户故事**: 作为系统维护者,我希望在修改模板字段时,已有素材的数据能够平滑迁移,不会丢失或损坏。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员修改字段的key THEN 系统应提供数据迁移工具,将旧key的值迁移到新key
|
||||
2. WHEN 管理员修改字段类型(如text改为select) THEN 系统应验证现有数据是否兼容
|
||||
3. WHEN 字段类型不兼容 THEN 系统应警告并提供数据转换选项
|
||||
4. WHEN 管理员删除字段 THEN 系统应保留素材中的数据(存储在metadata中)但不显示
|
||||
5. WHEN 管理员恢复已删除的字段(相同key) THEN 系统应自动恢复素材中的数据
|
||||
6. WHEN 模板版本升级 THEN 系统应支持批量更新素材的字段结构
|
||||
7. WHEN 数据迁移失败 THEN 系统应回滚并记录详细的错误日志
|
||||
|
||||
### 需求 22: 自定义字段的性能优化
|
||||
|
||||
**用户故事**: 作为用户,我希望即使模板包含大量自定义字段,上传和编辑素材的速度也不会明显变慢。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 模板包含超过30个字段 THEN 系统应使用虚拟滚动或分页显示
|
||||
2. WHEN 用户填写表单 THEN 系统应使用防抖技术减少验证频率
|
||||
3. WHEN 用户提交表单 THEN 系统应批量验证所有字段,避免多次往返
|
||||
4. WHEN 字段有复杂的显示条件 THEN 系统应缓存条件计算结果
|
||||
5. WHEN 加载素材详情 THEN 系统应并行读取标准字段和metadata
|
||||
6. WHEN 保存素材 THEN 系统应使用事务确保标准字段和metadata同时更新
|
||||
7. WHEN metadata字段过大 THEN 系统应使用压缩算法减少存储空间
|
||||
|
||||
### 需求 23: 自定义字段的错误恢复
|
||||
|
||||
**用户故事**: 作为用户,我希望在填写表单时如果发生错误,我的输入不会丢失,可以继续编辑。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 用户填写表单时 THEN 系统应自动保存草稿到浏览器本地存储
|
||||
2. WHEN 用户刷新页面或意外关闭 THEN 系统应提示恢复未保存的数据
|
||||
3. WHEN 提交失败(网络错误、验证失败等) THEN 系统应保留用户输入,允许修改后重新提交
|
||||
4. WHEN 用户在多个标签页同时编辑 THEN 系统应检测冲突并提示用户
|
||||
5. WHEN 草稿数据过期(超过7天) THEN 系统应自动清理
|
||||
6. WHEN 用户主动清除草稿 THEN 系统应提供"清除草稿"按钮
|
||||
7. WHEN 用户成功提交 THEN 系统应自动清除对应的草稿数据
|
||||
|
||||
### 需求 24: 字段池同步的完整性和一致性
|
||||
|
||||
**用户故事**: 作为系统开发者,我希望模板字段与字段池之间的同步是完整、一致且可靠的,确保首页筛选和分类管理功能正常工作。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员勾选字段的 `addToCategory` 并保存模板 THEN 系统应立即将该字段同步到字段池(FieldDefinition表)
|
||||
2. WHEN 多个模板包含相同key的字段 THEN 系统应合并这些字段的选项到同一个FieldDefinition记录
|
||||
3. WHEN 管理员取消勾选字段的 `addToCategory` THEN 系统应检查是否有其他模板使用该字段,如果没有则从字段池删除
|
||||
4. WHEN 管理员修改字段的选项列表 THEN 系统应更新字段池中的FieldOption记录
|
||||
5. WHEN 字段池同步过程中发生错误 THEN 系统应记录详细日志但不影响模板保存,并在后台重试
|
||||
6. WHEN 字段池数据与模板数据不一致 THEN 系统应提供"重新同步"工具,管理员可手动触发全量同步
|
||||
7. WHEN 系统启动时 THEN 系统应自动检查字段池完整性,发现问题自动修复
|
||||
|
||||
### 需求 25: 首页分类Tag按钮的动态生成
|
||||
|
||||
**用户故事**: 作为用户,我希望首页的分类Tag按钮能够准确反映字段池中的分类数据,并且加载速度快、交互流畅。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 用户访问首页 THEN 系统应从字段池读取所有启用的筛选字段(isFilterEnabled=true)
|
||||
2. WHEN 字段池包含多个字段 THEN 系统应按 filterOrder 排序显示Tag按钮
|
||||
3. WHEN 用户点击Tag按钮 THEN 系统应展开该字段的所有选项,并高亮当前选中的选项
|
||||
4. WHEN 用户选择某个选项 THEN 系统应立即筛选素材列表,并更新URL参数
|
||||
5. WHEN 字段的选项过多(>20个) THEN 系统应使用虚拟滚动或分页显示
|
||||
6. WHEN 字段的 filterDisplayName 存在 THEN 系统应优先显示自定义名称,否则显示 label
|
||||
7. WHEN 管理员在后台修改字段池 THEN 首页应在下次刷新时自动更新Tag按钮(无需重启服务)
|
||||
|
||||
### 需求 26: 首页Tag按钮的性能优化
|
||||
|
||||
**用户故事**: 作为用户,我希望首页Tag按钮的切换和筛选操作非常流畅,不会出现卡顿或闪烁。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 用户切换Tag按钮 THEN 系统应在100ms内完成UI更新(无闪烁)
|
||||
2. WHEN 用户快速连续点击多个Tag THEN 系统应使用防抖技术,避免重复请求
|
||||
3. WHEN 素材列表数据量大(>1000条) THEN 系统应使用虚拟滚动和分页加载
|
||||
4. WHEN 用户选择多个筛选条件 THEN 系统应合并为单次API请求
|
||||
5. WHEN 筛选结果为空 THEN 系统应显示友好的空状态提示,并建议清除筛选条件
|
||||
6. WHEN 用户清除所有筛选 THEN 系统应快速恢复到初始状态
|
||||
7. WHEN Tag按钮数量过多 THEN 系统应使用横向滚动或折叠显示,避免布局混乱
|
||||
|
||||
### 需求 27: 后台分类管理的字段池集成
|
||||
|
||||
**用户故事**: 作为管理员,我希望在后台分类管理页面能够方便地管理字段池,包括启用/禁用字段、调整顺序、重命名显示名称等。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员进入分类管理页面 THEN 系统应显示所有字段池中的字段(包括已启用和未启用)
|
||||
2. WHEN 管理员拖拽字段调整顺序 THEN 系统应实时更新 filterOrder,并同步到首页Tag按钮顺序
|
||||
3. WHEN 管理员点击"重命名"按钮 THEN 系统应弹出输入框,允许修改 filterDisplayName
|
||||
4. WHEN 管理员点击"启用/禁用"按钮 THEN 系统应切换 isFilterEnabled 状态,并立即反映到首页
|
||||
5. WHEN 管理员点击字段卡片 THEN 系统应在右侧显示该字段的所有选项
|
||||
6. WHEN 管理员添加/删除/重命名选项 THEN 系统应更新 FieldOption 表,并同步到所有使用该字段的模板
|
||||
7. WHEN 管理员点击"刷新"按钮 THEN 系统应重新从模板同步字段池数据
|
||||
|
||||
### 需求 28: 分类管理的选项清洗和规范化
|
||||
|
||||
**用户故事**: 作为管理员,我希望系统能够自动清洗和规范化字段选项,避免脏数据影响用户体验。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 字段选项包含多余的括号(如 `[[[选项]]]`) THEN 系统应自动去除多余括号
|
||||
2. WHEN 字段选项包含前后空格 THEN 系统应自动trim
|
||||
3. WHEN 字段选项包含特殊字符(如引号、反斜杠) THEN 系统应自动转义或清除
|
||||
4. WHEN 字段选项为嵌套数组格式(如 `["[选项1,选项2]"]`) THEN 系统应自动展开为单层数组
|
||||
5. WHEN 字段选项重复 THEN 系统应自动去重
|
||||
6. WHEN 字段选项为空字符串 THEN 系统应自动过滤
|
||||
7. WHEN 清洗过程发现无法处理的数据 THEN 系统应记录警告日志并跳过该选项
|
||||
|
||||
### 需求 29: 分类管理的批量操作
|
||||
|
||||
**用户故事**: 作为管理员,我希望能够批量操作字段和选项,提高管理效率。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员选中多个字段 THEN 系统应显示批量操作按钮(启用/禁用/删除)
|
||||
2. WHEN 管理员批量启用字段 THEN 系统应一次性更新所有选中字段的 isFilterEnabled
|
||||
3. WHEN 管理员批量删除字段 THEN 系统应确认操作并警告影响范围
|
||||
4. WHEN 管理员批量导入选项 THEN 系统应支持CSV或JSON格式,并自动清洗数据
|
||||
5. WHEN 管理员批量导出选项 THEN 系统应生成包含所有字段和选项的JSON文件
|
||||
6. WHEN 批量操作失败 THEN 系统应显示详细的错误报告,说明哪些操作成功、哪些失败
|
||||
7. WHEN 批量操作涉及大量数据(>100条) THEN 系统应显示进度条并支持取消
|
||||
|
||||
### 需求 30: 分类管理与模板的双向同步
|
||||
|
||||
**用户故事**: 作为管理员,我希望分类管理和模板管理之间能够双向同步,修改任何一方都能反映到另一方。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 管理员在分类管理中修改字段选项 THEN 系统应自动更新所有使用该字段的模板
|
||||
2. WHEN 管理员在模板管理中修改字段选项 THEN 系统应自动同步到字段池
|
||||
3. WHEN 管理员在分类管理中删除字段 THEN 系统应警告哪些模板正在使用该字段
|
||||
4. WHEN 管理员在模板管理中删除字段 THEN 系统应检查字段池是否还有其他模板使用
|
||||
5. WHEN 同步过程中发生冲突(如两个管理员同时修改) THEN 系统应使用版本号检测并提示冲突
|
||||
6. WHEN 同步失败 THEN 系统应回滚所有相关修改,确保数据一致性
|
||||
7. WHEN 管理员查看同步历史 THEN 系统应显示详细的同步日志(时间、操作、结果)
|
||||
@ -1,338 +0,0 @@
|
||||
# 自定义上传模板系统优化 - 实施任务列表
|
||||
|
||||
## 项目状态
|
||||
|
||||
**完成度**: 阶段1-8 全部完成 ✅ 100% | 已部署到生产环境 ✅
|
||||
**测试**: 619个测试全部通过 ✅ (含41个集成测试)
|
||||
**文档**: API文档、用户手册、部署指南 ✅
|
||||
**准备部署**: 数据迁移脚本、验证脚本、检查清单 ✅
|
||||
**项目清理**: 已完成,删除~50个过期文件,归档23个文档 ✅
|
||||
|
||||
### 新增功能模块 (2024-12-13)
|
||||
- `lib/field-validation.ts` - 字段验证规则配置 (44个测试)
|
||||
- `lib/field-conditions.ts` - 字段条件显示逻辑 (41个测试)
|
||||
- `lib/field-data-storage.ts` - 自定义字段数据存储 (34个测试)
|
||||
- `lib/field-migration.ts` - 字段迁移工具 (43个测试)
|
||||
- `lib/draft-storage.ts` - 草稿自动保存 (24个测试)
|
||||
- `hooks/useDraftStorage.ts` - 草稿存储React Hook
|
||||
- `FieldValidationConfig.tsx` - 验证规则配置UI组件
|
||||
- `FieldConditionsConfig.tsx` - 条件显示配置UI组件
|
||||
|
||||
### 首页Tag系统优化 (2024-12-13)
|
||||
- `components/HomeFilterTags.tsx` - 首页筛选Tag组件 (展开/收起、多选、响应式)
|
||||
- `hooks/useHomeFilterFields.ts` - 筛选字段数据Hook (SWR缓存、自动刷新)
|
||||
- `hooks/useFilterSelections.ts` - 筛选选择状态Hook (防抖、URL同步)
|
||||
- `lib/__tests__/home-filter-tags.test.ts` - 22个测试用例
|
||||
|
||||
### 后台分类管理优化 (2024-12-13)
|
||||
- `lib/category-batch-operations.ts` - 批量操作工具 (导入/导出CSV/JSON)
|
||||
- `lib/category-sync.ts` - 双向同步功能 (模板↔字段池)
|
||||
- `lib/__tests__/category-operations.test.ts` - 25个测试用例
|
||||
|
||||
查看完整总结:`docs/template-system-optimization-summary.md`
|
||||
查看项目状态:`docs/PROJECT_STATUS.md`
|
||||
查看清理总结:`docs/PROJECT_CLEANUP_SUMMARY.md`
|
||||
|
||||
## 阶段 1: 核心基础设施优化
|
||||
|
||||
- [x] 1. 完善模板验证工具
|
||||
|
||||
|
||||
- 已创建 `lib/template-validator.ts` 基础版本
|
||||
- 添加更详细的错误信息和日志
|
||||
- 添加模板状态检查(draft/active/archived)
|
||||
- 添加单元测试
|
||||
- _需求: 1.1, 1.2, 1.3, 1.4_
|
||||
|
||||
- [x] 2. 优化上传API的模板验证
|
||||
|
||||
- 已集成 `validateTemplateId` 函数
|
||||
- 优化错误处理和日志记录
|
||||
- 添加性能监控(记录验证耗时)
|
||||
- _需求: 1.1, 1.2, 1.3, 6.1, 6.4_
|
||||
|
||||
- [x] 3. 增强字段池同步机制
|
||||
|
||||
|
||||
|
||||
- 优化 `syncFieldPoolFromTemplates` 函数
|
||||
- 添加增量同步支持(只同步变更的字段)
|
||||
- 添加同步失败重试机制
|
||||
- 添加同步日志和监控
|
||||
- _需求: 3.1, 3.2, 3.3, 3.4, 24.1, 24.2, 24.3, 24.4, 24.5_
|
||||
|
||||
- [x] 4. 优化选项清洗逻辑
|
||||
- 增强 `normalizeFieldOptionValue` 函数,支持更多清洗选项
|
||||
- 添加 `OptionCleaningIssue` 枚举,记录清洗问题类型
|
||||
- 添加详细的清洗日志(可选开启)
|
||||
- 处理边界情况:null/undefined、Unicode控制字符、嵌套数组、转义字符、特殊引号、多余空白
|
||||
- 添加 `logCleaningIssue` 函数记录清洗警告
|
||||
- 更新 `parseOptionsStatic` 和 `expandOptionValue` 支持日志选项
|
||||
- _需求: 3.4, 28.1, 28.2, 28.3, 28.4, 28.5, 28.6, 28.7_
|
||||
|
||||
## 阶段 2: 模板管理功能增强
|
||||
|
||||
- [x] 5. 实现模板版本控制
|
||||
|
||||
- 在模板更新API中添加版本号检查
|
||||
- 检测并发冲突,返回409状态码
|
||||
- 更新成功后自动递增版本号
|
||||
- 添加版本冲突的前端提示
|
||||
- _需求: 4.1, 4.2, 4.3, 4.4_
|
||||
|
||||
|
||||
- [x] 6. 增强模板数据完整性验证
|
||||
|
||||
- 验证必填字段(name, materialType, materialKind)
|
||||
- 检查字段key的唯一性
|
||||
- 自动转换optionsStatic数组为JSON字符串
|
||||
- 规范化materialKind值
|
||||
- 添加删除模板时的警告提示
|
||||
- _需求: 5.1, 5.2, 5.3, 5.4, 5.5_
|
||||
|
||||
|
||||
- [x] 7. 优化模板CRUD API的错误处理
|
||||
|
||||
- 统一错误响应格式
|
||||
- 添加详细的错误日志
|
||||
- 处理Prisma特定错误代码(P2002, P2003等)
|
||||
- 返回用户友好的错误信息
|
||||
- _需求: 6.1, 6.2, 6.3, 6.4_
|
||||
|
||||
- [x] 8. 实现模板使用统计
|
||||
|
||||
|
||||
- 在模板列表API中添加materialCount字段
|
||||
- 创建模板详情页,显示使用该模板的素材列表
|
||||
- 统计每个字段的填写率
|
||||
- 提供低填写率字段的优化建议
|
||||
- _需求: 14.1, 14.2, 14.3, 14.4, 14.5_
|
||||
|
||||
## 阶段 3: 自定义字段功能完善
|
||||
|
||||
- [x] 9. 优化字段创建和管理UI
|
||||
- 实时验证字段key的唯一性和格式
|
||||
- 添加字段复制功能
|
||||
- 优化拖拽排序的流畅度
|
||||
- 添加字段删除确认对话框
|
||||
- _需求: 16.1, 16.2, 16.3, 16.4, 16.5, 16.6, 16.7_
|
||||
|
||||
- [x] 10. 增强select字段的选项管理
|
||||
- 支持逐行输入和JSON数组两种格式
|
||||
- 自动清洗和规范化选项值
|
||||
- 自动去重选项
|
||||
- 展开嵌套数组格式
|
||||
- 保存时转换为JSON字符串
|
||||
- _需求: 17.1, 17.2, 17.3, 17.4, 17.5, 17.6, 17.7_
|
||||
|
||||
- [x] 11. 实现字段验证规则配置
|
||||
|
||||
|
||||
- 支持必填验证
|
||||
- 支持最大长度验证(text字段)
|
||||
- 支持范围验证(number字段)
|
||||
- 支持正则表达式验证
|
||||
- 支持自定义错误提示
|
||||
- 前端实时验证和后端验证
|
||||
- _需求: 18.1, 18.2, 18.3, 18.4, 18.5, 18.6, 18.7_
|
||||
|
||||
- [x] 12. 实现字段条件显示
|
||||
|
||||
|
||||
- 创建可视化条件编辑器
|
||||
- 支持简单条件(字段A等于值X)
|
||||
- 支持复杂条件(AND/OR逻辑)
|
||||
- 实时计算条件结果
|
||||
- 检测循环依赖
|
||||
- _需求: 19.1, 19.2, 19.3, 19.4, 19.5, 19.6, 19.7_
|
||||
|
||||
## 阶段 4: 数据存储和兼容性
|
||||
|
||||
- [x] 13. 优化自定义字段数据存储
|
||||
|
||||
|
||||
- 实现字段key到标准字段的映射
|
||||
- 优化metadata JSON字段的存储
|
||||
- 实现数据读取时的合并逻辑
|
||||
- 处理metadata字段超限的情况
|
||||
- _需求: 20.1, 20.2, 20.3, 20.4, 20.5, 20.6, 20.7_
|
||||
|
||||
- [x] 14. 实现字段迁移工具
|
||||
|
||||
|
||||
- 创建字段key修改的迁移工具
|
||||
- 实现字段类型转换验证
|
||||
- 提供数据转换选项
|
||||
- 实现字段恢复功能
|
||||
- 支持批量更新素材字段结构
|
||||
- _需求: 21.1, 21.2, 21.3, 21.4, 21.5, 21.6, 21.7_
|
||||
|
||||
- [x] 15. 实现草稿自动保存
|
||||
|
||||
|
||||
|
||||
- 表单填写时自动保存到localStorage
|
||||
- 页面刷新时提示恢复数据
|
||||
- 提交失败时保留用户输入
|
||||
- 检测多标签页冲突
|
||||
- 自动清理过期草稿
|
||||
- _需求: 23.1, 23.2, 23.3, 23.4, 23.5, 23.6, 23.7_
|
||||
|
||||
## 阶段 5: 首页分类Tag系统优化
|
||||
|
||||
- [x] 16. 优化首页Tag按钮生成
|
||||
- 创建 `components/HomeFilterTags.tsx` 组件
|
||||
- 从字段池读取启用的筛选字段
|
||||
- 按filterOrder排序显示
|
||||
- 优先显示filterDisplayName
|
||||
- 实现Tag按钮的展开/收起(选项>10个时)
|
||||
- 支持单选和多选筛选模式
|
||||
- _需求: 25.1, 25.2, 25.3, 25.4, 25.6, 25.7_
|
||||
|
||||
- [x] 17. 优化Tag按钮性能
|
||||
- 创建 `hooks/useHomeFilterFields.ts` - SWR缓存和自动重新验证
|
||||
- 创建 `hooks/useFilterSelections.ts` - 筛选选择状态管理
|
||||
- 实现100ms内的UI更新(使用keepPreviousData避免闪烁)
|
||||
- 使用防抖技术避免重复请求(debounceMs配置)
|
||||
- 合并多个筛选条件为单次请求
|
||||
- 优化空状态提示(loading骨架屏)
|
||||
- _需求: 26.1, 26.2, 26.3, 26.4, 26.5, 26.6, 26.7_
|
||||
- _注:虚拟滚动暂未实现,当前选项数量通常<20个,展开/收起已足够_
|
||||
|
||||
- [x] 18. 实现Tag按钮的响应式布局
|
||||
- 支持横向滚动(小屏幕时flex-nowrap + overflow-x-auto)
|
||||
- 支持折叠显示(展开/收起按钮)
|
||||
- 优化移动端体验(max-sm断点样式)
|
||||
- _需求: 26.7_
|
||||
|
||||
## 阶段 6: 后台分类管理优化
|
||||
|
||||
- [x] 19. 优化分类管理页面
|
||||
- 显示所有字段池字段(包括未启用)✅ 已在CategoriesTab实现
|
||||
- 实现拖拽调整字段顺序 ✅ 使用@dnd-kit实现
|
||||
- 实现字段重命名功能 ✅ 支持filterDisplayName修改
|
||||
- 实现启用/禁用切换 ✅ handleToggleCategoryField
|
||||
- 显示字段的所有选项 ✅ 右侧选项卡片列表
|
||||
- 支持添加/删除/重命名选项 ✅ handleAddOption/handleDeleteOption/handleRenameOption
|
||||
- _需求: 27.1, 27.2, 27.3, 27.4, 27.5, 27.6, 27.7_
|
||||
|
||||
- [x] 20. 实现分类管理的批量操作
|
||||
- 创建 `lib/category-batch-operations.ts`
|
||||
- 支持批量启用/禁用字段 (batchToggleFields)
|
||||
- 支持批量删除字段 (batchDeleteFields)
|
||||
- 支持批量导入选项(CSV/JSON)(parseCSVOptions, parseJSONOptions, batchImportOptions)
|
||||
- 支持批量导出选项 (exportToCSV, exportToJSON, exportAllFields)
|
||||
- 获取字段影响范围 (getFieldImpactInfo)
|
||||
- _需求: 29.1, 29.2, 29.3, 29.4, 29.5, 29.6, 29.7_
|
||||
|
||||
- [x] 21. 实现分类管理与模板的双向同步
|
||||
- 创建 `lib/category-sync.ts`
|
||||
- 分类管理修改选项时同步到模板 (syncOptionToTemplates)
|
||||
- 模板修改选项时同步到字段池 (syncOptionToFieldPool)
|
||||
- 删除字段时警告影响范围 (getFieldDeletionImpact)
|
||||
- 使用版本号检测同步冲突 ✅
|
||||
- 同步失败时回滚 (syncWithRollback)
|
||||
- 记录同步历史日志 (logSync, getSyncLogs)
|
||||
- _需求: 30.1, 30.2, 30.3, 30.4, 30.5, 30.6, 30.7_
|
||||
|
||||
## 阶段 7: 性能优化和测试
|
||||
|
||||
- [x] 22. 数据库查询优化
|
||||
- 添加必要的索引(UploadTemplate、UploadField、FieldDefinition、FieldOption)
|
||||
- 优化查询性能(status、materialType、materialKind、templateId等)
|
||||
- 支持高效的排序和筛选(orderIndex、filterOrder等)
|
||||
- 已应用数据库迁移:20251213073100_add_template_system_indexes
|
||||
- _需求: 7.1, 7.2, 7.3, 7.4_
|
||||
|
||||
- [x] 23. 前端性能优化
|
||||
- 创建防抖Hooks(useDebounce、useDebouncedCallback)
|
||||
- 创建SWR缓存Hooks(useTemplates、useTemplate)
|
||||
- 字段验证已使用useMemo优化(实时计算但避免重复计算)
|
||||
- 表单渲染已优化(使用React.memo和useCallback)
|
||||
- _需求: 22.1, 22.2, 22.3, 22.4, 22.5, 22.6, 22.7_
|
||||
- _注:虚拟滚动暂未实现,当前字段数量通常<30个,性能足够_
|
||||
|
||||
- [x] 24. 编写单元测试
|
||||
- 测试模板验证器(25个测试用例)✅
|
||||
- 测试字段池管理器(34个测试用例)✅
|
||||
- 测试选项清洗逻辑(已包含在字段池测试中)✅
|
||||
- 测试模板CRUD API(13个测试用例)✅
|
||||
- 测试防抖Hooks(17个测试用例:9个useDebounce + 8个useDebouncedCallback)✅
|
||||
- 测试TemplateFieldsEditor组件(29个测试用例)✅
|
||||
- 总计118个测试用例全部通过 ✅
|
||||
- 核心模块代码覆盖率:
|
||||
- template-validator.ts: 92.62% statements, 78.26% branches, 100% functions ✅
|
||||
- field-pool.ts: 36.95% statements, 42.39% branches, 26.19% functions ⚠️
|
||||
- useDebounce.ts: 100% statements, 66.66% branches, 100% functions ✅
|
||||
- upload-templates API: 85.88% statements, 73.4% branches, 100% functions ✅
|
||||
- upload-templates/[id] API: 56.66% statements, 34.3% branches, 54.54% functions ⚠️
|
||||
- 整体覆盖率: 8.61% (包含所有API文件,大部分未测试) ⚠️
|
||||
- 核心功能覆盖率达标 ✅
|
||||
|
||||
- [x] 25. 编写集成测试
|
||||
|
||||
|
||||
- 测试模板创建流程 ✅
|
||||
- 测试素材上传流程 ✅
|
||||
- 测试字段池同步流程 ✅
|
||||
- 测试分类管理流程 ✅
|
||||
- 测试文件:`lib/__tests__/integration/template-workflow.integration.test.ts`
|
||||
- 41个集成测试用例全部通过 ✅
|
||||
- 总测试数:619个(新增41个集成测试)
|
||||
|
||||
## 阶段 8: 文档和部署
|
||||
|
||||
- [x] 26. 更新API文档
|
||||
- 记录所有新增和修改的API(模板管理、字段池管理、分类字段管理)
|
||||
- 添加请求/响应示例(包含完整的JSON示例)
|
||||
- 记录错误代码和处理方式(统一错误格式)
|
||||
- 添加性能指标和数据库优化说明
|
||||
- 添加最佳实践指南
|
||||
- 文档位置:`docs/api/template-system-api.md`
|
||||
|
||||
- [x] 27. 编写用户手册
|
||||
- 如何创建自定义模板(6个详细步骤)
|
||||
- 如何使用分类管理(同步机制说明)
|
||||
- 常见问题解答(10个FAQ)
|
||||
- 字段类型详解(8种字段类型)
|
||||
- 文档位置:`docs/user-guide/template-system-guide.md`
|
||||
|
||||
- [x] 28. 准备数据库迁移脚本
|
||||
- version字段已在schema中存在 ✅
|
||||
- 创建数据迁移脚本(scripts/migrate-template-data.ts)✅
|
||||
- 创建数据验证脚本(scripts/verify-template-data.ts)✅
|
||||
- 添加npm脚本命令(migrate:template-data, verify:template-data)✅
|
||||
- 创建部署前检查清单(docs/deployment/pre-deployment-checklist.md)✅
|
||||
|
||||
- [x] 29. 部署到测试环境
|
||||
- ✅ 备份数据库(部署脚本自动处理)
|
||||
- ✅ 部署新代码(已部署到45.249.208.118:4320)
|
||||
- ✅ 容器运行正常(youyidian-3d-material)
|
||||
- ✅ 健康检查通过
|
||||
- ✅ 迁移脚本TypeScript错误已修复
|
||||
- ✅ 数据库迁移已应用(标记为已完成)
|
||||
- ✅ 数据验证通过(3个模板,4个素材)
|
||||
- ✅ **功能冒烟测试通过** (2025-12-13)
|
||||
- `/api/health` - 系统健康 ✅
|
||||
- `/api/system/integrity` - 完整性检查通过,6个内置模板全部存在 ✅
|
||||
- `/api/templates` - 返回11个模板(5内置+6自定义)✅
|
||||
- `/api/materials` - 返回9个素材,视频转码状态正常 ✅
|
||||
- 前端登录页面正常显示 ✅
|
||||
- 📋 **操作指南**:`docs/deployment/部署后操作步骤.md`
|
||||
- 🌐 **访问地址**:http://45.249.208.118:4320
|
||||
|
||||
- [x] 30. 部署到生产环境
|
||||
- ✅ 备份数据库(部署脚本自动处理)
|
||||
- ✅ 运行数据库迁移(已应用)
|
||||
- ✅ 部署新代码(2025-12-13 21:01)
|
||||
- ✅ 健康检查通过(3秒内就绪)
|
||||
- ✅ 系统完整性检查通过(6个内置模板全部存在,无孤立素材)
|
||||
- 🌐 **访问地址**:http://45.249.208.118:4320
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **优先级**: 阶段1-4为高优先级,必须完成;阶段5-8可根据时间调整
|
||||
2. **测试**: 每个阶段完成后都要进行充分测试
|
||||
3. **向后兼容**: 所有修改都要确保向后兼容,不影响现有功能
|
||||
4. **性能**: 关注关键操作的性能指标,确保满足设计目标
|
||||
5. **文档**: 及时更新文档,记录重要的设计决策和实现细节
|
||||
@ -1,322 +0,0 @@
|
||||
# Design Document
|
||||
|
||||
## Overview
|
||||
|
||||
本设计文档针对四个关键问题提供技术解决方案:
|
||||
1. 旧模板缺失导致的上传失败问题
|
||||
2. 标签按钮UI在素材数量为0时文字消失的问题
|
||||
3. 视频转码状态停留在"pending"的问题
|
||||
4. 模板创建界面过于复杂的问题
|
||||
|
||||
## Architecture
|
||||
|
||||
### 1. 模板自动补全机制
|
||||
|
||||
```
|
||||
上传素材 → 验证模板ID → 模板不存在?
|
||||
↓ 是
|
||||
自动创建模板 → 继续上传
|
||||
↓ 否
|
||||
直接上传
|
||||
```
|
||||
|
||||
### 2. 视频转码流程
|
||||
|
||||
```
|
||||
上传视频 → 创建素材记录 → 触发转码任务 → 加入转码队列
|
||||
↓
|
||||
执行转码
|
||||
↓
|
||||
更新转码状态(done/failed)
|
||||
```
|
||||
|
||||
### 3. UI组件优化
|
||||
|
||||
```
|
||||
Tag组件 → 检查素材数量 → 应用正确的样式类
|
||||
↓
|
||||
确保文字对比度足够
|
||||
```
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### 1. 模板自动补全服务
|
||||
|
||||
**文件**: `lib/template-auto-create.ts`
|
||||
|
||||
```typescript
|
||||
interface TemplateAutoCreateOptions {
|
||||
templateId: string
|
||||
templateName?: string
|
||||
isBuiltin?: boolean
|
||||
}
|
||||
|
||||
interface TemplateCreationResult {
|
||||
success: boolean
|
||||
template?: Template
|
||||
error?: string
|
||||
}
|
||||
|
||||
// 主要函数
|
||||
async function ensureTemplateExists(templateId: string): Promise<TemplateCreationResult>
|
||||
async function createMissingTemplate(options: TemplateAutoCreateOptions): Promise<Template>
|
||||
```
|
||||
|
||||
### 2. 视频转码触发器
|
||||
|
||||
**文件**: `app/api/materials/upload/route.ts` (修改)
|
||||
|
||||
```typescript
|
||||
// 上传完成后立即触发转码
|
||||
if (isVideoMaterial) {
|
||||
await triggerTranscodeTask(materialId, videoSourceUrl)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Tag组件样式修复
|
||||
|
||||
**文件**: `components/ui/Tag.tsx` (修改)
|
||||
|
||||
```typescript
|
||||
// 修复颜色类,确保文字始终可见
|
||||
const colorClasses = {
|
||||
blue: selected
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-blue-50 text-blue-700 dark:bg-blue-950/50 dark:text-blue-200',
|
||||
// ... 其他颜色
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 模板创建UI简化
|
||||
|
||||
**文件**: `app/admin/templates/new/page.tsx` (修改)
|
||||
|
||||
```typescript
|
||||
// 隐藏optionsStatic字段
|
||||
{field.type === 'select' && (
|
||||
<div>
|
||||
{/* 不显示optionsStatic输入框 */}
|
||||
<p className="text-sm text-gray-500">
|
||||
选项通过分类管理功能配置
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
## Data Models
|
||||
|
||||
### Template 模型(已存在)
|
||||
|
||||
```prisma
|
||||
model Template {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
description String?
|
||||
isBuiltin Boolean @default(false)
|
||||
fields Json
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
materials Material[]
|
||||
}
|
||||
```
|
||||
|
||||
### Material 模型(已存在)
|
||||
|
||||
```prisma
|
||||
model Material {
|
||||
id String @id
|
||||
templateId String?
|
||||
template Template? @relation(fields: [templateId], references: [id])
|
||||
videoSourceUrl String?
|
||||
videoProcessingStatus String? // 'pending' | 'processing' | 'done' | 'failed'
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### 1. 模板缺失错误处理
|
||||
|
||||
```typescript
|
||||
try {
|
||||
// 尝试创建素材
|
||||
await prisma.material.create({ data })
|
||||
} catch (error) {
|
||||
if (error.code === 'P2003') { // 外键约束错误
|
||||
// 自动创建缺失的模板
|
||||
await ensureTemplateExists(templateId)
|
||||
// 重试创建素材
|
||||
await prisma.material.create({ data })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 视频转码错误处理
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await processVideoTranscode(options)
|
||||
} catch (error) {
|
||||
console.error('[Transcode] Error:', error)
|
||||
await prisma.material.update({
|
||||
where: { id: materialId },
|
||||
data: { videoProcessingStatus: 'failed' }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
1. **模板自动创建测试**
|
||||
- 测试内置模板的自动创建
|
||||
- 测试自定义模板的自动创建
|
||||
- 测试模板已存在时的处理
|
||||
|
||||
2. **Tag组件测试**
|
||||
- 测试不同素材数量下的样式
|
||||
- 测试深色/浅色模式下的可见性
|
||||
- 测试hover状态
|
||||
|
||||
3. **视频转码测试**
|
||||
- 测试转码任务的触发
|
||||
- 测试转码状态的更新
|
||||
- 测试转码失败的处理
|
||||
|
||||
### Integration Tests
|
||||
|
||||
1. **上传流程测试**
|
||||
- 测试使用缺失模板上传素材
|
||||
- 验证模板自动创建
|
||||
- 验证素材成功创建
|
||||
|
||||
2. **视频转码流程测试**
|
||||
- 测试视频上传后转码自动触发
|
||||
- 验证转码状态正确更新
|
||||
- 验证转码结果正确保存
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### 1. 模板自动补全实现要点
|
||||
|
||||
- 检查模板ID格式(builtin-xxx 或自定义ID)
|
||||
- 对于内置模板,使用预定义的字段配置
|
||||
- 对于自定义模板,使用基础字段配置
|
||||
- 记录日志以便追踪自动创建的模板
|
||||
|
||||
### 2. 视频转码实现要点
|
||||
|
||||
- 上传完成后立即触发转码(不等待HTTP响应)
|
||||
- 使用转码队列管理并发
|
||||
- 正确处理转码失败情况
|
||||
- 提供手动重试接口
|
||||
|
||||
### 3. Tag组件实现要点
|
||||
|
||||
- 使用更高对比度的颜色
|
||||
- 深色模式下使用更亮的文字颜色
|
||||
- 避免使用过低透明度的背景色
|
||||
- 确保hover状态不影响文字可见性
|
||||
|
||||
### 4. 模板UI简化要点
|
||||
|
||||
- 隐藏optionsStatic字段输入
|
||||
- 添加说明文字引导用户使用分类管理
|
||||
- 保持字段配置的简洁性
|
||||
- 提供清晰的用户提示
|
||||
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||
|
||||
### Property 1: 模板缺失检测
|
||||
|
||||
*For any* 素材上传请求,如果引用的模板ID在数据库中不存在,系统应该检测到模板缺失并返回相应的标识。
|
||||
|
||||
**Validates: Requirements 1.1**
|
||||
|
||||
### Property 2: 模板自动创建
|
||||
|
||||
*For any* 检测到缺失的模板ID,系统应该自动创建该模板记录,创建后该模板ID应该存在于数据库中。
|
||||
|
||||
**Validates: Requirements 1.2**
|
||||
|
||||
### Property 3: 模板默认配置完整性
|
||||
|
||||
*For any* 自动创建的模板,该模板应该包含必需的字段(name, isBuiltin, fields)且字段值应该是有效的。
|
||||
|
||||
**Validates: Requirements 1.3**
|
||||
|
||||
### Property 4: 模板创建日志记录
|
||||
|
||||
*For any* 自动创建的模板,系统日志应该包含该模板的创建记录,包括模板ID和创建时间。
|
||||
|
||||
**Validates: Requirements 1.4**
|
||||
|
||||
### Property 5: 上传前模板验证
|
||||
|
||||
*For any* 素材上传请求,在创建素材记录之前,系统应该验证模板存在性,如果不存在则先创建模板。
|
||||
|
||||
**Validates: Requirements 1.5**
|
||||
|
||||
### Property 6: 标签文字可见性(零素材)
|
||||
|
||||
*For any* 素材数量为0的标签按钮,文字应该保持可见(文字颜色与背景颜色的对比度应该 >= 4.5:1)。
|
||||
|
||||
**Validates: Requirements 2.1**
|
||||
|
||||
### Property 7: Hover状态文字可见性
|
||||
|
||||
*For any* 标签按钮,在hover状态下,文字的可见性不应该降低(对比度不应该减少)。
|
||||
|
||||
**Validates: Requirements 2.2**
|
||||
|
||||
### Property 8: 未选中状态对比度
|
||||
|
||||
*For any* 未选中状态的标签按钮,文字与背景的对比度应该 >= 4.5:1(符合WCAG AA标准)。
|
||||
|
||||
**Validates: Requirements 2.3**
|
||||
|
||||
### Property 9: 深色模式文字可见性
|
||||
|
||||
*For any* 在深色模式下显示的标签按钮,文字颜色的亮度应该足够高(L值 >= 60)。
|
||||
|
||||
**Validates: Requirements 2.4**
|
||||
|
||||
### Property 10: 浅色模式文字可见性
|
||||
|
||||
*For any* 在浅色模式下显示的标签按钮,文字颜色的亮度应该足够低(L值 <= 40)。
|
||||
|
||||
**Validates: Requirements 2.5**
|
||||
|
||||
### Property 11: 视频转码自动触发
|
||||
|
||||
*For any* 视频素材上传成功,系统应该自动将转码任务添加到转码队列中。
|
||||
|
||||
**Validates: Requirements 3.1**
|
||||
|
||||
### Property 12: 转码开始状态更新
|
||||
|
||||
*For any* 开始处理的转码任务,对应素材的videoProcessingStatus应该更新为"processing"。
|
||||
|
||||
**Validates: Requirements 3.2**
|
||||
|
||||
### Property 13: 转码完成状态更新
|
||||
|
||||
*For any* 成功完成的转码任务,对应素材的videoProcessingStatus应该更新为"done",且应该有preview540Url和clipUrl。
|
||||
|
||||
**Validates: Requirements 3.3**
|
||||
|
||||
### Property 14: 转码失败状态更新
|
||||
|
||||
*For any* 失败的转码任务,对应素材的videoProcessingStatus应该更新为"failed",且错误信息应该被记录。
|
||||
|
||||
**Validates: Requirements 3.4**
|
||||
|
||||
### Property 15: 转码及时处理
|
||||
|
||||
*For any* 状态为"pending"的视频素材,在30秒内应该开始转码处理(状态变为"processing"或"failed")。
|
||||
|
||||
**Validates: Requirements 3.5**
|
||||
@ -1,68 +0,0 @@
|
||||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
本需求文档描述了需要修复的问题:
|
||||
1. 旧模板(包括预制模板和自定义模板)在数据库中缺失,导致上传素材时出现外键约束错误
|
||||
2. 首页标签按钮在素材数量为0时,鼠标交互导致文字消失的UI问题
|
||||
3. 视频素材上传后转码状态一直显示"等待转码"
|
||||
4. 新建模板页面的下拉选择属性字段显示选项列表,对客户来说过于专业
|
||||
|
||||
## Glossary
|
||||
|
||||
- **System**: 素材库管理系统
|
||||
- **Template**: 素材模板,用于定义素材的字段结构
|
||||
- **Built-in Template**: 内置模板,系统预定义的模板
|
||||
- **Tag Component**: 标签组件,用于显示和筛选素材类型
|
||||
- **Material Type**: 素材类型,如3DMAX、视频等
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1
|
||||
|
||||
**User Story:** 作为用户,我想上传使用旧模板的素材,以便继续使用之前创建的模板。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户上传素材时引用的模板ID在数据库中不存在 THEN System SHALL 检测到模板缺失
|
||||
2. WHEN System 检测到模板缺失 THEN System SHALL 自动创建缺失的模板记录
|
||||
3. WHEN 自动创建模板时 THEN System SHALL 使用合理的默认配置和字段
|
||||
4. WHEN 模板被自动创建 THEN System SHALL 记录日志以便追踪
|
||||
5. WHEN 上传API接收到素材数据 THEN System SHALL 在创建素材记录前验证并补全模板
|
||||
|
||||
### Requirement 2
|
||||
|
||||
**User Story:** 作为用户,我想在首页看到清晰的标签按钮,即使某个类型的素材数量为0,标签文字也应该始终可见。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 标签按钮显示素材数量为0 THEN Tag Component SHALL 保持文字可见性
|
||||
2. WHEN 用户鼠标悬停在标签按钮上 THEN Tag Component SHALL 不改变文字的可见性
|
||||
3. WHEN 标签按钮处于未选中状态 THEN Tag Component SHALL 使用足够的文字对比度确保可读性
|
||||
4. WHEN 标签按钮在深色模式下显示 THEN Tag Component SHALL 确保文字在深色背景下可见
|
||||
5. WHEN 标签按钮在浅色模式下显示 THEN Tag Component SHALL 确保文字在浅色背景下可见
|
||||
|
||||
### Requirement 3
|
||||
|
||||
**User Story:** 作为用户,我想上传视频素材后能够自动转码,以便在系统中预览视频。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户上传视频素材 THEN System SHALL 自动触发视频转码流程
|
||||
2. WHEN 视频转码任务添加到队列 THEN System SHALL 立即开始处理队列中的任务
|
||||
3. WHEN 视频转码开始 THEN System SHALL 更新素材的转码状态为"processing"
|
||||
4. WHEN 视频转码完成 THEN System SHALL 更新素材的转码状态为"done"
|
||||
5. WHEN 视频转码失败 THEN System SHALL 更新素材的转码状态为"failed"并记录错误信息
|
||||
6. WHEN 转码队列处理完一个任务 THEN System SHALL 自动处理队列中的下一个任务
|
||||
7. WHEN 转码任务函数在队列中传递 THEN System SHALL 保持函数引用不丢失
|
||||
|
||||
### Requirement 4
|
||||
|
||||
**User Story:** 作为管理员,我想在新建模板时有简洁的界面,不需要看到过于专业的选项列表配置。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 管理员创建新模板 THEN System SHALL 隐藏下拉选择属性的选项列表字段
|
||||
2. WHEN 管理员需要管理选项 THEN System SHALL 通过分类管理tag功能提供选项管理
|
||||
3. WHEN 模板创建界面显示 THEN System SHALL 只显示必要的字段配置
|
||||
4. WHEN 用户查看模板配置 THEN System SHALL 提供清晰简洁的用户界面
|
||||
@ -1,112 +0,0 @@
|
||||
# Implementation Plan
|
||||
|
||||
- [x] 1. 创建模板自动补全服务
|
||||
|
||||
- 创建 `lib/template-auto-create.ts` 文件
|
||||
- 实现模板存在性检查函数
|
||||
- 实现内置模板自动创建逻辑
|
||||
- 实现自定义模板自动创建逻辑
|
||||
- 添加日志记录功能
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4_
|
||||
|
||||
- [x] 1.1 编写模板自动创建的单元测试
|
||||
- **Property 1: 模板缺失检测** ✅
|
||||
- **Property 2: 模板自动创建** ✅
|
||||
- **Property 3: 模板默认配置完整性** ✅
|
||||
- **Property 4: 模板创建日志记录** ✅
|
||||
- **Validates: Requirements 1.1, 1.2, 1.3, 1.4**
|
||||
- **测试结果**: 24个测试全部通过,代码覆盖率91.56%
|
||||
- **测试报告**: `TEST_REPORT_template-auto-create.md`
|
||||
|
||||
- [x] 2. 实现系统数据完整性保障策略
|
||||
|
||||
- 创建 `lib/system-integrity.ts` - 核心完整性检查和修复逻辑 ✅
|
||||
- 创建 `lib/startup-check.ts` - 应用启动检查模块 ✅
|
||||
- 修改 `app/layout.tsx` - 首次加载时自动执行初始化 ✅
|
||||
- 修改 `app/api/health/route.ts` - 健康检查时触发初始化 ✅
|
||||
- 创建 `scripts/docker-entrypoint.sh` - Docker启动脚本 ✅
|
||||
- 更新 `Dockerfile` - 使用启动脚本 ✅
|
||||
- 创建 `docs/系统数据完整性策略.md` - 策略文档 ✅
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
|
||||
- **说明**: 采用系统级自动化方案替代上传时检查,客户无需任何手动操作
|
||||
|
||||
- [x] 2.1 系统完整性测试验证
|
||||
|
||||
|
||||
|
||||
|
||||
- **Property 5: 启动时模板自动创建**
|
||||
- **Validates: Requirements 1.5**
|
||||
- **验证方式**: 通过 `/api/health` 和 `/api/system/integrity` API 验证
|
||||
|
||||
- [x] 3. 修复Tag组件样式问题
|
||||
|
||||
- 修改 `components/ui/Tag.tsx` ✅
|
||||
- 更新颜色类定义,提高文字对比度 ✅
|
||||
- 修复深色模式下的文字颜色 ✅
|
||||
- 修复浅色模式下的文字颜色 ✅
|
||||
- 确保hover状态不影响文字可见性 ✅
|
||||
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5_
|
||||
|
||||
- [x] 3.1 编写Tag组件的单元测试
|
||||
|
||||
|
||||
- **Property 6: 标签文字可见性(零素材)**
|
||||
- **Property 7: Hover状态文字可见性**
|
||||
- **Property 8: 未选中状态对比度**
|
||||
- **Property 9: 深色模式文字可见性**
|
||||
- **Property 10: 浅色模式文字可见性**
|
||||
- **Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5**
|
||||
|
||||
- [x] 4. 视频转码功能验证
|
||||
|
||||
- 视频转码功能正常工作 ✅
|
||||
- 之前的"等待转码"问题是因为模板缺失导致上传失败,转码任务未创建
|
||||
- 模板问题修复后,视频可以正常上传和转码
|
||||
- _Requirements: 3.1, 3.2, 3.5_
|
||||
- **说明**: 无需修改代码,原有转码逻辑正常
|
||||
|
||||
- [x] 4.1 视频转码验证
|
||||
- **Property 11: 视频转码自动触发** - 已验证正常
|
||||
- **Property 12: 转码开始状态更新** - 已验证正常
|
||||
- **Property 15: 转码及时处理** - 转码耗时较长但正常完成
|
||||
- **Validates: Requirements 3.1, 3.2, 3.5**
|
||||
|
||||
- [x] 5. 视频转码状态管理验证
|
||||
|
||||
- 转码完成后状态正确更新 ✅
|
||||
- 转码失败后状态正确更新 ✅
|
||||
- _Requirements: 3.3, 3.4_
|
||||
- **说明**: 无需修改代码,原有状态管理逻辑正常
|
||||
|
||||
- [x] 5.1 转码状态管理验证
|
||||
- **Property 13: 转码完成状态更新** - 已验证正常
|
||||
- **Property 14: 转码失败状态更新** - 已验证正常
|
||||
- **Validates: Requirements 3.3, 3.4**
|
||||
|
||||
- [x] 6. 简化模板创建UI
|
||||
- 查找模板创建页面文件 ✅
|
||||
- 隐藏optionsStatic字段输入 ✅
|
||||
- 添加说明文字引导用户使用分类管理 ✅
|
||||
- 优化页面布局和用户体验 ✅
|
||||
- _Requirements: 4.1, 4.2, 4.3_
|
||||
|
||||
- [x] 7. 测试和验证修复(用户手动验证)
|
||||
- 测试使用旧模板上传素材 ✅
|
||||
- 测试视频素材上传和转码 ✅
|
||||
- 测试标签按钮在不同状态下的显示 ✅
|
||||
- 测试模板创建界面 ✅
|
||||
- 验证所有修复都正常工作 ✅
|
||||
- **状态**: 用户验证通过 (2025-12-13)
|
||||
|
||||
- [x] 8. 修复特定视频素材的转码问题
|
||||
|
||||
- 检查素材 FLNXI2025121373113 的转码状态 ✅
|
||||
- 该素材在数据库中不存在(可能是测试数据或已删除)
|
||||
- 视频转码功能已验证正常工作
|
||||
- _Requirements: 3.1, 3.2, 3.3_
|
||||
|
||||
- [x] 9. Checkpoint - 确保所有测试通过
|
||||
|
||||
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
40
AGENTS.md
Normal file
40
AGENTS.md
Normal file
@ -0,0 +1,40 @@
|
||||
# 项目规则入口
|
||||
|
||||
本文件是当前项目规则的唯一入口。
|
||||
|
||||
## 1. 基本原则
|
||||
|
||||
- 后续统一使用 `Codex`
|
||||
- 优先使用 `Codex` 内置 multi-agent 能力
|
||||
- 不自建额外代理框架,不复制一套自定义调度协议
|
||||
|
||||
## 2. 参考源码目录
|
||||
|
||||
只读参考目录:
|
||||
|
||||
- `D:\AI Code\sucaiku\flnxi_sucai\gongxiangp2-source-reference-old-style-20260513-132942`:当前实际存在,用户已指定用于功能对比
|
||||
- `D:\AI Code\sucaiku\flnxi_sucai\gongxiangp2-source-reference-20260309-202917`:历史参考目录名,当前本机不存在;保留在排除配置中防止后续放回后被工具链误扫
|
||||
- `D:\AI Code\sucaiku\flnxi_sucai\gongxiangp2-source-reference-20260413-172354`:历史参考目录名,当前本机不存在;保留在排除配置中防止后续放回后被工具链误扫
|
||||
|
||||
规则:
|
||||
|
||||
- 平时不要主动打开、预加载、检索或修改这些目录
|
||||
- 只有在明确需要“查实现”或“做差异对比”时,才进入这些目录
|
||||
- 不修改
|
||||
- 不参与构建
|
||||
- 不参与测试
|
||||
- 只用于查实现和对比
|
||||
|
||||
## 3. 修改边界
|
||||
|
||||
- 优先修改文档、配置、脚本、规则文件
|
||||
- 未经明确要求,不改业务功能代码
|
||||
- 发现工具链会扫描参考目录或历史残留目录时,优先修正排除配置
|
||||
|
||||
## 4. 项目定位与目标环境
|
||||
|
||||
- 当前项目目标是企业/团队私有化交付,不是纯公网 SaaS
|
||||
- 目标部署设备默认是 NAS 或 Linux 服务器,需能运行 Docker / Podman
|
||||
- 设计和取舍优先兼容中低性能私有化环境,尤其是 NAS 场景
|
||||
- 普通最终用户默认只通过浏览器使用系统,不承担部署和运维动作
|
||||
- 做功能、部署、Agent、授权相关决策时,优先服务“可安装交付、低维护、可验收”
|
||||
384
README.md
384
README.md
@ -1,289 +1,225 @@
|
||||
# FLNXI素材库
|
||||
# FLNXI 素材库
|
||||
|
||||
一个遵循苹果设计语言的 3D 素材协作平台,专注企业/团队的私有化部署场景,内置 AI 辅助、部门权限、模板化元数据与可移植部署工具链。
|
||||
本仓库是 `FLNXI 素材库` 的当前主项目,不是旧版素材站,也不是单独的模板系统示例。项目目标是面向企业/团队做私有化交付,默认部署在 NAS 或 Linux 服务器上,通过浏览器给普通用户使用。
|
||||
|
||||
## 🎯 项目概览
|
||||
## 1. 项目定位
|
||||
|
||||
- **目标**:让团队能"一键部署 + 秒级迁移",快速落地到自有 NAS、IDC 或混合网络环境
|
||||
- **定位**:设计/电商团队素材仓库 + 管理驾驶舱 + AI 实验场
|
||||
- **原则**:性能优先、稳定性优先、易部署
|
||||
当前项目核心目标:
|
||||
|
||||
## 🌟 核心功能
|
||||
- 素材上传、检索、预览、下载、分享
|
||||
- 模板化元数据和动态筛选
|
||||
- 用户、部门、邀请和后台管理
|
||||
- 许可证激活、校验、免费版 / 付费版限制
|
||||
- 系统初始化、外观设置、对象存储配置
|
||||
- Docker / NAS / Linux 私有化交付
|
||||
|
||||
### 素材管理
|
||||
- **智能搜索**:多维度筛选(品牌/系列/SKU/渲染参数)
|
||||
- **模板系统**:自定义字段、拖拽排序、动态表单
|
||||
- **批量操作**:上传、下载、分享、标签管理
|
||||
- **版本控制**:并发冲突检测、自动版本递增
|
||||
一句话:这是一个可安装、可授权、可交付、可维护的私有化素材管理产品。
|
||||
|
||||
### 权限管理
|
||||
- **部门体系**:树状组织结构、部门管理员
|
||||
- **角色权限**:超级管理员、管理员、普通用户
|
||||
- **邀请系统**:邀请链接、多次使用、过期控制
|
||||
## 2. 仓库组成
|
||||
|
||||
### AI 功能
|
||||
- **智能对话**:Gemini 多模态对话
|
||||
- **图像生成**:自动生成风格稿
|
||||
- **智能标签**:自动提取素材信息
|
||||
```text
|
||||
.
|
||||
├── app/ # 主应用页面和 API(Next.js App Router)
|
||||
├── components/ # 主应用 React 组件
|
||||
├── hooks/ # 主应用前端 Hook
|
||||
├── lib/ # 主应用核心逻辑、授权、S3、认证、模板等
|
||||
├── prisma/ # 主应用 Prisma schema 与 seed
|
||||
├── public/ # 主应用静态资源
|
||||
├── scripts/ # 构建、迁移、修复、部署辅助脚本
|
||||
├── docs/ # 当前项目文档入口
|
||||
├── license-server/ # 独立许可证服务
|
||||
├── flnxi-agent/ # Node 版安装与运维 Agent
|
||||
└── flnxi-agent-go/ # Go 版安装与运维 Agent
|
||||
```
|
||||
|
||||
### 系统特性
|
||||
- **健康检查**:`/api/health` 端点
|
||||
- **系统监控**:资源使用、性能指标
|
||||
- **数据恢复**:S3 桶扫描、批量导入
|
||||
- **网络自识别**:内外网自动切换
|
||||
当前主应用是核心产品;`license-server/` 和两个 Agent 是私有化交付链路的一部分,不属于普通用户日常使用入口。
|
||||
|
||||
## 🧱 技术栈
|
||||
## 3. 当前主应用能力
|
||||
|
||||
- **前端**:Next.js 14 App Router、React 18、TypeScript、TailwindCSS
|
||||
- **认证**:NextAuth.js + 邮箱密码 + 钉钉 OAuth
|
||||
- **数据库**:Prisma 5.22 + PostgreSQL
|
||||
- **存储**:MinIO / S3 兼容对象存储
|
||||
- **AI**:Google Gemini API
|
||||
- **测试**:Jest + React Testing Library(118个单元测试)
|
||||
已在当前项目中存在的能力:
|
||||
|
||||
## 🚀 快速开始
|
||||
- 素材上传、搜索、预览、下载、分享、收藏、点赞
|
||||
- 上传模板、字段配置、字段池、首页动态筛选
|
||||
- 用户注册、登录、个人资料、个人素材、浏览历史
|
||||
- 部门、邀请、角色、管理员后台
|
||||
- 授权激活、许可证校验、心跳、免费版限制
|
||||
- 系统初始化、系统设置、Logo / 外观设置、完整性检查
|
||||
- 图片、PSD、音频、视频相关处理能力
|
||||
- 钉钉登录、AI 聊天 / AI 编辑相关接口
|
||||
|
||||
### 1. 安装依赖
|
||||
注意:钉钉、AI、许可证服务等能力依赖环境变量和外部服务配置;代码存在不等于部署后默认可用。
|
||||
|
||||
## 4. 参考源码目录
|
||||
|
||||
当前实际存在的参考源码目录:
|
||||
|
||||
```text
|
||||
gongxiangp2-source-reference-old-style-20260513-132942/
|
||||
```
|
||||
|
||||
它是其他版本素材库源码,只用于查实现和做功能对比,不属于当前主项目,不参与构建、测试和提交。
|
||||
|
||||
历史上出现过的参考目录名也保留在排除配置中,避免后续重新放回时被工具链误扫:
|
||||
|
||||
- `gongxiangp2-source-reference-20260309-202917/`
|
||||
- `gongxiangp2-source-reference-20260413-172354/`
|
||||
- `gongxiangp2-source-reference-old-style-20260513-132942/`
|
||||
|
||||
参考源码的当前功能吸收结论见:
|
||||
|
||||
- `docs/参考源码功能吸收整理_20260513.md`
|
||||
|
||||
## 5. 技术栈
|
||||
|
||||
主应用:
|
||||
|
||||
- Next.js 14
|
||||
- React 18
|
||||
- TypeScript
|
||||
- Prisma 5
|
||||
- PostgreSQL
|
||||
- MinIO / S3 兼容对象存储
|
||||
- NextAuth
|
||||
- Tailwind CSS
|
||||
- Jest + React Testing Library
|
||||
|
||||
许可证服务:
|
||||
|
||||
- Next.js 14
|
||||
- Prisma
|
||||
- PostgreSQL
|
||||
|
||||
安装 Agent:
|
||||
|
||||
- `flnxi-agent/`:Node.js / Express / Socket.IO
|
||||
- `flnxi-agent-go/`:Go / Gin / Docker SDK
|
||||
|
||||
## 6. 本地开发
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2. 配置环境变量
|
||||
### 配置环境变量
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
编辑 `.env` 文件,配置必要的环境变量:
|
||||
至少需要确认。下面只表示变量形状,不要把示例占位值用于客户环境:
|
||||
|
||||
```env
|
||||
# 数据库
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/flnxi_material
|
||||
|
||||
# 认证
|
||||
NEXTAUTH_SECRET=your-secret-key
|
||||
DATABASE_URL=postgresql://<user>:<strong-password>@<host>:5432/flnxi
|
||||
NEXTAUTH_SECRET=<generate-a-strong-random-secret>
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# S3存储
|
||||
S3_ENDPOINT=https://s3.example.com
|
||||
S3_BUCKET=your-bucket
|
||||
S3_ACCESS_KEY_ID=your-access-key
|
||||
S3_SECRET_ACCESS_KEY=your-secret-key
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
S3_BUCKET=flnxi-materials
|
||||
S3_ACCESS_KEY_ID=<object-storage-access-key>
|
||||
S3_SECRET_ACCESS_KEY=<object-storage-secret-key>
|
||||
LICENSE_SERVER_URL=https://your-license-server.example.com
|
||||
LICENSE_PROJECT_CODE=flnxi-sucai
|
||||
```
|
||||
|
||||
### 3. 初始化数据库
|
||||
`LICENSE_PROJECT_CODE` 必须和 `license-server` 中的项目编码一致。
|
||||
|
||||
### 初始化 Prisma
|
||||
|
||||
```bash
|
||||
npm run prisma:generate
|
||||
npm run prisma:push
|
||||
```
|
||||
|
||||
### 4. 启动开发服务
|
||||
### 启动主应用
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# 访问 http://localhost:3000
|
||||
```
|
||||
|
||||
### 5. 创建管理员账号
|
||||
默认访问:
|
||||
|
||||
首次注册的用户会自动成为超级管理员。
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
## 🔧 开发命令
|
||||
## 7. 系统初始化
|
||||
|
||||
常规激活流程会自动完成系统初始化。
|
||||
|
||||
如果是在尚未有管理员账号的环境里手动初始化,需要先配置 `SYSTEM_INIT_TOKEN`,再调用:
|
||||
|
||||
```bash
|
||||
# 开发
|
||||
npm run dev # 启动开发服务器
|
||||
npm run build # 构建生产版本
|
||||
npm start # 启动生产服务器
|
||||
|
||||
# 测试
|
||||
npm test # 运行测试
|
||||
npm run test:watch # 监听模式
|
||||
npm run test:coverage # 生成覆盖率报告
|
||||
|
||||
# 代码质量
|
||||
npm run lint # ESLint检查
|
||||
npm run type-check # TypeScript类型检查
|
||||
npm run check # 完整检查(类型+lint+prisma)
|
||||
|
||||
# 数据库
|
||||
npm run prisma:generate # 生成Prisma客户端
|
||||
npm run prisma:push # 推送schema到数据库
|
||||
npm run prisma:studio # 打开Prisma Studio
|
||||
npm run prisma:validate # 验证schema
|
||||
|
||||
# 数据迁移
|
||||
npm run migrate:template-data # 迁移模板数据
|
||||
npm run verify:template-data # 验证数据完整性
|
||||
curl -X POST http://localhost:3000/api/system/init \
|
||||
-H "x-system-init-token: your-init-token"
|
||||
```
|
||||
|
||||
## 📦 生产部署
|
||||
如果没有提供 `INIT_ADMIN_PASSWORD`,系统会生成一次性初始密码,并且首次登录后强制修改。项目不再内置固定的 `admin@flnxi.com / 88888888`。
|
||||
|
||||
### 使用Harbor部署(推荐)
|
||||
|
||||
```powershell
|
||||
# 1. 本地构建并推送到Harbor
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
|
||||
# 2. SSH到服务器执行数据迁移
|
||||
ssh root@your-server
|
||||
cd /root/deploy
|
||||
docker exec flnxi-material npx prisma migrate deploy
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
|
||||
# 3. 验证部署
|
||||
# 访问 http://your-server:4320
|
||||
```
|
||||
|
||||
### Docker Compose部署
|
||||
## 8. 常用命令
|
||||
|
||||
```bash
|
||||
# 生产环境
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# 开发环境
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
详细部署文档:
|
||||
- [完整部署说明](docs/deployment/DEPLOYMENT.md)
|
||||
- [快速部署指南](docs/deployment/quick-deployment-guide.md)
|
||||
- [部署前检查清单](docs/deployment/pre-deployment-checklist.md)
|
||||
|
||||
## 🗂️ 项目结构
|
||||
|
||||
```
|
||||
.
|
||||
├── app/ # Next.js App Router
|
||||
│ ├── page.tsx # 首页
|
||||
│ ├── admin/ # 管理后台
|
||||
│ └── api/ # API路由
|
||||
├── components/ # React组件
|
||||
├── lib/ # 工具库
|
||||
│ ├── template-validator.ts # 模板验证器
|
||||
│ ├── field-pool.ts # 字段池管理
|
||||
│ └── hooks/ # 自定义Hooks
|
||||
├── prisma/ # 数据库
|
||||
│ ├── schema.prisma # 数据模型
|
||||
│ └── migrations/ # 迁移文件
|
||||
├── scripts/ # 脚本工具
|
||||
│ ├── migrate-template-data.ts # 数据迁移
|
||||
│ └── verify-template-data.ts # 数据验证
|
||||
├── docs/ # 文档
|
||||
│ ├── api/ # API文档
|
||||
│ ├── deployment/ # 部署文档
|
||||
│ └── user-guide/ # 用户手册
|
||||
└── tests/ # 测试文件
|
||||
```
|
||||
|
||||
## 📚 文档
|
||||
|
||||
### 用户文档
|
||||
- [用户手册](docs/user-guide/template-system-guide.md) - 如何使用模板系统
|
||||
- [API文档](docs/api/template-system-api.md) - API接口说明
|
||||
|
||||
### 开发文档
|
||||
- [项目总结](docs/template-system-optimization-summary.md) - 最新优化总结
|
||||
- [数据迁移脚本](scripts/README.md) - 脚本使用说明
|
||||
|
||||
### 部署文档
|
||||
- [完整部署说明](docs/deployment/DEPLOYMENT.md) - 详细部署步骤
|
||||
- [快速部署指南](docs/deployment/quick-deployment-guide.md) - 快速上手
|
||||
- [部署前检查清单](docs/deployment/pre-deployment-checklist.md) - 检查清单
|
||||
|
||||
## 🧪 测试
|
||||
|
||||
项目包含118个单元测试,覆盖核心功能:
|
||||
|
||||
- 模板验证器(25个测试)
|
||||
- 字段池管理器(34个测试)
|
||||
- 模板CRUD API(13个测试)
|
||||
- 防抖Hooks(17个测试)
|
||||
- UI组件(29个测试)
|
||||
|
||||
```bash
|
||||
# 运行所有测试
|
||||
# 主应用
|
||||
npm run dev
|
||||
npm run build
|
||||
npm start
|
||||
npm run lint
|
||||
npm run type-check
|
||||
npm test
|
||||
|
||||
# 查看覆盖率
|
||||
npm run test:coverage
|
||||
# Prisma
|
||||
npm run prisma:generate
|
||||
npm run prisma:push
|
||||
npm run prisma:validate
|
||||
npm run prisma:studio
|
||||
|
||||
# 模板数据迁移 / 校验
|
||||
npm run migrate:template-data
|
||||
npm run verify:template-data
|
||||
```
|
||||
|
||||
核心模块覆盖率:
|
||||
- template-validator.ts: 92.62%
|
||||
- useDebounce.ts: 100%
|
||||
- upload-templates API: 85.88%
|
||||
## 9. 部署入口
|
||||
|
||||
## 🔐 环境变量
|
||||
当前主应用生产编排:
|
||||
|
||||
### 必需变量
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `DATABASE_URL` | PostgreSQL连接字符串 |
|
||||
| `NEXTAUTH_SECRET` | NextAuth加密密钥 |
|
||||
| `NEXTAUTH_URL` | 应用URL |
|
||||
| `S3_ENDPOINT` | S3端点 |
|
||||
| `S3_BUCKET` | S3桶名称 |
|
||||
| `S3_ACCESS_KEY_ID` | S3访问密钥 |
|
||||
| `S3_SECRET_ACCESS_KEY` | S3密钥 |
|
||||
`docker-compose.prod.yml` 当前会拉起:
|
||||
|
||||
### 可选变量
|
||||
- PostgreSQL
|
||||
- MinIO
|
||||
- MinIO 初始化容器
|
||||
- 主应用容器
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `GEMINI_API_KEY` | Gemini AI密钥 |
|
||||
| `DINGTALK_CLIENT_ID` | 钉钉客户端ID |
|
||||
| `DINGTALK_CLIENT_SECRET` | 钉钉客户端密钥 |
|
||||
主应用镜像通过当前 Registry 发布流程生成和推送,镜像名、容器名、卷名、网络名和端口可通过 `.env` 覆盖,例如 `APP_IMAGE`、`APP_PORT`、`APP_CONTAINER_NAME`。
|
||||
|
||||
完整配置说明请参考 `.env.example`
|
||||
它不会直接拉起 Nginx / Traefik、独立许可证服务或 Agent。不同 NAS / Linux 现场的反向代理、域名、证书、许可证服务地址,需要按部署文档和实际环境配置。
|
||||
|
||||
## 🆘 故障排除
|
||||
内部维护环境仍保留:
|
||||
|
||||
### 常见问题
|
||||
- `deploy-registry.ps1`
|
||||
- `deploy-new-fixed.ps1`
|
||||
- `发布管理.ps1`
|
||||
- `license-server/deploy.ps1`
|
||||
|
||||
**Q: Docker构建失败?**
|
||||
A: 检查 `.env.production` 文件是否存在,或使用 `-EnvFile` 参数指定环境变量文件。
|
||||
这些脚本执行前必须先读内容和参数,不应把内部固定服务器或固定镜像仓库当成客户默认交付配置。客户交付时要显式写入版本化 `APP_IMAGE` / `IMAGE_TAG`,`latest` 只保留给内部测试或兼容场景。当前新 Registry 发布脚本是主线,旧远程部署脚本只作历史或内部维护参考。
|
||||
|
||||
**Q: 数据库连接失败?**
|
||||
A: 确认 `DATABASE_URL` 配置正确,数据库服务正在运行。
|
||||
## 10. 文档入口
|
||||
|
||||
**Q: S3上传失败?**
|
||||
A: 检查S3配置和网络连接,查看错误日志。
|
||||
优先阅读:
|
||||
|
||||
**Q: 测试失败?**
|
||||
A: 运行 `npm run prisma:generate` 重新生成Prisma客户端。
|
||||
- `docs/README.md`
|
||||
- `docs/deployment/部署说明.md`
|
||||
- `docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md`
|
||||
- `docs/初次部署与出厂设置指南.md`
|
||||
- `docs/许可证服务接入说明.md`
|
||||
- `docs/参考源码功能吸收整理_20260513.md`
|
||||
- `scripts/README.md`
|
||||
|
||||
更多问题请查看:
|
||||
- [部署故障排除](docs/deployment/DEPLOYMENT.md#常见问题)
|
||||
- [脚本故障排除](scripts/README.md#故障排除)
|
||||
原则:
|
||||
|
||||
## 📈 性能指标
|
||||
|
||||
| 操作 | 目标时间 | 实际时间 |
|
||||
|------|---------|---------|
|
||||
| 模板列表加载 | < 500ms | ~300ms |
|
||||
| 素材上传验证 | < 100ms | ~50ms |
|
||||
| 字段池同步 | < 1s | ~500ms |
|
||||
| 首页Tag响应 | < 100ms | ~50ms |
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
欢迎提交Issue和Pull Request!
|
||||
|
||||
## 📝 License
|
||||
|
||||
MIT
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
- 项目地址:[GitHub](https://github.com/your-repo)
|
||||
- 问题反馈:[Issues](https://github.com/your-repo/issues)
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2025年12月13日
|
||||
**版本**: 1.0.0
|
||||
- 当前项目事实以当前代码、配置、部署文件和这些入口文档为准。
|
||||
- 旧部署记录、一次性排障报告、旧交接文档和历史总结不作为当前事实来源。
|
||||
- 参考源码只用于查思路和做功能吸收判断,不直接参与当前项目运行链路。
|
||||
|
||||
@ -1,61 +1,78 @@
|
||||
# Docker 基础镜像仓库
|
||||
|
||||
本目录用于存储项目所需的基础 Docker 镜像,作为备用方案。
|
||||
本目录用于存储项目所需的基础 Docker 镜像,主要作为离线/受限网络下的备用资源。
|
||||
|
||||
**注意**:如果你本地已经有基础镜像(如 `node:18-alpine`),部署脚本会直接使用,无需从 Registry 或本地文件加载。
|
||||
**注意**:这不是客户正式部署入口。当前主要只有 `scripts/build-material-image.ps1` 这类内部构建脚本会优先尝试本地基础镜像;主发布脚本 `deploy-registry.ps1` 不依赖这份目录作为标准交付前提。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
docker-images/
|
||||
├── README.md # 本文件
|
||||
├── node/ # Node.js 基础镜像
|
||||
│ └── node-18-alpine.tar # 镜像文件
|
||||
├── minio-mc/ # MinIO Client 镜像
|
||||
│ ├── minio-mc-latest.tar # 镜像文件
|
||||
│ ├── minio-mc-RELEASE.2025-04-16T18-13-26Z.tar # 镜像文件
|
||||
│ └── VERSION.txt # 版本信息
|
||||
├── postgres/ # PostgreSQL 镜像(如需要)
|
||||
└── minio/ # MinIO Server 镜像(如需要)
|
||||
```
|
||||
|
||||
当前目录下没有 `postgres/` 和 `minio/` 镜像包。生产 Compose 默认可走官方运行时镜像;客户离线或受控网络交付时,应在 `.env` 中显式填写 `POSTGRES_IMAGE`、`MINIO_IMAGE`、`MINIO_MC_IMAGE` 到当前可用的私有 Registry 版本镜像。
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 加载镜像到本地 Docker
|
||||
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
docker load -i docker-images\minio-mc\minio-mc-latest.tar
|
||||
docker load -i docker-images\minio-mc\minio-mc-RELEASE.2025-04-16T18-13-26Z.tar
|
||||
|
||||
# Linux/Mac
|
||||
docker load -i docker-images/minio-mc/minio-mc-latest.tar
|
||||
docker load -i docker-images/minio-mc/minio-mc-RELEASE.2025-04-16T18-13-26Z.tar
|
||||
```
|
||||
|
||||
### 2. 在部署脚本中使用
|
||||
### 2. 在内部构建脚本中使用
|
||||
|
||||
部署脚本会自动检查本地镜像,如果存在则优先使用,否则从网络下载。
|
||||
内部构建脚本会先检查本地镜像;如果存在则优先使用,否则再按脚本逻辑去 Registry 或网络拉取。客户正式交付不要依赖这份目录“自动生效”。
|
||||
|
||||
### 3. 更新镜像
|
||||
|
||||
当需要更新镜像时:
|
||||
|
||||
```powershell
|
||||
# 1. 拉取最新镜像
|
||||
docker pull minio/mc:latest
|
||||
# 1. 拉取固定版本镜像
|
||||
docker pull minio/mc:RELEASE.2025-04-16T18-13-26Z
|
||||
|
||||
# 2. 保存到项目目录
|
||||
docker save minio/mc:latest -o docker-images\minio-mc\minio-mc-latest.tar
|
||||
docker save minio/mc:RELEASE.2025-04-16T18-13-26Z -o docker-images\minio-mc\minio-mc-RELEASE.2025-04-16T18-13-26Z.tar
|
||||
|
||||
# 3. 更新版本信息
|
||||
echo "latest - $(Get-Date -Format 'yyyy-MM-dd')" > docker-images\minio-mc\VERSION.txt
|
||||
echo "RELEASE.2025-04-16T18-13-26Z - $(Get-Date -Format 'yyyy-MM-dd')" > docker-images\minio-mc\VERSION.txt
|
||||
```
|
||||
|
||||
## 镜像列表
|
||||
|
||||
| 镜像名称 | 版本 | 文件路径 | 大小 | 更新时间 |
|
||||
|---------|------|---------|------|---------|
|
||||
| minio/mc | latest | `minio-mc/minio-mc-latest.tar` | ~85MB | 2025-11-23 |
|
||||
| 镜像名称 | 版本 | 文件路径 | 大小 | 更新时间 | 用途 |
|
||||
|---------|------|---------|------|---------|------|
|
||||
| node | 18-alpine | `node/node-18-alpine.tar` | ~42.86 MiB | 未记录 | 素材系统构建基础镜像 |
|
||||
| minio/mc | RELEASE.2025-04-16T18-13-26Z | `minio-mc/minio-mc-RELEASE.2025-04-16T18-13-26Z.tar` | ~28.47 MiB | 见 `minio-mc/VERSION.txt` | MinIO 客户端 |
|
||||
|
||||
## 重要约定
|
||||
|
||||
### 构建优先使用本地基础镜像
|
||||
|
||||
**内部构建脚本**应优先使用本地基础镜像,避免因网络问题导致构建失败。
|
||||
|
||||
构建脚本会按以下顺序查找基础镜像:
|
||||
1. 检查 Docker 本地是否已有该镜像
|
||||
2. 如果没有,从 `docker-images/` 目录加载
|
||||
3. 如果本地文件也不存在,才尝试在线拉取
|
||||
|
||||
这个约定主要体现在以下脚本里:
|
||||
- `scripts/build-material-image.ps1` - 素材系统镜像构建
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **版本管理**:每个镜像目录应包含 `VERSION.txt` 文件,记录版本和更新时间
|
||||
1. **版本管理**:新增或更新镜像时建议同步维护 `VERSION.txt`;当前 `node/` 目录没有版本文件,不能把“每个目录都有”当成事实
|
||||
2. **文件大小**:镜像文件较大,建议使用 `.gitignore` 排除,或使用 Git LFS
|
||||
3. **定期更新**:定期检查并更新基础镜像,确保安全性和兼容性
|
||||
4. **备份**:重要镜像建议备份到其他位置
|
||||
|
||||
@ -1,235 +0,0 @@
|
||||
# 功能需求实现报告
|
||||
|
||||
## 📋 需求列表
|
||||
|
||||
根据用户提供的需求,以下是各项功能的实现状态:
|
||||
|
||||
### ✅ 1. 用户管理页面的树形结构根为最高权限账号
|
||||
**状态:已实现**
|
||||
|
||||
**实现内容:**
|
||||
- 修改 `app/api/admin/users/route.ts`,确保返回 `isSuperAdmin` 和 `managedDepartment` 字段
|
||||
- 更新 `app/admin/page.tsx` 中的 `getUserTreeData()` 函数,正确识别最高权限账号
|
||||
- 用户管理页面以最高权限账号为根节点显示树形结构
|
||||
- 如果没有最高权限账号,fallback到显示管理员列表
|
||||
|
||||
**代码位置:**
|
||||
- `app/api/admin/users/route.ts` (第18-39行)
|
||||
- `app/admin/page.tsx` (第862-907行, 第1188-1396行)
|
||||
|
||||
---
|
||||
|
||||
### ✅ 2. 部门管理员账号显示合并到部门内置顶
|
||||
**状态:已实现**
|
||||
|
||||
**实现内容:**
|
||||
- 在 `getUsersByDepartmentWithAdminPinned()` 函数中,确保部门管理员在部门用户列表的最前面
|
||||
- 部门管理员显示特殊的"部门管理员"徽标
|
||||
- 普通用户显示在部门管理员之后
|
||||
|
||||
**代码位置:**
|
||||
- `app/admin/page.tsx` (第877-889行, 第1282-1310行)
|
||||
|
||||
---
|
||||
|
||||
### ✅ 3. 系统第一个注册的账号自动设置为最高权限
|
||||
**状态:已实现**
|
||||
|
||||
**实现内容:**
|
||||
- 在 `app/api/auth/register/route.ts` 中,检查用户总数
|
||||
- 如果用户总数为0(第一个用户),自动设置 `isSuperAdmin: true`
|
||||
- 确保系统初始化时第一个注册的用户获得最高权限
|
||||
|
||||
**代码位置:**
|
||||
- `app/api/auth/register/route.ts` (第82-94行)
|
||||
|
||||
---
|
||||
|
||||
### ✅ 4. 激活功能开关只有最高权限账号可以操作
|
||||
**状态:已实现**
|
||||
|
||||
**实现内容:**
|
||||
- 在 `app/admin/settings/page.tsx` 中,激活功能开关只有最高权限账号可以操作
|
||||
- 非最高权限账号看到开关但无法操作(显示禁用状态和提示文字)
|
||||
- 后端API (`app/api/system-settings/route.ts`) 也进行了权限检查
|
||||
|
||||
**代码位置:**
|
||||
- `app/admin/settings/page.tsx` (第596-611行)
|
||||
- `app/api/system-settings/route.ts` (第66-72行)
|
||||
|
||||
---
|
||||
|
||||
### ✅ 5. 邀请功能只有管理员及以上权限才能看到
|
||||
**状态:已实现**
|
||||
|
||||
**实现内容:**
|
||||
- 在 `app/admin/page.tsx` 中,邀请管理标签页只对管理员显示
|
||||
- 后端API (`app/api/invitations/route.ts`) 也进行了权限检查,只有管理员及以上权限可以访问
|
||||
|
||||
**代码位置:**
|
||||
- `app/admin/page.tsx` (第1013-1026行)
|
||||
- `app/api/invitations/route.ts` (第9-19行, 第44-54行)
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ 6. 多账号登录功能(支持每个网页登录不同账号)
|
||||
**状态:待实现**
|
||||
|
||||
**技术挑战:**
|
||||
- NextAuth默认使用Cookie存储session,所有标签页共享同一个session
|
||||
- 要实现多账号登录,需要修改NextAuth的session存储机制
|
||||
|
||||
**实现方案(建议):**
|
||||
|
||||
#### 方案1:使用多窗口隔离(推荐)
|
||||
- 每个浏览器窗口使用独立的session storage
|
||||
- 通过 `window.name` 或 URL参数区分不同的登录会话
|
||||
- 优点:实现相对简单,不需要修改NextAuth核心
|
||||
- 缺点:同一窗口内的标签页仍然共享session
|
||||
|
||||
#### 方案2:自定义Session存储
|
||||
- 修改NextAuth配置,使用自定义session存储(如localStorage + 数据库)
|
||||
- 每个标签页使用不同的session key
|
||||
- 优点:完全支持多账号登录
|
||||
- 缺点:需要大量修改现有代码,可能影响现有功能
|
||||
|
||||
#### 方案3:使用iframe隔离
|
||||
- 每个标签页使用iframe加载应用
|
||||
- iframe之间隔离,可以使用不同的session
|
||||
- 优点:完全隔离,支持多账号
|
||||
- 缺点:用户体验较差,可能影响性能
|
||||
|
||||
**推荐实现:方案1(多窗口隔离)**
|
||||
|
||||
**实现步骤:**
|
||||
1. 在登录时生成唯一的session key
|
||||
2. 将session key存储到URL参数或window.name
|
||||
3. 修改NextAuth配置,根据session key读取不同的session
|
||||
4. 每个窗口使用独立的session存储
|
||||
|
||||
**优先级:中**
|
||||
**预计工作量:2-3天**
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ 7. 断点续传功能保存文件上传进度
|
||||
**状态:部分实现(仅localStorage)**
|
||||
|
||||
**当前实现:**
|
||||
- 上传进度保存在 `localStorage` 中
|
||||
- 刷新页面后可以恢复表单数据,但文件需要重新选择
|
||||
- 无法实现真正的断点续传(从上次中断的地方继续上传)
|
||||
|
||||
**技术挑战:**
|
||||
- 真正的断点续传需要分块上传(chunked upload)
|
||||
- 需要服务器支持分块上传和合并
|
||||
- 需要数据库存储上传任务和进度
|
||||
|
||||
**实现方案(建议):**
|
||||
|
||||
#### 方案1:服务器端进度保存(简化版)
|
||||
- 创建上传任务记录(数据库表)
|
||||
- 在上传开始时创建任务,上传过程中更新进度
|
||||
- 刷新页面后可以从服务器获取进度
|
||||
- 优点:即使刷新页面也能看到进度
|
||||
- 缺点:仍然无法实现真正的断点续传(文件需要重新上传)
|
||||
|
||||
#### 方案2:完整的分块上传和断点续传
|
||||
- 实现分块上传(将大文件分成多个小块)
|
||||
- 每个小块独立上传,支持失败重试
|
||||
- 服务器端存储已上传的块,支持从中断处继续
|
||||
- 所有块上传完成后,服务器端合并文件
|
||||
- 优点:真正的断点续传,支持网络中断恢复
|
||||
- 缺点:实现复杂,需要大量开发和测试
|
||||
|
||||
**推荐实现:方案1(服务器端进度保存)**
|
||||
|
||||
**实现步骤:**
|
||||
1. 创建 `UploadTask` 数据库表
|
||||
```prisma
|
||||
model UploadTask {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
fileName String
|
||||
fileSize BigInt
|
||||
progress Int @default(0) // 0-100
|
||||
status String // pending/uploading/completed/failed
|
||||
metadata Json? // 存储表单数据
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
2. 创建上传任务API
|
||||
- `POST /api/upload/tasks` - 创建上传任务
|
||||
- `PATCH /api/upload/tasks/:id` - 更新上传进度
|
||||
- `GET /api/upload/tasks` - 获取用户的上传任务列表
|
||||
|
||||
3. 修改上传组件
|
||||
- 上传开始时创建任务
|
||||
- 上传过程中定期更新进度
|
||||
- 上传完成后标记任务为完成
|
||||
|
||||
4. 修改上传页面
|
||||
- 页面加载时检查是否有未完成的上传任务
|
||||
- 显示未完成的任务和进度
|
||||
- 允许用户继续上传或取消任务
|
||||
|
||||
**优先级:中**
|
||||
**预计工作量:3-5天**
|
||||
|
||||
---
|
||||
|
||||
## 📊 总结
|
||||
|
||||
### 已完成功能(5/7)
|
||||
1. ✅ 用户管理页面树形结构根为最高权限账号
|
||||
2. ✅ 部门管理员账号置顶显示
|
||||
3. ✅ 系统第一个注册账号自动设置为最高权限
|
||||
4. ✅ 激活功能开关权限控制
|
||||
5. ✅ 邀请功能权限控制
|
||||
|
||||
### 待实现功能(2/7)
|
||||
1. ⚠️ 多账号登录功能(需要修改NextAuth核心机制)
|
||||
2. ⚠️ 断点续传进度保存(需要实现分块上传)
|
||||
|
||||
### 建议
|
||||
1. **优先实现断点续传进度保存(方案1)**:这是用户更常用的功能,且实现相对简单
|
||||
2. **多账号登录功能**:如果用户确实需要,建议使用方案1(多窗口隔离),实现相对简单且影响范围小
|
||||
|
||||
---
|
||||
|
||||
## 🔧 技术细节
|
||||
|
||||
### 数据库变更
|
||||
- 无需新增数据库表(已完成的功能使用现有表结构)
|
||||
- 待实现功能可能需要新增表(如 `UploadTask` 表)
|
||||
|
||||
### API变更
|
||||
- ✅ `GET /api/admin/users` - 已更新,返回 `isSuperAdmin` 和 `managedDepartment`
|
||||
- ⚠️ 待实现:上传任务相关API(如 `/api/upload/tasks`)
|
||||
|
||||
### 前端变更
|
||||
- ✅ `app/admin/page.tsx` - 已更新用户管理页面显示逻辑
|
||||
- ✅ `app/admin/settings/page.tsx` - 已更新激活功能开关权限控制
|
||||
- ⚠️ 待实现:多账号登录相关组件
|
||||
- ⚠️ 待实现:上传进度保存相关组件
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **最高权限账号**:确保系统中至少有一个最高权限账号,否则某些功能可能无法正常使用
|
||||
2. **权限检查**:所有权限相关的功能都进行了前后端双重检查,确保安全性
|
||||
3. **向后兼容**:所有修改都保持了向后兼容,不会影响现有功能
|
||||
4. **测试建议**:建议在测试环境中充分测试所有功能,特别是权限控制相关功能
|
||||
|
||||
---
|
||||
|
||||
## 🚀 下一步行动
|
||||
|
||||
1. **立即部署已完成的修复**:将已完成的5个功能部署到生产环境
|
||||
2. **用户反馈**:收集用户对已实现功能的反馈,确认是否符合预期
|
||||
3. **实现断点续传进度保存**:如果用户需要,实现方案1(服务器端进度保存)
|
||||
4. **评估多账号登录需求**:确认用户是否真的需要多账号登录功能,如果不需要,可以暂缓实现
|
||||
|
||||
@ -1,140 +0,0 @@
|
||||
# 字段分类功能更新
|
||||
|
||||
## ✅ 已完成的修改
|
||||
|
||||
### 1. 标准字段模板更新
|
||||
|
||||
已将以下字段设置为**下拉选择类型**并**默认勾选"添加到分类"**:
|
||||
|
||||
| 字段键名 | 字段标签 | 类型 | 添加到分类 | 默认选项 |
|
||||
|---------|---------|------|-----------|---------|
|
||||
| `materialType` | 素材类型 | select | ✅ | 默认类型 |
|
||||
| `categoryName` | 使用店铺 | select | ✅ | 默认店铺 |
|
||||
| `sceneName` | 场景类别 | select | ✅ | 默认场景 |
|
||||
| `platformName` | 平台分类 | select | ✅ | 默认平台 |
|
||||
| `renderer` | 渲染器 | select | ✅ | 默认渲染器 |
|
||||
| `brandName` | 品牌名称 | select | ✅ | 默认品牌 |
|
||||
| `seriesName` | 系列/产品线 | select | ✅ | 默认产品线 |
|
||||
| `materialTexture` | 材质/工艺 | select | ✅ | 默认材质 |
|
||||
|
||||
### 2. 默认选项配置
|
||||
|
||||
每个勾选"添加到分类"的下拉选择字段都包含一个默认选项:
|
||||
|
||||
- **素材类型**: `默认类型` + 原有选项(3DMAX、PSD、AI等)
|
||||
- **使用店铺**: `默认店铺`
|
||||
- **场景类别**: `默认场景`
|
||||
- **平台分类**: `默认平台`
|
||||
- **渲染器**: `默认渲染器`
|
||||
- **品牌名称**: `默认品牌`
|
||||
- **产品线**: `默认产品线`
|
||||
- **材质工艺**: `默认材质`
|
||||
|
||||
## 📋 分类管理卡片显示说明
|
||||
|
||||
### 为什么分类管理卡片可能没有内容?
|
||||
|
||||
分类管理卡片显示的是**所有启用模板中勾选了"添加到分类"的 select 类型字段**。
|
||||
|
||||
如果卡片为空,可能的原因:
|
||||
|
||||
1. **没有启用的模板**
|
||||
- 需要创建模板并设置为"启用"状态
|
||||
|
||||
2. **模板中的字段没有勾选"添加到分类"**
|
||||
- 如果使用的是旧模板,需要重新编辑模板,确保字段勾选了"添加到分类"
|
||||
|
||||
3. **字段类型不是 select**
|
||||
- 确保字段类型是"下拉选择"
|
||||
|
||||
### 如何让分类管理卡片显示内容?
|
||||
|
||||
#### 方法 1: 创建新模板(推荐)
|
||||
|
||||
1. 进入**模板管理**
|
||||
2. 点击**创建模板**
|
||||
3. 使用**标准字段**添加以下字段:
|
||||
- 素材类型
|
||||
- 使用店铺
|
||||
- 场景类别
|
||||
- 平台分类
|
||||
- 渲染器
|
||||
- 品牌名称
|
||||
- 系列/产品线
|
||||
- 材质/工艺
|
||||
4. 这些字段会自动:
|
||||
- 类型设置为"下拉选择"
|
||||
- 勾选"添加到分类"
|
||||
- 包含默认选项
|
||||
5. 保存模板并设置为**启用**状态
|
||||
6. 进入**分类管理**,应该能看到对应的卡片
|
||||
|
||||
#### 方法 2: 编辑现有模板
|
||||
|
||||
1. 进入**模板管理**
|
||||
2. 编辑现有模板
|
||||
3. 找到需要添加到分类的字段
|
||||
4. 确保:
|
||||
- 字段类型为"下拉选择"
|
||||
- 勾选"添加到分类"复选框
|
||||
- 至少有一个选项(建议包含默认选项)
|
||||
5. 保存模板并确保模板状态为**启用**
|
||||
|
||||
## 🔍 验证步骤
|
||||
|
||||
1. **检查模板状态**
|
||||
- 进入模板管理
|
||||
- 确认至少有一个模板状态为"启用"
|
||||
|
||||
2. **检查字段配置**
|
||||
- 打开启用的模板
|
||||
- 确认有字段类型为"下拉选择"
|
||||
- 确认这些字段勾选了"添加到分类"
|
||||
- 确认字段有选项(optionsStatic 不为空)
|
||||
|
||||
3. **检查分类管理**
|
||||
- 进入系统设置 → 分类管理
|
||||
- 应该能看到"模板分类"卡片
|
||||
- 每个勾选了"添加到分类"的字段都会显示一个卡片
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **默认选项位置**
|
||||
- 默认选项会出现在选项列表的第一位
|
||||
- 例如:`['默认类型', '3DMAX', 'PSD', ...]`
|
||||
|
||||
2. **选项管理**
|
||||
- 可以在分类管理中编辑选项
|
||||
- 编辑后,所有使用该字段的模板都会同步更新
|
||||
|
||||
3. **字段键名唯一性**
|
||||
- 相同 `fieldKey` 的字段会合并到一个分类卡片
|
||||
- 例如:多个模板都有 `categoryName` 字段,只会显示一个"使用店铺"卡片
|
||||
|
||||
4. **模板状态**
|
||||
- 只有**启用**状态的模板中的字段才会出现在分类管理中
|
||||
- 草稿或归档状态的模板不会显示
|
||||
|
||||
## 🎯 预期效果
|
||||
|
||||
修改完成后,当您:
|
||||
|
||||
1. **创建新模板**并使用标准字段时:
|
||||
- 这些字段会自动配置为下拉选择
|
||||
- 自动勾选"添加到分类"
|
||||
- 包含默认选项
|
||||
|
||||
2. **查看分类管理**时:
|
||||
- 能看到所有勾选了"添加到分类"的字段卡片
|
||||
- 每个卡片显示字段名称和选项列表
|
||||
- 可以编辑选项,同步更新所有模板
|
||||
|
||||
3. **上传素材**时:
|
||||
- 可以选择这些分类字段的值
|
||||
- 默认选项会出现在下拉列表的第一位
|
||||
|
||||
---
|
||||
|
||||
**更新日期**: 2025-11-23
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
## 初次部署与“出厂设置”指南
|
||||
|
||||
本指南覆盖 NAS/内网环境从零安装的完整流程,亦可用于将现有测试环境**一键恢复到出厂状态**。建议严格按顺序执行,并在自述中记录执行时间,便于后续追溯。
|
||||
|
||||
### 1. 环境准备
|
||||
- 服务器/NAS:4C8G 以上、100GB 可用空间,建议启用 SSD/NVMe。
|
||||
- 系统:Ubuntu 22.04 LTS 或群晖 DSM(需支持 Docker / Podman)。
|
||||
- 依赖:Docker 24+、Docker Compose V2、Node.js 20(仅本地调试需要)、pnpm 或 npm。
|
||||
- 网络:可访问对象存储(MinIO/S3),放通 80/443/3000/5432/9000 等常用端口。
|
||||
- 证书:准备好公网域名和 TLS 证书(可选,Nginx/Traefik 层处理)。
|
||||
|
||||
### 2. 拉取代码与依赖
|
||||
```bash
|
||||
git clone https://your.repo/flnxi_sucai.git
|
||||
cd flnxi_sucai
|
||||
cp .env.example .env
|
||||
npm install # 或 pnpm install
|
||||
```
|
||||
- 将 `config-bianliang.env` 与 `.env` 一并填写,保证 `DATABASE_URL`、S3、域名、NEXTAUTH_SECRET 等关键变量存在。
|
||||
- NAS 环境若使用外置 Postgres/MinIO,可在 `.env` 中直接指向局域网地址。
|
||||
|
||||
### 3. 数据库初始化 / 恢复出厂
|
||||
> **⚠️ 会删除所有数据。** 确认已备份再执行。
|
||||
```bash
|
||||
npx prisma migrate reset --force --skip-seed
|
||||
```
|
||||
- 该命令会串行应用 `prisma/migrations` 下的所有迁移,包括最新的素材归属字段。
|
||||
- 如果在容器内执行,确保 `DATABASE_URL` 已指向真实数据库。
|
||||
|
||||
### 4. 生成 Prisma Client
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
- Docker 镜像构建过程会自动执行;裸机调试时建议显式运行,避免 TS/构建期间的类型缺失。
|
||||
|
||||
### 5. 启动服务
|
||||
```bash
|
||||
# 开发 / 单机调试
|
||||
npm run dev
|
||||
|
||||
# 生产部署(推荐)
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
```
|
||||
- Compose 会启动 Next.js、Nginx、Postgres、MinIO 等组件。若已有外部 Postgres/MinIO,可注释对应服务并调整 `.env`。
|
||||
|
||||
### 6. 系统初始化接口
|
||||
部署启动后,调用一次 `POST /api/system/init` 即可创建默认资源:
|
||||
```bash
|
||||
curl -X POST https://your-domain.com/api/system/init
|
||||
```
|
||||
返回值示例:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "系统初始化成功",
|
||||
"data": {
|
||||
"admin": { "email": "admin", "username": "admin", "password": "88888888" },
|
||||
"defaultDepartment": "默认部门",
|
||||
"defaultCategory": "默认店铺",
|
||||
"defaultScene": "默认场景"
|
||||
}
|
||||
}
|
||||
```
|
||||
- 首次登录后务必修改管理员密码,可在“个人中心 → 修改密码”完成。
|
||||
|
||||
### 7. 首次登录与检查
|
||||
1. 访问 `https://your-domain.com/login`,使用 `admin / 88888888` 登录。
|
||||
2. 进入后台 `/admin`,确认仪表盘可正常加载。
|
||||
3. 打开“系统设置”,填写对象存储、LOGO、前端域名等信息。
|
||||
4. 上传一份素材,验证转码、归属(`ownerType/ownerId`)、预览、下载链路是否正常。
|
||||
|
||||
### 8. 恢复为干净环境的快速指令
|
||||
若需要再次“清空一切”并保留最新 schema,可直接执行:
|
||||
```bash
|
||||
npx prisma migrate reset --force --skip-seed
|
||||
curl -X POST https://your-domain.com/api/system/init
|
||||
```
|
||||
可将上述两条命令收录到自定义脚本,例如 `scripts/reset-to-factory.ps1/.sh`,方便演示或客户验收前重置。
|
||||
|
||||
### 9. 常见问题
|
||||
- **`prisma migrate reset` 卡在输入确认**:加上 `--force --skip-generate`/`--skip-seed` 使其无交互运行。
|
||||
- **`postgres connection refused`**:确认容器/服务已启动,或在 `.env` 中关闭 TLS(`?sslmode=disable`)。
|
||||
- **MinIO/S3 访问失败**:在 System Settings 中补齐 `s3Endpoint/s3Bucket/s3PublicUrl`,并确保对象存储已创建对应桶。
|
||||
- **视频转码异常**:检查服务器是否已安装 `ffmpeg`,或在 Dockerfile 中启用 `apt install ffmpeg`。日志可在容器内 `/root/.pm2/logs` 或 `docker logs youyidian-3d-material` 查看。
|
||||
|
||||
### 10. 下一步
|
||||
- 按照 `docs/USER_ONBOARDING_GUIDE.md` 给团队成员做首登引导。
|
||||
- 开启钉钉登录、AI 改图、内网穿透等可选模块前,建议先在测试环境演练一遍整套流程。
|
||||
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
# MinIO Init 版本说明
|
||||
|
||||
## 当前配置
|
||||
|
||||
在 `docker-compose.prod.yml` 中,`minio-init` 服务使用:
|
||||
|
||||
```yaml
|
||||
minio-init:
|
||||
image: minio/mc:latest
|
||||
```
|
||||
|
||||
## 版本兼容性
|
||||
|
||||
`minio/mc` (MinIO Client) 与 `minio/minio` 服务器版本兼容性:
|
||||
|
||||
- **MinIO Server**: `minio/minio:RELEASE.2025-04-22T22-12-26Z`
|
||||
- **MinIO Client**: `minio/mc:latest` 或任何较新的版本都可以使用
|
||||
|
||||
## 推荐版本
|
||||
|
||||
### 选项 1: 使用 latest(推荐)
|
||||
```yaml
|
||||
image: minio/mc:latest
|
||||
```
|
||||
- ✅ 自动获取最新版本
|
||||
- ✅ 兼容性好
|
||||
- ⚠️ 需要网络连接拉取
|
||||
|
||||
### 选项 2: 使用特定版本(稳定性优先)
|
||||
```yaml
|
||||
image: minio/mc:RELEASE.2024-11-20T18-20-07Z
|
||||
```
|
||||
- ✅ 版本固定,稳定性高
|
||||
- ✅ 可重现部署
|
||||
- ⚠️ 需要手动指定版本号
|
||||
|
||||
## 手动下载镜像
|
||||
|
||||
如果服务器无法访问 Docker Hub,可以手动下载镜像:
|
||||
|
||||
### 方法 1: 在本地下载后导出
|
||||
```bash
|
||||
# 在本地(能访问外网的地方)
|
||||
docker pull minio/mc:latest
|
||||
docker save minio/mc:latest -o minio-mc-latest.tar
|
||||
|
||||
# 传输到服务器
|
||||
scp minio-mc-latest.tar root@45.249.208.118:/root/
|
||||
|
||||
# 在服务器上导入
|
||||
ssh root@45.249.208.118
|
||||
docker load -i /root/minio-mc-latest.tar
|
||||
```
|
||||
|
||||
### 方法 2: 使用国内镜像源
|
||||
```bash
|
||||
# 在服务器上配置 Docker 镜像加速器后
|
||||
docker pull minio/mc:latest
|
||||
```
|
||||
|
||||
### 方法 3: 使用阿里云镜像仓库
|
||||
```bash
|
||||
# 从阿里云镜像仓库拉取
|
||||
docker pull registry.cn-hangzhou.aliyuncs.com/acs/minio-mc:latest
|
||||
docker tag registry.cn-hangzhou.aliyuncs.com/acs/minio-mc:latest minio/mc:latest
|
||||
```
|
||||
|
||||
## 验证镜像
|
||||
|
||||
下载后验证镜像:
|
||||
```bash
|
||||
docker images | grep minio/mc
|
||||
docker run --rm minio/mc --version
|
||||
```
|
||||
|
||||
## 更新 docker-compose.yml
|
||||
|
||||
如果使用特定版本,更新 `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
minio-init:
|
||||
image: minio/mc:RELEASE.2024-11-20T18-20-07Z # 替换为实际版本
|
||||
# ... 其他配置
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. `minio/mc` 客户端版本通常向后兼容,使用 `latest` 通常是安全的
|
||||
2. 如果追求稳定性,建议使用与 MinIO Server 发布时间相近的版本
|
||||
3. 确保镜像已下载后再启动 `minio-init` 服务
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
## 可移植部署与生产规范(权威版)
|
||||
|
||||
本指南用于指导将本项目部署到任意公司、任意服务器(含 NAS、低配机)的稳定方案。后续开发与代码评审优先对齐本指南;如有新增需求,在此文档持续补充。
|
||||
|
||||
### 1. 设计目标
|
||||
- 可移植:支持任意服务器或 NAS(x86/ARM,Linux 为主,Windows/WSL 也可)。
|
||||
- 稳定与低开销:默认配置适配低性能机器,按需启用昂贵功能。
|
||||
- 安全与最小暴露:仅部署必要产物与环境变量;严控日志与资源权限。
|
||||
- 可配置:一切可变项均通过环境变量/系统设置控制,避免硬编码。
|
||||
- 结构整洁:功能分区清晰,杜绝与业务无关文件进入产线镜像。
|
||||
|
||||
### 2. 环境与依赖
|
||||
- Node 18(推荐容器化运行)。
|
||||
- PostgreSQL 14+(容器/外部 RDS 均可)。
|
||||
- 反向代理(nginx/caddy/traefik 均可选)。
|
||||
- 对象存储(MinIO/S3 兼容端点)。
|
||||
|
||||
建议使用 Docker Compose 在同机编排 Web 与数据库;受限网络环境请配置镜像源(示例见 `scripts/deploy/setup-docker-mirror.ps1`)。
|
||||
|
||||
### 3. 目录结构(产线关注)
|
||||
- app/…、components/… 前后端源代码(仅保留必要生产构建结果进入镜像)。
|
||||
- app/api/… 所有 API 路由(按功能分区)。
|
||||
- prisma/schema.prisma 数据模型。生产环境仅执行迁移或确保表结构同步。
|
||||
- scripts/deploy/… 部署脚本(客户端执行,不进入产线镜像)。
|
||||
- docs/… 文档集合(不进入产线镜像)。
|
||||
|
||||
生产镜像中不应包含:docs、测试样例、截图、临时脚本、未用到的部署脚本、编辑器配置等。
|
||||
|
||||
### 4. 环境变量(必须项与推荐项)
|
||||
- NEXTAUTH_URL:站点对外访问完整 URL(如 https://portal.example.com)。
|
||||
- APP_PUBLIC_URL:邀请链接生成优先使用的公网 URL(强烈推荐设置,避免回落 localhost)。
|
||||
- DATABASE_URL:PostgreSQL 连接串。
|
||||
- SESSION_SECRET / NEXTAUTH_SECRET:会话密钥(必须为强随机)。
|
||||
- S3_ENDPOINT/S3_ACCESS_KEY/S3_SECRET_KEY/S3_BUCKET/S3_REGION/S3_PUBLIC_URL:对象存储配置(启用文件/图像/视频上传时需要)。
|
||||
|
||||
说明:
|
||||
- 所有 URL 均不可写死,代码已按优先级读取请求头与 APP_PUBLIC_URL/NEXTAUTH_URL 组合回退。
|
||||
- 严禁将密钥写入代码或镜像层;通过环境变量注入或外部密钥管理。
|
||||
|
||||
### 5. 权限与角色
|
||||
- 角色:USER、ADMIN、SUPER_ADMIN(最高权限)。
|
||||
- 首个注册用户自动成为 SUPER_ADMIN,可在“管理后台”→“转移最高权限”移交。
|
||||
- 系统设置中“用户激活功能”开关仅 SUPER_ADMIN 可修改(后端强制校验)。
|
||||
- 邀请管理 UI/API 仅 ADMIN 或 SUPER_ADMIN 可见与可用。
|
||||
|
||||
### 6. 性能与资源控制
|
||||
|
||||
**核心原则**:性能开销优先,稳定性优先
|
||||
|
||||
- **默认限制**:
|
||||
- 大文件上传采用原生 XHR,支持断点续传状态保留(localStorage),避免复杂分片服务端状态。
|
||||
- 组件按需渲染,管理页数据分页/筛选(如列表增长后应启用分页)。
|
||||
- 日志级别在生产降噪(仅错误/关键信息)。
|
||||
|
||||
- **资源管理**:
|
||||
- 内存使用:空闲时 < 200MB,正常运行时 < 500MB,峰值 < 1GB
|
||||
- CPU使用:空闲时 < 5%,正常运行时 < 30%,峰值 < 80%
|
||||
- 数据库连接池:限制为10个连接(可在DATABASE_URL中配置)
|
||||
- 文件描述符:限制并发上传为5个
|
||||
|
||||
- **性能优化**:
|
||||
- ✅ 禁用Next.js Image Optimizer(图片已在上传时优化)
|
||||
- ✅ 使用流式处理大文件,避免全部加载到内存
|
||||
- ✅ 数据库查询只选择必要字段
|
||||
- ✅ 实现查询结果缓存(1分钟TTL)
|
||||
- ✅ 所有列表API实现分页(默认20条,最大100条)
|
||||
|
||||
**详细规范请参考**:[架构设计与编码规范](../架构设计与编码规范.md)
|
||||
- 可配置建议:
|
||||
- 通过反向代理/对象存储为大文件配置更大超时与缓冲。
|
||||
- 容器资源限制:CPU/内存上限,避免同机服务互相影响。
|
||||
- 关闭不需要的功能开关(如手动注册、激活制度)。
|
||||
|
||||
### 7. 安全与“代码加密”思路
|
||||
- Web 侧使用 Next.js 产物与 Node 运行时代码,难以“加密源代码”。可行替代:
|
||||
- 仅发布构建产物(生产 build)并通过容器运行,避免在目标机暴露源码与开发文件。
|
||||
- 将镜像推送到私有镜像仓库(公司私有 Registry),目标机仅拉取镜像并以只读运行。
|
||||
- 在容器层不包含 docs/tests/脚本等无关文件,减少代码泄露面。
|
||||
- Node 层可通过混淆器做有限混淆,但会影响调试与性能,不作为默认方案。
|
||||
- 后端密钥仅通过环境变量注入;数据库连接请限制来源 IP、强密码、TLS(如可用)。
|
||||
|
||||
### 8. 构建产物与最小化发布
|
||||
- 使用 `.dockerignore` 严格排除:
|
||||
- node_modules(按构建多阶段正确处理)、.git、docs、screenshots、scripts/deploy、本地开发配置、编辑器配置等。
|
||||
- 采用多阶段 Dockerfile:
|
||||
1) builder:安装依赖、构建 Next.js。
|
||||
2) runner:仅拷贝 .next/standalone 与 .next/static(或等价产物)+ 必需文件,NODE_ENV=production。
|
||||
- Compose 中将仅运行 Web/DB/MinIO(如需),不把开发脚本放进容器。
|
||||
|
||||
### 9. 数据库与迁移
|
||||
- Prisma schema 已包含 `User.isSuperAdmin` 与 `SystemSettings.enableUserActivation`。
|
||||
- 首次部署:`prisma migrate deploy` 或手动执行 SQL 保证表结构一致。
|
||||
- 升级变更:随版本附带 migration 或在运维指南中标注必需 SQL(避免运行期报错)。
|
||||
|
||||
### 10. 上传与断点续传
|
||||
- 前端以 localStorage 保存“表单与进度”快照,24 小时内提示恢复;文件句柄基于浏览器安全策略不可直接恢复,需用户重新选择文件。
|
||||
- 上传接口为相对路径 `/api/materials/upload`,建议在网关/代理侧加大超时;可选配置内网域名以提升上传速度。
|
||||
|
||||
### 11. 链接生成与多公司部署
|
||||
- 邀请链接生成顺序:`APP_PUBLIC_URL` > 请求头 `x-forwarded-host/origin/host` > 非 localhost 的 `NEXTAUTH_URL` > 兜底 `http://localhost:3000`。
|
||||
- 不同公司仅需配置环境变量与系统设置,无需修改代码。
|
||||
|
||||
### 12. 日志与监控
|
||||
- 生产日志默认记录错误与关键信息;可通过环境变量控制详细度。
|
||||
- 建议接入:
|
||||
- 反代访问日志(nginx/traefik)。
|
||||
- Node 进程健康探针(/healthz 简易实现或容器健康检查)。
|
||||
- 数据库连接/慢查询监控(PG_STAT_STATEMENTS)。
|
||||
|
||||
### 13. Windows/NAS 兼容注意
|
||||
- Windows 部署建议使用 WSL2 或 Docker Desktop;PowerShell 脚本已提供无交互参数(注意逐条执行,不使用 &&)。
|
||||
- NAS 部署(群晖/威联通):
|
||||
- 使用其 Docker/Container Manager,创建基于 compose 的栈;
|
||||
- ARM 架构需选择兼容 Node 基础镜像;
|
||||
- 映射持久卷:数据库数据目录、对象存储数据、上传临时目录。
|
||||
|
||||
### 14. 部署流程摘要(示意)
|
||||
- 准备环境变量(.env 或 CI 注入):DATABASE_URL、APP_PUBLIC_URL、NEXTAUTH_URL、NEXTAUTH_SECRET、S3_* 等。
|
||||
- 构建发布镜像(本地或 CI):
|
||||
- 多阶段 Dockerfile,推送到私有仓库。
|
||||
- 目标机拉取镜像并启动 compose:
|
||||
- 数据库容器(或外部 DB)→ Web 容器 → 反代路由。
|
||||
- 首次启动后执行 migrate(如需要)。
|
||||
- 验证功能:登录、邀请、上传、系统设置(特别是用户激活开关权限)。
|
||||
|
||||
### 15. 变更记录(与现有实现对齐)
|
||||
- 超级管理员:首个注册为 SUPER_ADMIN,可任意转移。
|
||||
- 系统设置“用户激活功能”:仅 SUPER_ADMIN 可改,后端强校验。
|
||||
- 邀请管理:仅 ADMIN 或 SUPER_ADMIN;链接不再写死 localhost。
|
||||
- 用户管理:以“最高权限”为根;部门管理员置顶并合并展示。
|
||||
- 断点续传:前端持久化上传状态,重开页面可恢复。
|
||||
|
||||
### 16. 代码约束清单(评审基准)
|
||||
- 所有可变项必须通过环境变量/系统设置注入;不得硬编码公司/域名。
|
||||
- 不向生产镜像引入与运行无关目录/文件(docs、脚本、图像等)。
|
||||
- 低开销优先:避免不必要的实时统计、全量加载、大对象驻留。
|
||||
- API 权限严谨:前端可见性与后端接口权限双保险。
|
||||
- 迁移/变更必须提供 DB 同步方案(migrate 或 SQL 说明)。
|
||||
|
||||
如需新增公司特定行为,必须通过配置开关或注入策略实现;不允许分叉硬编码。以后开发以此文档为主,必要时补充版本控制的“变更记录”。
|
||||
|
||||
|
||||
@ -1,271 +0,0 @@
|
||||
# 项目清理总结
|
||||
|
||||
**清理日期**: 2025年12月13日
|
||||
**清理目的**: 删除过期、重复、无用的文件,优化项目结构
|
||||
|
||||
## 清理内容
|
||||
|
||||
### 1. 临时文件和日志 ✅
|
||||
|
||||
已删除:
|
||||
- `ssh_*.txt` - SSH临时日志文件(12个)
|
||||
- `temp_*.txt` - 临时输出文件
|
||||
- `tsconfig.tsbuildinfo` - TypeScript构建缓存
|
||||
- `;` - 无效文件
|
||||
|
||||
### 2. 过期的部署脚本 ✅
|
||||
|
||||
已删除:
|
||||
- `deploy-simple.ps1` - 简化部署脚本(已被deploy-new-fixed.ps1替代)
|
||||
- `deploy-new.ps1` - 旧版部署脚本
|
||||
- `deploy-fixed.ps1` - 旧版修复脚本
|
||||
- `一键镜像部署.ps1` - 旧版一键部署
|
||||
- `copy-install-files.ps1` - 文件复制脚本
|
||||
- `setup-new-repo.ps1` - 仓库设置脚本
|
||||
- `secure_ssh.ps1` - SSH安全脚本
|
||||
|
||||
**保留**:
|
||||
- `deploy-new-fixed.ps1` - 当前使用的Harbor部署脚本 ✅
|
||||
- `push-to-harbor.ps1` - Harbor推送脚本 ✅
|
||||
|
||||
### 3. 重复的配置文件 ✅
|
||||
|
||||
已删除:
|
||||
- `docker-compose-registry.yml` - 重复的Registry配置
|
||||
- `docker-compose.registry.yml` - 重复的Registry配置
|
||||
- `docker-compose.standard.yml` - 标准配置(已合并到主配置)
|
||||
|
||||
**保留**:
|
||||
- `docker-compose.yml` - 主配置 ✅
|
||||
- `docker-compose.dev.yml` - 开发环境 ✅
|
||||
- `docker-compose.prod.yml` - 生产环境 ✅
|
||||
|
||||
### 4. 无用的二进制文件 ✅
|
||||
|
||||
已删除:
|
||||
- `minio-mc-latest.tar` - MinIO客户端压缩包
|
||||
- `minio-mc-latest.tar.gz` - MinIO客户端压缩包
|
||||
- `known_hosts` - SSH已知主机文件
|
||||
|
||||
### 5. 过期的脚本文件 ✅
|
||||
|
||||
已删除:
|
||||
- `fix_user_permissions.js` - 用户权限修复脚本
|
||||
- `verify_first_user.js` - 首位用户验证脚本
|
||||
- `fix-encoding.sh` - 编码修复脚本
|
||||
- `fix-line-endings.sh` - 行尾修复脚本
|
||||
- `飞牛nas安装文件复制.bat` - NAS安装脚本
|
||||
|
||||
### 6. 根目录文档归档 ✅
|
||||
|
||||
已归档到 `docs/archive/`(23个文件):
|
||||
- `安装与出厂设置指南.md`
|
||||
- `版本架构设计方案.md`
|
||||
- `代码优化总结.md`
|
||||
- `服务器测试配置指南.md`
|
||||
- `更新日志.md`
|
||||
- `后续修改.txt`
|
||||
- `架构设计与编码规范.md`
|
||||
- `设计系统.md`
|
||||
- `设计系统迁移示例.md`
|
||||
- `设计系统使用指南.md`
|
||||
- `私有镜像仓库部署指南.md`
|
||||
- `素材模板迁移指南.md`
|
||||
- `项目说明-由壹点3D素材.md`
|
||||
- `项目优化分析与建议.md`
|
||||
- `新手用户使用指引.md`
|
||||
- `性能优化指南.md`
|
||||
- `一键部署快速指南.md`
|
||||
- `一键部署完整指南.md`
|
||||
- `优化计划与优先级.md`
|
||||
- `优化记录.md`
|
||||
- `在git.1080p.hk部署镜像仓库指南.md`
|
||||
- `AI智能体部署助手提示词.md`
|
||||
- `change.md`
|
||||
|
||||
**保留在根目录**:
|
||||
- `README.md` - 项目说明(已更新) ✅
|
||||
- `VERSION.txt` - 版本信息 ✅
|
||||
- `config-bianliang.env` - 环境配置 ✅
|
||||
|
||||
### 7. 聊天记录清理 ✅
|
||||
|
||||
已删除:
|
||||
- `docs/chats/` - 整个聊天记录目录
|
||||
|
||||
### 8. 文档目录清理 ✅
|
||||
|
||||
已删除:
|
||||
- `docs/deployment/README.md` - 重复的README(已有DEPLOYMENT.md)
|
||||
|
||||
## 清理后的项目结构
|
||||
|
||||
### 根目录文件(精简后)
|
||||
|
||||
```
|
||||
.
|
||||
├── .env.example # 环境变量模板
|
||||
├── .eslintrc.json # ESLint配置
|
||||
├── .gitignore # Git忽略文件
|
||||
├── config-bianliang.env # 环境配置
|
||||
├── deploy-new-fixed.ps1 # Harbor部署脚本
|
||||
├── docker-compose.yml # Docker主配置
|
||||
├── docker-compose.dev.yml # 开发环境配置
|
||||
├── docker-compose.prod.yml # 生产环境配置
|
||||
├── Dockerfile # Docker镜像定义
|
||||
├── jest.config.js # Jest配置
|
||||
├── jest.setup.js # Jest设置
|
||||
├── middleware.ts # Next.js中间件
|
||||
├── next.config.js # Next.js配置
|
||||
├── package.json # 项目依赖
|
||||
├── postcss.config.js # PostCSS配置
|
||||
├── push-to-harbor.ps1 # Harbor推送脚本
|
||||
├── README.md # 项目说明(已更新)
|
||||
├── tailwind.config.ts # Tailwind配置
|
||||
├── tsconfig.json # TypeScript配置
|
||||
└── VERSION.txt # 版本信息
|
||||
```
|
||||
|
||||
### 文档目录结构
|
||||
|
||||
```
|
||||
docs/
|
||||
├── api/ # API文档
|
||||
│ └── template-system-api.md
|
||||
├── archive/ # 归档文档(23个)
|
||||
├── deployment/ # 部署文档
|
||||
│ ├── DEPLOYMENT.md
|
||||
│ ├── pre-deployment-checklist.md
|
||||
│ └── quick-deployment-guide.md
|
||||
├── guides/ # 指南文档(保留)
|
||||
├── reports/ # 报告文档(保留)
|
||||
├── scripts/ # 脚本文档(保留)
|
||||
├── user-guide/ # 用户手册
|
||||
│ └── template-system-guide.md
|
||||
├── template-system-optimization-summary.md
|
||||
└── PROJECT_CLEANUP_SUMMARY.md (本文档)
|
||||
```
|
||||
|
||||
## 保留的重要文件
|
||||
|
||||
### 部署相关
|
||||
- ✅ `deploy-new-fixed.ps1` - 当前使用的Harbor部署脚本
|
||||
- ✅ `push-to-harbor.ps1` - Harbor推送脚本
|
||||
- ✅ `docker-compose.yml` - Docker主配置
|
||||
- ✅ `docker-compose.dev.yml` - 开发环境
|
||||
- ✅ `docker-compose.prod.yml` - 生产环境
|
||||
|
||||
### 配置文件
|
||||
- ✅ `config-bianliang.env` - 环境配置
|
||||
- ✅ `.env.example` - 环境变量模板
|
||||
- ✅ `package.json` - 项目依赖
|
||||
- ✅ `tsconfig.json` - TypeScript配置
|
||||
- ✅ `next.config.js` - Next.js配置
|
||||
|
||||
### 文档
|
||||
- ✅ `README.md` - 项目说明(已更新为简洁版本)
|
||||
- ✅ `VERSION.txt` - 版本信息
|
||||
- ✅ `docs/deployment/` - 完整的部署文档
|
||||
- ✅ `docs/api/` - API文档
|
||||
- ✅ `docs/user-guide/` - 用户手册
|
||||
|
||||
### 脚本
|
||||
- ✅ `scripts/migrate-template-data.ts` - 数据迁移脚本
|
||||
- ✅ `scripts/verify-template-data.ts` - 数据验证脚本
|
||||
- ✅ `scripts/README.md` - 脚本使用说明
|
||||
|
||||
## 清理效果
|
||||
|
||||
### 文件数量变化
|
||||
- **删除**: ~50个文件
|
||||
- **归档**: 23个文档文件
|
||||
- **保留**: 核心文件和最新文档
|
||||
|
||||
### 项目结构优化
|
||||
- ✅ 根目录更简洁,只保留必要文件
|
||||
- ✅ 文档结构更清晰,分类明确
|
||||
- ✅ 删除了所有临时文件和日志
|
||||
- ✅ 移除了过期的部署脚本
|
||||
- ✅ 归档了历史文档
|
||||
|
||||
### 维护性提升
|
||||
- ✅ 更容易找到当前使用的文件
|
||||
- ✅ 减少了混淆和重复
|
||||
- ✅ 降低了项目复杂度
|
||||
- ✅ 提高了代码可读性
|
||||
|
||||
## 后续建议
|
||||
|
||||
### 1. 检查归档文件
|
||||
查看 `docs/archive/` 中的文件,确认不需要后可以删除整个目录:
|
||||
|
||||
```bash
|
||||
# 查看归档文件
|
||||
ls docs/archive/
|
||||
|
||||
# 如果确认不需要,可以删除
|
||||
rm -rf docs/archive/
|
||||
```
|
||||
|
||||
### 2. 提交清理后的代码
|
||||
|
||||
```bash
|
||||
# 查看变更
|
||||
git status
|
||||
|
||||
# 添加所有变更
|
||||
git add .
|
||||
|
||||
# 提交
|
||||
git commit -m "chore: 清理项目文件,优化项目结构
|
||||
|
||||
- 删除临时文件和日志
|
||||
- 移除过期的部署脚本
|
||||
- 清理重复的配置文件
|
||||
- 归档历史文档到 docs/archive/
|
||||
- 更新 README.md
|
||||
- 添加清理总结文档"
|
||||
|
||||
# 推送到远程
|
||||
git push
|
||||
```
|
||||
|
||||
### 3. 定期清理
|
||||
|
||||
建议每个月或每个大版本发布后进行一次项目清理:
|
||||
- 删除临时文件
|
||||
- 归档过期文档
|
||||
- 更新README
|
||||
- 清理无用依赖
|
||||
|
||||
### 4. 文档维护
|
||||
|
||||
- 保持 `README.md` 简洁明了
|
||||
- 详细文档放在 `docs/` 目录
|
||||
- 及时更新部署文档
|
||||
- 记录重要变更
|
||||
|
||||
## 清理清单
|
||||
|
||||
- [x] 删除临时文件和日志
|
||||
- [x] 移除过期的部署脚本
|
||||
- [x] 清理重复的配置文件
|
||||
- [x] 删除无用的二进制文件
|
||||
- [x] 移除过期的脚本文件
|
||||
- [x] 归档根目录文档
|
||||
- [x] 删除聊天记录
|
||||
- [x] 清理文档目录
|
||||
- [x] 更新README.md
|
||||
- [x] 创建清理总结文档
|
||||
|
||||
## 总结
|
||||
|
||||
本次清理成功优化了项目结构,删除了约50个过期、重复和无用的文件,归档了23个历史文档。清理后的项目更加简洁、清晰,易于维护和理解。
|
||||
|
||||
所有核心功能文件和最新文档都已保留,不会影响项目的正常开发和部署。
|
||||
|
||||
---
|
||||
|
||||
**清理完成时间**: 2025年12月13日
|
||||
**清理执行人**: AI Assistant
|
||||
**文档版本**: 1.0
|
||||
@ -1,330 +0,0 @@
|
||||
# 项目状态报告
|
||||
|
||||
**更新日期**: 2025年12月13日
|
||||
**项目版本**: 1.0.0
|
||||
**状态**: ✅ 生产就绪
|
||||
|
||||
## 📊 项目概况
|
||||
|
||||
### 完成度
|
||||
|
||||
| 模块 | 完成度 | 状态 |
|
||||
|------|--------|------|
|
||||
| 核心基础设施 | 100% | ✅ 完成 |
|
||||
| 模板管理功能 | 100% | ✅ 完成 |
|
||||
| 自定义字段功能 | 70% | 🟡 部分完成 |
|
||||
| 数据存储和兼容性 | 0% | ⚪ 未开始 |
|
||||
| 首页分类Tag系统 | 0% | ⚪ 未开始 |
|
||||
| 后台分类管理 | 0% | ⚪ 未开始 |
|
||||
| 性能优化和测试 | 100% | ✅ 完成 |
|
||||
| 文档和部署 | 100% | ✅ 完成 |
|
||||
|
||||
**总体完成度**: 阶段1-4(高优先级)100%,阶段7-8(测试和文档)100%
|
||||
|
||||
### 测试覆盖
|
||||
|
||||
- **单元测试**: 118个测试用例全部通过 ✅
|
||||
- **核心模块覆盖率**: >80% ✅
|
||||
- **集成测试**: 待完成 ⚪
|
||||
|
||||
### 文档完整性
|
||||
|
||||
- ✅ API文档(`docs/api/template-system-api.md`)
|
||||
- ✅ 用户手册(`docs/user-guide/template-system-guide.md`)
|
||||
- ✅ 部署文档(`docs/deployment/`)
|
||||
- ✅ 项目总结(`docs/template-system-optimization-summary.md`)
|
||||
- ✅ 脚本说明(`scripts/README.md`)
|
||||
|
||||
## 🎯 已完成的工作
|
||||
|
||||
### 阶段1: 核心基础设施优化 ✅
|
||||
|
||||
1. **模板验证工具** - 完成
|
||||
- 支持内置模板和自定义模板
|
||||
- 详细的错误信息和日志
|
||||
- 模板状态检查
|
||||
- 25个单元测试
|
||||
|
||||
2. **上传API模板验证** - 完成
|
||||
- 集成模板验证器
|
||||
- 优化错误处理
|
||||
- 性能监控
|
||||
|
||||
3. **字段池同步机制** - 完成
|
||||
- 增量同步支持
|
||||
- 失败重试机制
|
||||
- 详细的同步日志
|
||||
- 34个单元测试
|
||||
|
||||
4. **选项清洗逻辑** - 完成
|
||||
- 处理多种边界情况
|
||||
- 详细的清洗日志
|
||||
- 自动去重和规范化
|
||||
|
||||
### 阶段2: 模板管理功能增强 ✅
|
||||
|
||||
5. **模板版本控制** - 完成
|
||||
- 并发冲突检测
|
||||
- 自动版本号递增
|
||||
- 前端冲突提示
|
||||
|
||||
6. **数据完整性验证** - 完成
|
||||
- 必填字段验证
|
||||
- 字段key唯一性检查
|
||||
- 自动数据转换
|
||||
|
||||
7. **错误处理优化** - 完成
|
||||
- 统一错误响应格式
|
||||
- 详细的错误日志
|
||||
- Prisma错误代码处理
|
||||
|
||||
8. **模板使用统计** - 完成
|
||||
- 素材数量统计
|
||||
- 字段填写率分析
|
||||
- 优化建议
|
||||
|
||||
### 阶段3: 自定义字段功能完善 🟡
|
||||
|
||||
9. **字段管理UI优化** - 完成
|
||||
- 实时key唯一性验证
|
||||
- 字段复制功能
|
||||
- 流畅的拖拽排序
|
||||
- 29个单元测试
|
||||
|
||||
10. **Select字段选项管理** - 完成
|
||||
- 支持多种输入格式
|
||||
- 自动清洗和规范化
|
||||
- 自动去重
|
||||
|
||||
11. **字段验证规则配置** - 未完成 ⚪
|
||||
12. **字段条件显示** - 未完成 ⚪
|
||||
|
||||
### 阶段7: 性能优化和测试 ✅
|
||||
|
||||
22. **数据库查询优化** - 完成
|
||||
- 添加关键索引
|
||||
- 优化查询性能
|
||||
- 数据库迁移已应用
|
||||
|
||||
23. **前端性能优化** - 完成
|
||||
- 防抖Hooks
|
||||
- SWR缓存Hooks
|
||||
- React优化
|
||||
- 17个单元测试
|
||||
|
||||
24. **单元测试** - 完成
|
||||
- 118个测试用例
|
||||
- 核心模块覆盖率>80%
|
||||
|
||||
### 阶段8: 文档和部署 ✅
|
||||
|
||||
26. **API文档** - 完成
|
||||
27. **用户手册** - 完成
|
||||
28. **数据库迁移脚本** - 完成
|
||||
- 数据迁移脚本
|
||||
- 数据验证脚本
|
||||
- npm命令集成
|
||||
- 部署检查清单
|
||||
|
||||
## 🚀 部署准备
|
||||
|
||||
### 部署脚本
|
||||
|
||||
- ✅ `deploy-new-fixed.ps1` - Harbor部署脚本
|
||||
- ✅ `push-to-harbor.ps1` - Harbor推送脚本
|
||||
- ✅ 数据迁移脚本
|
||||
- ✅ 数据验证脚本
|
||||
|
||||
### 部署文档
|
||||
|
||||
- ✅ 完整部署说明(`docs/deployment/DEPLOYMENT.md`)
|
||||
- ✅ 快速部署指南(`docs/deployment/quick-deployment-guide.md`)
|
||||
- ✅ 部署前检查清单(`docs/deployment/pre-deployment-checklist.md`)
|
||||
|
||||
### 环境配置
|
||||
|
||||
- ✅ `.env.example` - 环境变量模板
|
||||
- ✅ `config-bianliang.env` - 环境配置
|
||||
- ✅ Docker配置文件
|
||||
|
||||
## 📈 性能指标
|
||||
|
||||
| 操作 | 目标 | 实际 | 状态 |
|
||||
|------|------|------|------|
|
||||
| 模板列表加载 | < 500ms | ~300ms | ✅ |
|
||||
| 素材上传验证 | < 100ms | ~50ms | ✅ |
|
||||
| 字段池同步 | < 1s | ~500ms | ✅ |
|
||||
| 首页Tag响应 | < 100ms | ~50ms | ✅ |
|
||||
|
||||
## 🧪 测试状态
|
||||
|
||||
### 单元测试
|
||||
|
||||
```
|
||||
✅ 模板验证器: 25个测试
|
||||
✅ 字段池管理器: 34个测试
|
||||
✅ 模板CRUD API: 13个测试
|
||||
✅ 防抖Hooks: 17个测试
|
||||
✅ TemplateFieldsEditor: 29个测试
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ 总计: 118个测试全部通过
|
||||
```
|
||||
|
||||
### 代码覆盖率
|
||||
|
||||
```
|
||||
✅ template-validator.ts: 92.62%
|
||||
✅ useDebounce.ts: 100%
|
||||
✅ upload-templates API: 85.88%
|
||||
⚠️ field-pool.ts: 36.95%
|
||||
⚠️ upload-templates/[id] API: 56.66%
|
||||
```
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
### 核心文件
|
||||
|
||||
```
|
||||
.
|
||||
├── app/ # Next.js应用
|
||||
├── components/ # React组件
|
||||
├── lib/ # 工具库
|
||||
│ ├── template-validator.ts
|
||||
│ ├── field-pool.ts
|
||||
│ └── hooks/
|
||||
├── prisma/ # 数据库
|
||||
├── scripts/ # 脚本工具
|
||||
│ ├── migrate-template-data.ts
|
||||
│ └── verify-template-data.ts
|
||||
├── docs/ # 文档
|
||||
│ ├── api/
|
||||
│ ├── deployment/
|
||||
│ └── user-guide/
|
||||
└── tests/ # 测试文件
|
||||
```
|
||||
|
||||
### 文档结构
|
||||
|
||||
```
|
||||
docs/
|
||||
├── api/ # API文档
|
||||
├── deployment/ # 部署文档
|
||||
├── user-guide/ # 用户手册
|
||||
├── archive/ # 归档文档
|
||||
├── guides/ # 指南
|
||||
├── reports/ # 报告
|
||||
├── template-system-optimization-summary.md
|
||||
├── PROJECT_CLEANUP_SUMMARY.md
|
||||
└── PROJECT_STATUS.md (本文档)
|
||||
```
|
||||
|
||||
## 🔄 待完成的工作
|
||||
|
||||
### 低优先级功能
|
||||
|
||||
以下功能为低优先级,不影响核心功能使用:
|
||||
|
||||
#### 阶段3: 自定义字段功能(部分)
|
||||
- [ ] 任务11: 字段验证规则配置
|
||||
- [ ] 任务12: 字段条件显示
|
||||
|
||||
#### 阶段4: 数据存储和兼容性
|
||||
- [ ] 任务13: 优化自定义字段数据存储
|
||||
- [ ] 任务14: 字段迁移工具
|
||||
- [ ] 任务15: 草稿自动保存
|
||||
|
||||
#### 阶段5: 首页分类Tag系统优化
|
||||
- [ ] 任务16-18: Tag按钮优化
|
||||
|
||||
#### 阶段6: 后台分类管理优化
|
||||
- [ ] 任务19-21: 分类管理增强
|
||||
|
||||
#### 阶段7: 性能优化和测试(部分)
|
||||
- [ ] 任务25: 集成测试
|
||||
|
||||
#### 阶段8: 文档和部署(部分)
|
||||
- [ ] 任务29: 部署到测试环境
|
||||
- [ ] 任务30: 部署到生产环境
|
||||
|
||||
## 📋 下一步行动
|
||||
|
||||
### 立即行动
|
||||
|
||||
1. **代码提交**
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "chore: 项目清理和文档更新"
|
||||
git push
|
||||
```
|
||||
|
||||
2. **测试环境部署**
|
||||
```powershell
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
```
|
||||
|
||||
3. **数据迁移**
|
||||
```bash
|
||||
ssh root@your-server
|
||||
docker exec flnxi-material npx prisma migrate deploy
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
```
|
||||
|
||||
### 短期计划(1-2周)
|
||||
|
||||
1. 在测试环境验证所有功能
|
||||
2. 收集用户反馈
|
||||
3. 修复发现的bug
|
||||
4. 优化用户体验细节
|
||||
|
||||
### 中期计划(1-2月)
|
||||
|
||||
1. 实现字段验证规则配置(任务11)
|
||||
2. 实现草稿自动保存(任务15)
|
||||
3. 优化首页Tag系统(任务16-18)
|
||||
|
||||
### 长期计划(3-6月)
|
||||
|
||||
1. 实现字段条件显示(任务12)
|
||||
2. 完善分类管理功能(任务19-21)
|
||||
3. 实现字段迁移工具(任务14)
|
||||
4. 添加更多集成测试
|
||||
|
||||
## 🎉 项目亮点
|
||||
|
||||
### 技术亮点
|
||||
|
||||
1. **完善的测试覆盖** - 118个单元测试,核心模块覆盖率>80%
|
||||
2. **性能优化** - 所有关键操作响应时间达标
|
||||
3. **版本控制** - 并发冲突检测,数据一致性保证
|
||||
4. **错误处理** - 统一的错误响应格式,详细的日志
|
||||
5. **数据完整性** - 多层验证,自动清洗和规范化
|
||||
|
||||
### 工程亮点
|
||||
|
||||
1. **完整的文档** - API文档、用户手册、部署指南齐全
|
||||
2. **自动化部署** - 一键部署脚本,Harbor集成
|
||||
3. **数据迁移** - 完善的迁移和验证脚本
|
||||
4. **项目清理** - 简洁的项目结构,易于维护
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
- **技术负责人**: [姓名] - [联系方式]
|
||||
- **运维负责人**: [姓名] - [联系方式]
|
||||
- **产品负责人**: [姓名] - [联系方式]
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
### 2025-12-13
|
||||
|
||||
- ✅ 完成模板系统优化(阶段1-4)
|
||||
- ✅ 完成性能优化和测试(阶段7)
|
||||
- ✅ 完成文档和部署准备(阶段8)
|
||||
- ✅ 项目清理和结构优化
|
||||
- ✅ 更新README和文档
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: 1.0
|
||||
**最后更新**: 2025年12月13日
|
||||
**状态**: 生产就绪 ✅
|
||||
@ -1,22 +1,57 @@
|
||||
# 文档索引
|
||||
|
||||
> 本目录集中存放部署、运维与问题排查资料,按主题分组,便于快速查找。
|
||||
本目录只保留当前项目仍有维护价值的文档入口。文档口径必须服务当前项目真相:企业/团队私有化交付、NAS / Linux 部署、浏览器使用、许可证控制、低维护验收。
|
||||
|
||||
## 快速入口
|
||||
- **部署流程**:`guides/安全部署指南.md`、`guides/安全部署说明.md`
|
||||
- **团队协作**:`WORKFLOW.md`
|
||||
- **更新记录**:`guides/更新说明.md`、`reports/DEPLOYMENT_REPORT_20251028.md`
|
||||
- **故障排查**:`guides/上传问题完整解决方案.md`、`reports/NETWORK_FIX_REPORT.md`
|
||||
- **网络/安全**:`guides/FRP服务器诊断修复报告.md`、`guides/SSH安全评估报告.md`
|
||||
## 1. 必看入口
|
||||
|
||||
## 目录结构
|
||||
- `guides/`:正式的操作指南与流程文档
|
||||
- `reports/`:部署、优化、故障排查等报告
|
||||
- `chats/`:沟通记录与会议纪要
|
||||
- 其它 `.md` 文件:历史材料、需求背景和补充说明
|
||||
- `../README.md`
|
||||
仓库总说明,先看这里确认项目定位、目录结构、参考源码边界和常用入口。
|
||||
- `../AGENTS.md`
|
||||
项目规则入口,定义参考源码目录、修改边界和目标环境。
|
||||
- `当前工作流.md`
|
||||
当前开发、验证、部署和文档维护的基本流程。
|
||||
|
||||
## 使用建议
|
||||
1. 先阅读 `guides/` 获取标准流程,再按需查看对应 `reports/`。
|
||||
2. 文档使用 UTF-8 编码,推荐在支持中文的编辑器中打开。
|
||||
3. 更新或新增文档时,请在此文件的“快速入口”中补充链接,方便他人定位。
|
||||
## 2. 部署与交付
|
||||
|
||||
- `deployment/部署说明.md`
|
||||
主应用标准部署、快速步骤、部署前检查、验证和回滚说明。
|
||||
- `deployment/多品牌 NAS 与 Linux 交付差异清单.md`
|
||||
面向不同 NAS / Linux 客户现场的部署前采集清单。
|
||||
- `初次部署与出厂设置指南.md`
|
||||
从零安装、系统初始化和恢复出厂流程。
|
||||
- `新手用户上手指引.md`
|
||||
给普通使用者看的基础操作指引。
|
||||
|
||||
## 3. 许可证与授权
|
||||
|
||||
- `许可证服务接入说明.md`
|
||||
当前主应用接入 `license-server` 的项目编码、公开接口和联调方式。
|
||||
- `其他项目接入许可证系统说明.md`
|
||||
给其他项目负责人使用的许可证系统接入说明。
|
||||
|
||||
## 4. 功能与 API
|
||||
|
||||
- `api/模板系统API文档.md`
|
||||
上传模板、字段和模板系统相关 API。
|
||||
- `user-guide/模板系统用户手册.md`
|
||||
模板系统的使用说明。
|
||||
- `项目目标与目标环境.md`
|
||||
项目最终目标、目标客户和部署环境结论。
|
||||
|
||||
## 5. 内部参考资料
|
||||
|
||||
- `参考源码功能吸收整理_20260513.md`
|
||||
只在需要核对旧参考源码能力边界时阅读;它不是当前产品功能清单,也不是搬旧功能的依据。
|
||||
- `商业化地基排查问题清单_20260524.md`
|
||||
内部整改跟踪清单,混有已完成项、待整改项和审计痕迹;只在追问题和核对状态时查看,不作为部署、接入或普通使用手册。
|
||||
- `许可证链路安全评估_20260312.md`
|
||||
历史安全评估快照,部分条目已被后续代码和部署配置修正;保留作溯源参考,不作为当前整改入口。
|
||||
|
||||
## 6. 使用原则
|
||||
|
||||
1. 判断当前项目事实,先看 `../README.md`、`../AGENTS.md` 和当前代码。
|
||||
2. 做部署和验收,先看 `deployment/部署说明.md`。
|
||||
3. 做 NAS / Linux 私有化交付,先看 `deployment/多品牌 NAS 与 Linux 交付差异清单.md`。
|
||||
4. 做许可证联调,先看 `许可证服务接入说明.md`。
|
||||
5. 做参考源码功能吸收,先看 `参考源码功能吸收整理_20260513.md`,不要直接搬旧源码。
|
||||
6. 旧报告、旧交付材料、旧交接文档和一次性排障记录不作为当前默认入口;内部整改清单只在追问题和核对状态时查看。
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
# 模板功能改进说明
|
||||
|
||||
## ✅ 已完成的修改
|
||||
|
||||
### 1. 预制模板隐藏状态同步到首页
|
||||
|
||||
**问题**:预制模板隐藏后,首页的 tag 不会一起隐藏
|
||||
|
||||
**解决方案**:
|
||||
- 将隐藏状态从 `localStorage` 迁移到数据库 `SystemSettings` 表
|
||||
- 新增 `hiddenBuiltinTemplates` 字段(String 类型,存储 JSON 数组)
|
||||
- 修改 `/api/templates` 和 `/api/upload-templates` API,读取隐藏状态并过滤
|
||||
- 修改 `TemplateManager` 组件,从数据库读取和保存隐藏状态
|
||||
|
||||
**修改文件**:
|
||||
- `prisma/schema.prisma` - 添加 `hiddenBuiltinTemplates` 字段
|
||||
- `app/api/system-settings/route.ts` - 支持读取和更新隐藏状态
|
||||
- `app/api/templates/route.ts` - 过滤隐藏的预制模板
|
||||
- `app/api/upload-templates/route.ts` - 过滤隐藏的预制模板
|
||||
- `app/admin/_components/TemplateManager.tsx` - 从数据库读取/保存隐藏状态
|
||||
|
||||
**数据库迁移**:
|
||||
⚠️ **重要说明**:数据库迁移只需要执行**一次**(添加 `hiddenBuiltinTemplates` 字段),之后隐藏/显示预制模板的操作**不需要**再次迁移。
|
||||
|
||||
- **第一次**:运行迁移添加字段(已完成)
|
||||
```bash
|
||||
npx prisma db push # 已执行
|
||||
```
|
||||
|
||||
- **之后的操作**:隐藏/显示预制模板只是更新 `SystemSettings` 表中的数据值,不是修改数据库结构,所以**不需要迁移**。这就像更新其他系统设置(如站点名称、Logo等)一样,只是数据更新,不是结构修改。
|
||||
|
||||
---
|
||||
|
||||
### 2. 预览模板样式同步到上传素材表单
|
||||
|
||||
**问题**:预览面板和实际上传表单的样式不一致
|
||||
|
||||
**解决方案**:
|
||||
- 将预览面板的样式类应用到上传表单
|
||||
- 统一使用 `rounded-2xl`、渐变背景、阴影等样式
|
||||
- 统一必填字段的标识样式(紫色渐变标签)
|
||||
|
||||
**修改文件**:
|
||||
- `components/UploadModal.tsx` - 同步预览面板的样式类
|
||||
|
||||
**样式改进**:
|
||||
- 输入框:`rounded-2xl`、渐变背景、阴影效果
|
||||
- 必填标签:紫色渐变标签(`from-purple-500 to-indigo-600`)
|
||||
- 标签样式:`text-xs font-semibold`、`tracking-tight`
|
||||
- 过渡动画:`transition-all duration-300 ease-[cubic-bezier(0.4,0,0.2,1)]`
|
||||
|
||||
---
|
||||
|
||||
### 3. 自定义模板中隐藏"素材类型"字段
|
||||
|
||||
**问题**:自定义模板的目的是灵活应对不同客户群体,不需要"素材类型"字段
|
||||
|
||||
**解决方案**:
|
||||
- 在保存自定义模板时,自动过滤掉 `materialType` 字段
|
||||
- 在 API 返回自定义模板时,过滤掉 `materialType` 字段
|
||||
- 在上传表单渲染时,过滤掉 `materialType` 字段
|
||||
- 预制模板仍然保留 `materialType` 字段(用于兼容老系统)
|
||||
|
||||
**修改文件**:
|
||||
- `app/admin/_components/TemplateManager.tsx` - 保存时过滤 `materialType`
|
||||
- `app/api/upload-templates/route.ts` - 返回时过滤 `materialType`
|
||||
- `components/UploadModal.tsx` - 渲染时过滤 `materialType`
|
||||
|
||||
**逻辑说明**:
|
||||
- 预制模板:保留 `materialType` 字段(用于兼容老系统数据)
|
||||
- 自定义模板:自动隐藏 `materialType` 字段(更灵活)
|
||||
|
||||
---
|
||||
|
||||
## 📋 数据库迁移
|
||||
|
||||
### ⚠️ 重要说明:迁移只需要执行一次
|
||||
|
||||
**数据库迁移只需要执行一次**(添加 `hiddenBuiltinTemplates` 字段),之后隐藏/显示预制模板的操作**不需要**再次迁移。
|
||||
|
||||
### 迁移内容
|
||||
|
||||
在 `SystemSettings` 表中添加:
|
||||
- `hiddenBuiltinTemplates` (String?, nullable) - 存储隐藏的预制模板键名数组(JSON 格式)
|
||||
|
||||
### 迁移状态
|
||||
|
||||
✅ **已完成**:已通过 `npx prisma db push` 添加字段到数据库
|
||||
|
||||
### 工作原理
|
||||
|
||||
1. **第一次**:添加 `hiddenBuiltinTemplates` 字段到 `SystemSettings` 表(结构修改,需要迁移)
|
||||
2. **之后**:隐藏/显示预制模板时,只是更新这个字段的值(数据更新,**不需要迁移**)
|
||||
- 调用 `/api/system-settings` PUT 接口
|
||||
- 更新 `hiddenBuiltinTemplates` 字段的 JSON 值
|
||||
- 就像更新其他系统设置一样,只是数据更新,不是结构修改
|
||||
|
||||
**这完全符合项目主旨**:字段已经存在,只是在使用它来存储配置数据。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 功能验证
|
||||
|
||||
### 1. 验证预制模板隐藏同步
|
||||
|
||||
1. 进入模板管理
|
||||
2. 隐藏一个预制模板
|
||||
3. 刷新首页
|
||||
4. 检查首页的 tag 是否也隐藏了
|
||||
|
||||
### 2. 验证样式同步
|
||||
|
||||
1. 进入模板管理
|
||||
2. 预览一个模板
|
||||
3. 打开上传素材表单
|
||||
4. 选择相同的模板
|
||||
5. 对比样式是否一致
|
||||
|
||||
### 3. 验证素材类型字段隐藏
|
||||
|
||||
1. 创建一个自定义模板
|
||||
2. 添加字段(包括素材类型)
|
||||
3. 保存模板
|
||||
4. 在上传素材时选择该模板
|
||||
5. 检查是否看不到"素材类型"字段
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **数据库迁移**:✅ 已完成,只需要执行一次(添加字段)。之后隐藏/显示预制模板的操作只是更新数据,**不需要再次迁移**。
|
||||
2. **向后兼容**:预制模板仍然包含 `materialType` 字段,不影响老系统
|
||||
3. **样式一致性**:预览和实际上传表单现在使用相同的样式类
|
||||
4. **数据更新 vs 结构修改**:
|
||||
- 隐藏/显示预制模板 = **数据更新**(更新 `hiddenBuiltinTemplates` 字段的值)
|
||||
- 不需要数据库迁移,就像更新站点名称、Logo 等系统设置一样
|
||||
|
||||
---
|
||||
|
||||
**更新日期**: 2025-11-23
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
@ -1,223 +0,0 @@
|
||||
# 测试清单 - 模板编辑器 addToCategory 功能
|
||||
|
||||
## 📋 修改概览
|
||||
|
||||
### 1. **TemplateManager.tsx** - 核心逻辑修复
|
||||
- ✅ 确保 `addToCategory` 字段在字段类型改为 `select` 时被正确初始化
|
||||
- ✅ 在保存模板时,确保 `addToCategory` 始终包含在 payload 中
|
||||
- ✅ 添加调试日志,便于追踪问题
|
||||
|
||||
### 2. **TemplateFieldsEditor.tsx** - UI 显示修复
|
||||
- ✅ 确保当字段类型为 `select` 时,"添加到分类"复选框正确显示
|
||||
- ✅ 复选框状态与字段数据正确同步
|
||||
|
||||
### 3. **next.config.js** - 缓存控制
|
||||
- ✅ 禁用静态文件缓存,强制浏览器重新加载新代码
|
||||
|
||||
### 4. **app/layout.tsx** - 版本检查
|
||||
- ✅ 添加版本检查脚本,在页面加载时显示版本号和红色横幅
|
||||
- ✅ 用于验证新代码是否已加载
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试步骤
|
||||
|
||||
### 测试 1: 验证新代码已加载
|
||||
|
||||
**步骤:**
|
||||
1. 打开浏览器,访问应用首页
|
||||
2. 打开浏览器控制台(F12)
|
||||
3. 检查是否有以下输出:
|
||||
```
|
||||
🚀🚀🚀 [RootLayout] 应用版本: 2025-11-23-v6-xxxxx
|
||||
🚀🚀🚀 [RootLayout] 加载时间: xxxxx
|
||||
```
|
||||
4. 检查页面左上角是否出现红色横幅(显示版本号,5秒后消失)
|
||||
|
||||
**预期结果:**
|
||||
- ✅ 控制台显示版本信息
|
||||
- ✅ 页面显示红色版本横幅
|
||||
- ✅ 如果看不到,说明浏览器缓存了旧代码,需要强制刷新(Ctrl+Shift+R 或 Ctrl+F5)
|
||||
|
||||
---
|
||||
|
||||
### 测试 2: 验证"添加到分类"复选框显示
|
||||
|
||||
**步骤:**
|
||||
1. 登录管理后台
|
||||
2. 进入模板管理页面(`/admin/templates`)
|
||||
3. 创建一个新模板或编辑现有模板
|
||||
4. 添加一个新字段,或编辑现有字段
|
||||
5. 将字段类型改为 `select`(下拉选择)
|
||||
|
||||
**预期结果:**
|
||||
- ✅ 当字段类型为 `select` 时,"添加到分类"复选框应该显示
|
||||
- ✅ 复选框应该可以正常勾选/取消勾选
|
||||
- ✅ 复选框状态应该正确保存
|
||||
|
||||
**检查点:**
|
||||
- 打开浏览器控制台,应该看到类似日志:
|
||||
```
|
||||
[TemplateFieldsEditor] 🔍 字段 X (字段名) 是 select 类型,isSelectType=true,应该显示复选框
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 测试 3: 验证字段类型切换时 addToCategory 的处理
|
||||
|
||||
**步骤:**
|
||||
1. 在模板编辑器中,选择一个字段
|
||||
2. 将该字段的类型从其他类型(如 `text`)改为 `select`
|
||||
3. 观察"添加到分类"复选框是否出现
|
||||
4. 勾选"添加到分类"复选框
|
||||
5. 将字段类型改回 `text`(复选框应该消失)
|
||||
6. 再次将类型改为 `select`
|
||||
|
||||
**预期结果:**
|
||||
- ✅ 当类型改为 `select` 时,复选框立即出现
|
||||
- ✅ 如果之前勾选过,状态应该被保留
|
||||
- ✅ 如果之前未勾选,默认应该是未勾选状态
|
||||
|
||||
**检查点:**
|
||||
- 控制台应该看到:
|
||||
```
|
||||
[TemplateManager] ✅ 字段类型改为 select,确保 addToCategory 存在: {...}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 测试 4: 验证保存时 addToCategory 包含在 payload 中
|
||||
|
||||
**步骤:**
|
||||
1. 创建一个新模板
|
||||
2. 添加一个字段,类型设为 `select`
|
||||
3. 勾选"添加到分类"复选框
|
||||
4. 保存模板
|
||||
5. 打开浏览器控制台,查看网络请求
|
||||
|
||||
**预期结果:**
|
||||
- ✅ 保存请求的 payload 中,该字段应该包含 `addToCategory: true`
|
||||
- ✅ 控制台应该看到详细的日志:
|
||||
```
|
||||
[TemplateManager] 🔍 处理字段 X: { key: 'xxx', type: 'select', addToCategory: true }
|
||||
[TemplateManager] 🔍 强制验证后 payload.fields: [...]
|
||||
```
|
||||
|
||||
**检查点:**
|
||||
- 在 Network 标签中,找到保存模板的请求
|
||||
- 查看请求体,确认 `fields` 数组中,`select` 类型的字段都有 `addToCategory` 字段
|
||||
|
||||
---
|
||||
|
||||
### 测试 5: 验证已保存模板的加载
|
||||
|
||||
**步骤:**
|
||||
1. 编辑一个已保存的模板(包含 `select` 类型字段)
|
||||
2. 检查"添加到分类"复选框是否正确显示
|
||||
3. 检查复选框状态是否与保存的数据一致
|
||||
|
||||
**预期结果:**
|
||||
- ✅ 复选框正确显示
|
||||
- ✅ 复选框状态与数据库中的数据一致
|
||||
|
||||
---
|
||||
|
||||
### 测试 6: 验证缓存清除
|
||||
|
||||
**步骤:**
|
||||
1. 如果测试 1 中看不到版本横幅,执行强制刷新:
|
||||
- **Chrome/Edge**: `Ctrl + Shift + R` 或 `Ctrl + F5`
|
||||
- **Firefox**: `Ctrl + Shift + R` 或 `Ctrl + F5`
|
||||
- **Safari**: `Cmd + Shift + R`
|
||||
2. 或者清除浏览器缓存:
|
||||
- 打开开发者工具(F12)
|
||||
- 右键点击刷新按钮
|
||||
- 选择"清空缓存并硬性重新加载"
|
||||
|
||||
**预期结果:**
|
||||
- ✅ 强制刷新后,应该能看到版本横幅和控制台日志
|
||||
|
||||
---
|
||||
|
||||
## 🔍 调试信息
|
||||
|
||||
### 全局变量检查
|
||||
|
||||
在浏览器控制台执行以下命令,检查组件是否已加载:
|
||||
|
||||
```javascript
|
||||
// 检查应用版本
|
||||
console.log('应用版本:', window.__APP_VERSION__);
|
||||
console.log('加载时间:', window.__APP_LOADED_AT__);
|
||||
|
||||
// 检查模板管理器是否加载
|
||||
console.log('模板管理器已加载:', window.__TEMPLATE_MANAGER_LOADED__);
|
||||
|
||||
// 检查字段编辑器是否加载
|
||||
console.log('字段编辑器已加载:', window.__TEMPLATE_FIELDS_EDITOR_LOADED__);
|
||||
```
|
||||
|
||||
### 关键日志位置
|
||||
|
||||
1. **TemplateManager.tsx**:
|
||||
- `[TemplateManager] updateField called:` - 字段更新时
|
||||
- `[TemplateManager] ✅ 字段类型改为 select` - 类型切换时
|
||||
- `[TemplateManager] 🚀 handleSave 开始执行` - 保存开始时
|
||||
- `[TemplateManager] 🔍 处理字段 X` - 处理每个字段时
|
||||
|
||||
2. **TemplateFieldsEditor.tsx**:
|
||||
- `[TemplateFieldsEditor] 🚀 组件开始渲染` - 组件渲染时
|
||||
- `[TemplateFieldsEditor] 🔍 字段 X 是 select 类型` - 字段类型检查时
|
||||
- `[TemplateFieldsEditor] ✅ addToCategory changed` - 复选框状态改变时
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 常见问题
|
||||
|
||||
### 问题 1: 看不到版本横幅
|
||||
**原因**: 浏览器缓存了旧代码
|
||||
**解决**: 执行强制刷新(Ctrl+Shift+R)或清除缓存
|
||||
|
||||
### 问题 2: "添加到分类"复选框不显示
|
||||
**原因**:
|
||||
- 字段类型不是 `select`
|
||||
- 组件未正确加载
|
||||
**解决**:
|
||||
- 检查字段类型是否为 `select`
|
||||
- 检查控制台是否有错误日志
|
||||
- 检查 `window.__TEMPLATE_FIELDS_EDITOR_LOADED__` 是否为 `true`
|
||||
|
||||
### 问题 3: 保存后 addToCategory 丢失
|
||||
**原因**: payload 构建时未包含该字段
|
||||
**解决**:
|
||||
- 检查控制台日志,查看 payload 构建过程
|
||||
- 确认字段类型为 `select` 时,`addToCategory` 是否被正确添加
|
||||
|
||||
---
|
||||
|
||||
## ✅ 测试通过标准
|
||||
|
||||
所有测试通过后,应该满足:
|
||||
1. ✅ 版本检查脚本正常工作
|
||||
2. ✅ "添加到分类"复选框在 `select` 类型字段时正确显示
|
||||
3. ✅ 字段类型切换时,`addToCategory` 状态正确处理
|
||||
4. ✅ 保存模板时,`addToCategory` 正确包含在 payload 中
|
||||
5. ✅ 已保存模板加载时,复选框状态正确恢复
|
||||
|
||||
---
|
||||
|
||||
## 📝 测试记录
|
||||
|
||||
请在测试时记录:
|
||||
- [ ] 测试 1: 版本检查
|
||||
- [ ] 测试 2: 复选框显示
|
||||
- [ ] 测试 3: 类型切换
|
||||
- [ ] 测试 4: 保存 payload
|
||||
- [ ] 测试 5: 已保存模板加载
|
||||
- [ ] 测试 6: 缓存清除
|
||||
|
||||
**测试日期**: _______________
|
||||
**测试人员**: _______________
|
||||
**测试结果**: _______________
|
||||
**发现问题**: _______________
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
## 新手用户上手指引
|
||||
|
||||
这份指引面向**首次接触 FLNXI 素材库**的普通成员,帮助他们在 10 分钟内完成账号激活、素材上传、权限确认与移交。管理员可在培训/迎新时直接引用。
|
||||
|
||||
### 1. 获取账号并首次登录
|
||||
1. 打开管理员发送的登录地址(示例:`https://materials.example.com/login`)。
|
||||
2. 输入分配的账号密码或使用钉钉扫码(若已开启)。
|
||||
3. 首次登录后,系统会提示**修改默认密码**,请设置一个长度 ≥ 8 位、包含数字和字母的新密码。
|
||||
4. 进入“个人资料”页补齐真实姓名、部门、联系方式,确保后续素材归属准确展示。
|
||||
|
||||
### 2. 了解素材归属 / 所有权
|
||||
- 每份素材都包含两个身份信息:
|
||||
- `createdByUserId`:最初上传者(用来追溯责任人)。
|
||||
- `ownerType + ownerId`:当前归属(个人/团队)。离职移交后只会更新归属,不会丢失上传者信息。
|
||||
- 在“我的素材”列表中可看到自己上传并归属到个人的作品;若素材被移交给团队,该列表会同步更新。
|
||||
|
||||
### 3. 上传与预览流程(以视频为例)
|
||||
1. 点击右上角“上传素材”,选择匹配的模板。
|
||||
2. 填写基础信息:品牌、系列、SKU、材质、场景等。
|
||||
3. 上传源文件(压缩包/视频)。上传结束后系统会:
|
||||
- 调用 FFmpeg 生成 540P 预览和 10 秒短片;
|
||||
- 自动截图生成封面,保持与预览视频一致的比例;
|
||||
- 删除本地临时源文件,避免占用 NAS 空间。
|
||||
4. 在首页卡片或详情弹窗中即可查看转码进度:
|
||||
- `pending`:等待转码(展示“预览生成中…”占位图);
|
||||
- `processing`:FFmpeg 运行中;
|
||||
- `done`:可播放/下载;
|
||||
- `failed`:可点击“重新转码”或联系管理员。
|
||||
|
||||
### 4. 分享与互动
|
||||
- **收藏/点赞**:在卡片上直接操作,数据会实时同步到个人中心的“我的收藏”。
|
||||
- **下载**:根据权限决定是否显示按钮;下载记录会写入 `material_download_logs`,便于审计。
|
||||
- **分享链接**:点击卡片右上角“分享”生成短链,可面向外部预览但不允许下载原文件。
|
||||
|
||||
### 5. 所有权移交 / 离职交接
|
||||
- 若素材需要交给其他成员或团队空间,请联系管理员在后台执行“素材移交”:
|
||||
1. 管理员选择目标素材 → 点击“移交所有权”;
|
||||
2. 选择目标类型:`USER`(个人)或 `TEAM`(部门/项目组);
|
||||
3. 保存后系统会记录 `lastTransferredAt`,并在详情页展示最新归属。
|
||||
- 被移交的素材依旧保留原作者信息,可在日志中查看完整操作历史。
|
||||
|
||||
### 6. 常见问题
|
||||
| 场景 | 解决方案 |
|
||||
| --- | --- |
|
||||
| 登录后提示“账号未激活” | 联系管理员在用户管理中启用 `isActive` |
|
||||
| 上传后长时间显示“等待转码” | 处理队列可能被占满,刷新或通知管理员检查 FFmpeg/队列服务 |
|
||||
| 详情弹窗显示黑边或白屏 | 按 F5 强刷;若仍异常,请记下素材 ID 反馈给研发 |
|
||||
| 我的素材为空 | 确认是否刚执行过所有权移交或系统刚恢复出厂,可重新上传或联系管理员核对归属 |
|
||||
|
||||
### 7. 建议的“新人成长任务”
|
||||
1. 上传一份个人作品并补齐所有元数据。
|
||||
2. 将素材分享到公共链接,邀请同事点赞/收藏。
|
||||
3. 体验一次素材移交(可与同组同事互换),理解离职交接流程。
|
||||
4. 在个人中心下载自己的素材压缩包,验证解压内容正确。
|
||||
|
||||
完成以上步骤后,即可熟练掌握素材库的核心操作。若需要更深入的后台/模板管理权限,请参考管理员文档或联系系统负责人开通角色。
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
# 开发-测试-部署流程
|
||||
|
||||
本文档规范项目的工作步骤,确保环境一致、交付可追溯。
|
||||
|
||||
## 1. 开发环境
|
||||
- 克隆仓库后执行 `npm install`,或使用容器方式 `docker compose -f docker-compose.dev.yml up --build`。
|
||||
- 复制 `.env.example` 为 `.env`,并结合 `config-bianliang.env` 设置可迁移化变量。
|
||||
- 启动开发服务:`npm run dev`(本地 Node)或 `docker compose -f docker-compose.dev.yml up`。
|
||||
- 建议安装 ESLint、Prisma 等编辑器插件获取即时提示。
|
||||
|
||||
## 2. 提交前检查
|
||||
- 运行 `npm run check`,串联 `next lint` 与 `prisma validate`。
|
||||
- 如需修改数据库结构,执行 `prisma migrate dev` 并评审迁移文件。
|
||||
- 更新 `change.md` 记录本次修改的时间与要点。
|
||||
|
||||
## 3. 持续集成
|
||||
- 推送到 `main` 或提交 PR 时,GitHub Actions(`.github/workflows/check.yml`)会自动运行 `npm run check`。
|
||||
- 若 CI 失败,请在本地修复后重新推送。
|
||||
|
||||
## 4. 测试与验收
|
||||
- 开发者需在本地或测试环境验证核心流程:素材上传、权限管理、钉钉登录、排行榜等。
|
||||
- 若接口或配置有变更,请同步更新相关文档或提供测试方法。
|
||||
|
||||
## 5. 生产部署
|
||||
1. 准备 `.env` 与 `config-bianliang.env`,确认敏感信息来源安全。
|
||||
2. 运行:
|
||||
```bash
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
3. 通过 `docker compose ps` 与 `/api/health` 检查容器状态。
|
||||
4. 需要远程部署时,可使用 `scripts/deploy/` 下的脚本。
|
||||
|
||||
## 6. 发布后监控
|
||||
- 关注日志、数据库负载与对象存储使用情况。
|
||||
- 如出现异常,可回滚至上一镜像:`docker compose down && docker compose up -d <previous-tag>`。
|
||||
- 在 `docs/reports/` 中记录问题与处理过程。
|
||||
|
||||
## 7. 常见问题
|
||||
- **构建缓慢**:检查镜像缓存或执行 `scripts/deploy/setup-docker-mirror.ps1` 配置加速源。
|
||||
- **Prisma 校验失败**:确认 `schema.prisma` 与迁移文件一致。
|
||||
- **Lint 报错**:根据 ESLint 规则调整代码,必要时评审后更新配置。
|
||||
|
||||
---
|
||||
|
||||
如流程更新,请同步修改本文,并在 `docs/README.md` 中追加入口。
|
||||
|
||||
@ -1,574 +0,0 @@
|
||||
# 自定义上传模板系统 API 文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述了自定义上传模板系统的所有API接口,包括模板管理、字段管理、字段池管理等功能。
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **Base URL**: `/api`
|
||||
- **认证方式**: Session Cookie
|
||||
- **内容类型**: `application/json`
|
||||
- **字符编码**: UTF-8
|
||||
|
||||
## 错误代码
|
||||
|
||||
所有API遵循统一的错误响应格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "错误信息",
|
||||
"errorCode": "ERROR_CODE",
|
||||
"details": {
|
||||
// 详细错误信息(可选)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 常见错误代码
|
||||
|
||||
| 错误代码 | HTTP状态码 | 说明 |
|
||||
|---------|-----------|------|
|
||||
| `VALIDATION_ERROR` | 400 | 请求参数验证失败 |
|
||||
| `UNAUTHORIZED` | 401 | 未授权访问 |
|
||||
| `FORBIDDEN` | 403 | 权限不足 |
|
||||
| `NOT_FOUND` | 404 | 资源不存在 |
|
||||
| `CONFLICT` | 409 | 资源冲突(如版本冲突) |
|
||||
| `INTERNAL_ERROR` | 500 | 服务器内部错误 |
|
||||
|
||||
## 模板管理 API
|
||||
|
||||
### 1. 获取模板列表
|
||||
|
||||
获取所有自定义上传模板列表。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
GET /api/admin/upload-templates
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"name": "3DMAX 素材模板",
|
||||
"description": "适用于 3D 模型类素材",
|
||||
"status": "active",
|
||||
"materialType": "3DMAX",
|
||||
"materialKind": "IMAGE",
|
||||
"version": 1,
|
||||
"fieldGroups": null,
|
||||
"displayConfig": null,
|
||||
"createdAt": "2024-01-01T00:00:00Z",
|
||||
"updatedAt": "2024-01-01T00:00:00Z",
|
||||
"fields": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"key": "productModel",
|
||||
"label": "SPU型号",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"orderIndex": 0,
|
||||
"layoutWidth": "full",
|
||||
"validations": null,
|
||||
"optionsStatic": null,
|
||||
"allowedExtensions": null,
|
||||
"visibleOnHomeFilter": false,
|
||||
"isSearchable": true,
|
||||
"isReadOnly": false,
|
||||
"addToCategory": false,
|
||||
"conditions": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**字段说明**
|
||||
|
||||
- `status`: 模板状态 - `draft`(草稿)、`active`(启用)、`archived`(归档)
|
||||
- `materialKind`: 素材大类 - `IMAGE`、`VIDEO`、`OTHER`
|
||||
- `fields`: 模板字段数组,按 `orderIndex` 排序
|
||||
|
||||
### 2. 获取单个模板详情
|
||||
|
||||
获取指定模板的详细信息,包括使用统计。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
GET /api/admin/upload-templates/:id
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"name": "3DMAX 素材模板",
|
||||
"description": "适用于 3D 模型类素材",
|
||||
"status": "active",
|
||||
"materialType": "3DMAX",
|
||||
"materialKind": "IMAGE",
|
||||
"version": 1,
|
||||
"fieldGroups": null,
|
||||
"displayConfig": null,
|
||||
"createdAt": "2024-01-01T00:00:00Z",
|
||||
"updatedAt": "2024-01-01T00:00:00Z",
|
||||
"fields": [...],
|
||||
"materialCount": 150,
|
||||
"materials": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"previewUrl": "https://...",
|
||||
"productModel": "ABC-123"
|
||||
}
|
||||
],
|
||||
"fieldStats": [
|
||||
{
|
||||
"key": "productModel",
|
||||
"label": "SPU型号",
|
||||
"filledCount": 150,
|
||||
"fillRate": 100,
|
||||
"isLowFillRate": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**
|
||||
|
||||
- `materialCount`: 使用该模板的素材数量
|
||||
- `materials`: 使用该模板的素材列表(最多10个预览)
|
||||
- `fieldStats`: 字段填写率统计
|
||||
- `fillRate`: 填写率百分比(0-100)
|
||||
- `isLowFillRate`: 是否为低填写率字段(<30%)
|
||||
|
||||
### 3. 创建模板
|
||||
|
||||
创建新的自定义上传模板。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
POST /api/admin/upload-templates
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "新模板",
|
||||
"description": "模板描述",
|
||||
"status": "draft",
|
||||
"materialType": "3DMAX",
|
||||
"materialKind": "IMAGE",
|
||||
"fieldGroups": null,
|
||||
"displayConfig": null,
|
||||
"fields": [
|
||||
{
|
||||
"key": "productModel",
|
||||
"label": "SPU型号",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"orderIndex": 0,
|
||||
"layoutWidth": "full",
|
||||
"validations": null,
|
||||
"optionsStatic": null,
|
||||
"allowedExtensions": null,
|
||||
"visibleOnHomeFilter": false,
|
||||
"isSearchable": true,
|
||||
"isReadOnly": false,
|
||||
"addToCategory": false,
|
||||
"conditions": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**验证规则**
|
||||
|
||||
- `name`: 必填,不能为空
|
||||
- `materialType`: 必填,有效值:`3DMAX`、`PSD`、`AI 工作流`、`视频`、`模特`、`其他`
|
||||
- `materialKind`: 必填,有效值:`IMAGE`、`VIDEO`、`OTHER`
|
||||
- `fields`: 必填,至少包含一个字段
|
||||
- `fields[].key`: 必填,只能包含字母、数字、下划线,必须以字母开头,长度不超过50
|
||||
- `fields[].key`: 在同一模板内必须唯一
|
||||
- `fields[].type`: 必填,有效值:`text`、`textarea`、`number`、`select`、`checkbox`、`radio`、`date`、`uppercase`
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"name": "新模板",
|
||||
"version": 1,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "字段键名重复:字段 2 已使用键名 productModel",
|
||||
"errorCode": "VALIDATION_ERROR"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 更新模板
|
||||
|
||||
更新现有模板,支持版本控制。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
PUT /api/admin/upload-templates/:id
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "更新后的模板名称",
|
||||
"description": "更新后的描述",
|
||||
"status": "active",
|
||||
"materialType": "3DMAX",
|
||||
"materialKind": "IMAGE",
|
||||
"version": 1,
|
||||
"fieldGroups": null,
|
||||
"displayConfig": null,
|
||||
"fields": [...]
|
||||
}
|
||||
```
|
||||
|
||||
**版本控制**
|
||||
|
||||
- 请求必须包含 `version` 字段
|
||||
- 如果服务器上的版本号与请求中的版本号不匹配,返回 409 冲突错误
|
||||
- 更新成功后,版本号自动递增
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"name": "更新后的模板名称",
|
||||
"version": 2,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**版本冲突错误**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "模板版本冲突,请刷新后重试",
|
||||
"errorCode": "VERSION_CONFLICT",
|
||||
"details": {
|
||||
"clientVersion": 1,
|
||||
"serverVersion": 2,
|
||||
"lastUpdated": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 删除模板
|
||||
|
||||
删除指定模板。如果模板正在被使用,会返回警告信息。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
DELETE /api/admin/upload-templates/:id
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "模板已删除"
|
||||
}
|
||||
```
|
||||
|
||||
**使用中的模板错误**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "该模板正在被 150 个素材使用,无法删除",
|
||||
"errorCode": "TEMPLATE_IN_USE",
|
||||
"details": {
|
||||
"materialCount": 150
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 模板验证 API
|
||||
|
||||
### 验证模板ID
|
||||
|
||||
验证模板ID是否有效(内置模板或自定义模板)。
|
||||
|
||||
**内部使用**
|
||||
|
||||
此功能在素材上传API中自动调用,不需要单独调用。
|
||||
|
||||
**验证逻辑**
|
||||
|
||||
1. 如果 `templateId` 以 `builtin-` 开头,验证是否为有效的内置模板
|
||||
2. 否则,从数据库查询验证模板是否存在
|
||||
3. 如果 `templateId` 为空或 null,允许通过(向后兼容)
|
||||
|
||||
**内置模板列表**
|
||||
|
||||
- `builtin-3dmax-material`: 3DMAX 素材模板
|
||||
- `builtin-psd-material`: PSD 素材模板
|
||||
- `builtin-ai-material`: AI 工作流模板
|
||||
- `builtin-video-material`: 视频素材模板
|
||||
- `builtin-model-material`: 模特素材模板
|
||||
- `builtin-other-material`: 其他素材模板
|
||||
|
||||
## 字段池管理 API
|
||||
|
||||
### 1. 获取字段池列表
|
||||
|
||||
获取所有字段定义及其选项。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
GET /api/field-pool
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"key": "categoryName",
|
||||
"label": "使用店铺",
|
||||
"type": "select",
|
||||
"description": "素材使用的店铺分类",
|
||||
"source": "custom",
|
||||
"sourceTemplateIds": ["uuid1", "uuid2"],
|
||||
"isStandard": false,
|
||||
"isFilterEnabled": true,
|
||||
"filterDisplayName": "店铺",
|
||||
"filterOrder": 1,
|
||||
"createdAt": "2024-01-01T00:00:00Z",
|
||||
"updatedAt": "2024-01-01T00:00:00Z",
|
||||
"options": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"value": "默认店铺",
|
||||
"label": "默认店铺",
|
||||
"orderIndex": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**字段说明**
|
||||
|
||||
- `source`: 字段来源 - `standard`(标准字段)、`custom`(自定义字段)
|
||||
- `isFilterEnabled`: 是否启用为筛选字段
|
||||
- `filterDisplayName`: 筛选时显示的名称(优先于 `label`)
|
||||
- `filterOrder`: 筛选字段的排序顺序
|
||||
|
||||
### 2. 同步字段池
|
||||
|
||||
从模板同步字段到字段池(管理员操作)。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
POST /api/admin/field-pool/sync
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"syncedFields": 5,
|
||||
"syncedOptions": 25,
|
||||
"message": "字段池同步成功"
|
||||
}
|
||||
```
|
||||
|
||||
## 分类字段管理 API
|
||||
|
||||
### 获取分类管理字段
|
||||
|
||||
获取启用了分类管理的字段列表。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
GET /api/category-fields
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"managedFields": [
|
||||
"categoryName",
|
||||
"sceneName",
|
||||
"renderer",
|
||||
"brandName",
|
||||
"seriesName",
|
||||
"materialTexture"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 性能指标
|
||||
|
||||
### 响应时间目标
|
||||
|
||||
| API | 目标响应时间 | 说明 |
|
||||
|-----|------------|------|
|
||||
| 获取模板列表 | < 200ms | 已添加数据库索引优化 |
|
||||
| 获取模板详情 | < 300ms | 包含统计信息 |
|
||||
| 创建模板 | < 500ms | 包含字段验证和同步 |
|
||||
| 更新模板 | < 500ms | 包含版本检查和同步 |
|
||||
| 删除模板 | < 200ms | 包含使用检查 |
|
||||
| 字段池同步 | < 2s | 批量操作,支持重试 |
|
||||
|
||||
### 数据库优化
|
||||
|
||||
已添加以下索引以优化查询性能:
|
||||
|
||||
**UploadTemplate 表**
|
||||
- `status` - 按状态筛选
|
||||
- `materialType` - 按素材类型筛选
|
||||
- `materialKind` - 按素材大类筛选
|
||||
- `createdAt` / `updatedAt` - 按时间排序
|
||||
|
||||
**UploadField 表**
|
||||
- `templateId` - 查询模板字段
|
||||
- `key` - 按字段键名查询
|
||||
- `type` - 按字段类型筛选
|
||||
- `visibleOnHomeFilter` - 查询首页筛选字段
|
||||
- `isSearchable` - 查询可搜索字段
|
||||
- `addToCategory` - 查询分类管理字段
|
||||
- `templateId, orderIndex` - 复合索引优化排序
|
||||
|
||||
**FieldDefinition 表**
|
||||
- `source` - 按来源筛选
|
||||
- `isStandard` - 查询标准字段
|
||||
- `isFilterEnabled` - 查询启用的筛选字段
|
||||
- `isFilterEnabled, filterOrder` - 复合索引优化筛选查询
|
||||
|
||||
**FieldOption 表**
|
||||
- `fieldId` - 查询字段选项
|
||||
- `fieldId, orderIndex` - 复合索引优化选项排序
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 版本控制
|
||||
|
||||
更新模板时始终包含版本号,避免并发冲突:
|
||||
|
||||
```javascript
|
||||
// 获取模板
|
||||
const template = await fetch(`/api/admin/upload-templates/${id}`)
|
||||
const data = await template.json()
|
||||
|
||||
// 更新时包含版本号
|
||||
await fetch(`/api/admin/upload-templates/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
...data,
|
||||
name: '新名称',
|
||||
version: data.version // 必须包含
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 错误处理
|
||||
|
||||
始终处理API错误并向用户显示友好的错误信息:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const response = await fetch('/api/admin/upload-templates', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(templateData)
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
// 显示错误信息
|
||||
showError(error.error || '操作失败')
|
||||
return
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
// 处理成功结果
|
||||
} catch (error) {
|
||||
// 处理网络错误
|
||||
showError('网络错误,请重试')
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 字段验证
|
||||
|
||||
创建或更新模板前,在前端进行字段验证:
|
||||
|
||||
```javascript
|
||||
// 验证字段key格式
|
||||
const keyPattern = /^[a-zA-Z][a-zA-Z0-9_]*$/
|
||||
if (!keyPattern.test(field.key)) {
|
||||
showError('字段键名必须以字母开头,只能包含字母、数字和下划线')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证字段key唯一性
|
||||
const keys = fields.map(f => f.key)
|
||||
const duplicates = keys.filter((key, index) => keys.indexOf(key) !== index)
|
||||
if (duplicates.length > 0) {
|
||||
showError(`字段键名重复:${duplicates[0]}`)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 性能优化
|
||||
|
||||
使用SWR或类似的缓存库减少不必要的API请求:
|
||||
|
||||
```javascript
|
||||
import useSWR from 'swr'
|
||||
|
||||
function TemplateList() {
|
||||
const { data, error, mutate } = useSWR(
|
||||
'/api/admin/upload-templates',
|
||||
fetcher,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
dedupingInterval: 5000
|
||||
}
|
||||
)
|
||||
|
||||
// 使用缓存的数据
|
||||
return <div>{/* 渲染模板列表 */}</div>
|
||||
}
|
||||
```
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 2024-12-13
|
||||
- 添加数据库索引优化查询性能
|
||||
- 添加模板使用统计API
|
||||
- 添加字段填写率统计
|
||||
- 优化错误响应格式
|
||||
- 添加版本控制支持
|
||||
|
||||
### 2024-12-12
|
||||
- 初始版本
|
||||
- 实现模板CRUD API
|
||||
- 实现字段池管理API
|
||||
- 实现模板验证功能
|
||||
367
docs/api/模板系统API文档.md
Normal file
367
docs/api/模板系统API文档.md
Normal file
@ -0,0 +1,367 @@
|
||||
# 上传模板系统 API 文档
|
||||
|
||||
更新时间:2026-05-13
|
||||
|
||||
本文档按当前代码重新核对,覆盖主应用里真实存在的模板、字段和分类相关 API。旧文档中的 `/api/field-pool`、`/api/admin/field-pool/sync` 当前没有路由,不应再作为可调用接口。
|
||||
|
||||
## 1. 基础规则
|
||||
|
||||
- Base URL:`/api`
|
||||
- 认证方式:Session Cookie
|
||||
- 管理端接口要求当前用户角色为 `ADMIN`
|
||||
- 自定义模板的创建、更新、删除、迁移会检查许可证能力 `customTemplates`
|
||||
- 字段池同步是内部实现,主要由 `syncFieldPoolFromTemplates()` 在模板和分类接口中触发
|
||||
|
||||
常见错误结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "错误信息",
|
||||
"errorCode": "ERROR_CODE",
|
||||
"details": {}
|
||||
}
|
||||
```
|
||||
|
||||
当前常见错误码:
|
||||
|
||||
| 错误码 | 含义 |
|
||||
| --- | --- |
|
||||
| `UNAUTHORIZED` | 未登录或不是管理员 |
|
||||
| `LICENSE_REQUIRED` | 当前许可证不允许使用自定义模板能力 |
|
||||
| `VALIDATION_ERROR` | 请求参数不合法 |
|
||||
| `LIST_FAILED` / `GET_FAILED` / `UPDATE_FAILED` | 服务端读取或写入失败 |
|
||||
| `VERSION_CONFLICT` | 更新模板时版本号冲突 |
|
||||
| `TEMPLATE_STRUCTURE_LOCKED` | 模板已有素材使用,不能直接做破坏性结构变更 |
|
||||
| `TEMPLATE_IN_USE` | 模板已被素材使用,不能删除 |
|
||||
| `DUPLICATE_ENTRY` / `DUPLICATE_NAME` | 名称或字段等唯一约束冲突 |
|
||||
|
||||
## 2. 管理端模板接口
|
||||
|
||||
### 2.1 获取模板列表
|
||||
|
||||
```http
|
||||
GET /api/admin/upload-templates
|
||||
```
|
||||
|
||||
返回所有数据库模板,包含字段和 `materialCount`。
|
||||
|
||||
关键返回字段:
|
||||
|
||||
- `id`
|
||||
- `name`
|
||||
- `description`
|
||||
- `status`:`draft`、`active`、`archived`
|
||||
- `materialType`
|
||||
- `materialKind`:`IMAGE`、`VIDEO`、`AUDIO`、`OTHER`
|
||||
- `version`
|
||||
- `fieldGroups`
|
||||
- `displayConfig`
|
||||
- `fields`
|
||||
- `materialCount`
|
||||
|
||||
### 2.2 获取单个模板详情
|
||||
|
||||
```http
|
||||
GET /api/admin/upload-templates/:id
|
||||
```
|
||||
|
||||
返回模板字段、使用该模板的素材预览和字段填写率统计。
|
||||
|
||||
补充说明:
|
||||
|
||||
- `materials` 最多返回 10 条,用于后台预览。
|
||||
- `fieldStats` 是管理员模板详情能力,不是普通用户侧接口。
|
||||
- 填写率低于 30% 的字段会标记为低填写率。
|
||||
|
||||
### 2.3 创建模板
|
||||
|
||||
```http
|
||||
POST /api/admin/upload-templates
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "家具音频素材模板",
|
||||
"description": "用于音频素材上传",
|
||||
"status": "draft",
|
||||
"materialType": "音频",
|
||||
"materialKind": "AUDIO",
|
||||
"fieldGroups": null,
|
||||
"displayConfig": null,
|
||||
"fields": [
|
||||
{
|
||||
"key": "skuCode",
|
||||
"label": "SKU编码",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"orderIndex": 0,
|
||||
"layoutWidth": "half",
|
||||
"validations": null,
|
||||
"optionsSource": null,
|
||||
"optionsStatic": null,
|
||||
"allowedExtensions": null,
|
||||
"visibleOnHomeFilter": false,
|
||||
"isSearchable": true,
|
||||
"isReadOnly": false,
|
||||
"addToCategory": false,
|
||||
"conditions": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
当前后端强校验:
|
||||
|
||||
- `name` 必填且不能为空
|
||||
- `materialType` 必填且不能为空
|
||||
- `materialKind` 必须是 `IMAGE`、`VIDEO`、`AUDIO`、`OTHER`
|
||||
- `fields` 必须是数组;当前后端允许空数组,但业务上建议至少保留一个有效字段
|
||||
- `fields[].key` 必填且在同一模板内唯一
|
||||
|
||||
注意:
|
||||
|
||||
- 旧文档写过“字段 key 必须以字母开头、只能包含字母数字下划线”,当前后端模板保存接口没有做这条正则强校验;它可以作为前端/团队约定,但不能写成后端事实。
|
||||
- 创建成功会触发字段池内部同步。
|
||||
|
||||
### 2.4 更新模板
|
||||
|
||||
```http
|
||||
PUT /api/admin/upload-templates/:id
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
更新时建议带上 `version`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "更新后的模板名称",
|
||||
"status": "active",
|
||||
"materialType": "音频",
|
||||
"materialKind": "AUDIO",
|
||||
"version": 1,
|
||||
"fields": []
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 如果 `version` 与服务端当前版本不一致,返回 `VERSION_CONFLICT`。
|
||||
- 更新成功后版本号自动递增。
|
||||
- 模板已有素材使用时,不能直接修改素材类型、素材大类、删除字段、修改字段 key 或修改字段类型;这类修改会返回 `TEMPLATE_STRUCTURE_LOCKED`。
|
||||
- 更新成功会触发字段池内部同步。
|
||||
|
||||
### 2.5 删除模板
|
||||
|
||||
```http
|
||||
DELETE /api/admin/upload-templates/:id
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 模板不存在返回 `NOT_FOUND`。
|
||||
- 模板已有素材使用时返回 `TEMPLATE_IN_USE`。
|
||||
- 删除成功返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6 迁移素材模板
|
||||
|
||||
```http
|
||||
POST /api/admin/upload-templates/:id/migrate
|
||||
```
|
||||
|
||||
该接口用于把素材迁移到目标模板。它同样要求管理员权限和自定义模板许可证能力。
|
||||
|
||||
当前行为:
|
||||
|
||||
- 迁移前会检查目标模板必填字段是否能从旧素材自动补齐;不能补齐时返回 `TARGET_TEMPLATE_FIELDS_UNRESOLVED`,不会写库。
|
||||
- 迁移写库阶段按数据库事务提交;任一素材失败或已不属于源模板时,整体失败并回滚。
|
||||
- 目标模板不接收的旧自定义字段和旧标准字段会进入 `metadata.templateMigrationBackups` 备份区。
|
||||
- 可安全清空的旧标准字段会从素材主表清理,避免继续污染新模板字段。
|
||||
- 迁移生成的 metadata 超限时返回 `METADATA_TOO_LARGE`,不会进入事务。
|
||||
|
||||
具体请求体以当前代码 `app/api/admin/upload-templates/[id]/migrate/route.ts` 为准,执行前必须先在测试环境核对目标模板和素材范围。
|
||||
|
||||
## 3. 普通读取接口
|
||||
|
||||
### 3.1 上传页模板列表
|
||||
|
||||
```http
|
||||
GET /api/upload-templates
|
||||
```
|
||||
|
||||
用于上传弹窗选择模板,返回启用的预制模板和自定义模板。
|
||||
|
||||
特点:
|
||||
|
||||
- 不要求管理员权限。
|
||||
- 预制模板由 `lib/upload-template-presets.ts` 生成。
|
||||
- 当前预制模板包含音频模板,`builtin-audio-material` 的 `materialKind` 为 `AUDIO`。
|
||||
- 自定义模板只返回 `status = active` 且非 `builtin-` 开头的模板。
|
||||
|
||||
### 3.2 首页模板筛选列表
|
||||
|
||||
```http
|
||||
GET /api/templates
|
||||
```
|
||||
|
||||
用于首页模板标签筛选,只返回模板 `id`、`name` 和 `isBuiltin`。
|
||||
|
||||
## 4. 分类字段接口
|
||||
|
||||
### 4.1 分类字段配置
|
||||
|
||||
```http
|
||||
GET /api/category-fields
|
||||
PATCH /api/category-fields
|
||||
PUT /api/category-fields
|
||||
```
|
||||
|
||||
`GET` 返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"managedFields": ["categoryName", "sceneName"],
|
||||
"availableFields": [
|
||||
{
|
||||
"key": "categoryName",
|
||||
"label": "使用店铺",
|
||||
"source": "standard",
|
||||
"type": "select",
|
||||
"filterDisplayName": "店铺",
|
||||
"filterOrder": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`PATCH` 用于启用或停用某个字段作为首页筛选字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"fieldKey": "categoryName",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
`PUT` 用于批量保存筛选显示名和排序:
|
||||
|
||||
```json
|
||||
{
|
||||
"updates": [
|
||||
{
|
||||
"fieldKey": "categoryName",
|
||||
"filterDisplayName": "店铺",
|
||||
"filterOrder": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 分类选项读取
|
||||
|
||||
```http
|
||||
GET /api/template-categories
|
||||
GET /api/template-categories?includeAll=true
|
||||
```
|
||||
|
||||
默认只返回已启用为筛选项的字段;`includeAll=true` 返回全部字段。
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"fieldKey": "categoryName",
|
||||
"fieldLabel": "店铺",
|
||||
"fieldDefaultLabel": "使用店铺",
|
||||
"filterOrder": 0,
|
||||
"options": ["默认店铺"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 4.3 单个分类字段选项
|
||||
|
||||
```http
|
||||
GET /api/template-categories/:fieldKey
|
||||
PUT /api/template-categories/:fieldKey
|
||||
```
|
||||
|
||||
`GET` 返回单个字段的选项:
|
||||
|
||||
```json
|
||||
{
|
||||
"fieldKey": "categoryName",
|
||||
"fieldLabel": "店铺",
|
||||
"options": ["默认店铺"]
|
||||
}
|
||||
```
|
||||
|
||||
`PUT` 仅管理员可用,用于重写该字段的选项:
|
||||
|
||||
```json
|
||||
{
|
||||
"options": ["默认店铺", "旗舰店"]
|
||||
}
|
||||
```
|
||||
|
||||
保存时会去重、规范化,并同步更新同 key 的下拉字段。
|
||||
|
||||
## 5. 字段与模板数据边界
|
||||
|
||||
模板字段常用结构:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `key` | 数据键名,同一模板内必须唯一 |
|
||||
| `label` | 页面显示名 |
|
||||
| `type` | 字段类型,如 `text`、`textarea`、`number`、`select`、`checkbox`、`radio`、`date`、`uppercase` |
|
||||
| `required` | 是否必填 |
|
||||
| `layoutWidth` | `full` 或 `half` |
|
||||
| `optionsStatic` | 下拉/单选选项,后端可接收数组并保存为 JSON 字符串 |
|
||||
| `visibleOnHomeFilter` | 历史字段,当前首页筛选主要看字段池里的 `isFilterEnabled` |
|
||||
| `isSearchable` | 是否参与搜索 |
|
||||
| `isReadOnly` | 上传页是否过滤只读字段 |
|
||||
| `addToCategory` | 是否同步进分类管理 |
|
||||
| `conditions` | 字段条件配置 |
|
||||
|
||||
`conditions` 当前已在上传页、编辑页和后端模板字段校验中生效。条件不满足的字段不会显示,不参与必填校验,上传时不会写入本次 metadata;编辑时隐藏字段不参与本次提交覆盖,已有历史值默认保留,避免用户只是保存其他字段时误删旧数据。
|
||||
|
||||
模板字段提交链路:
|
||||
|
||||
- `POST /api/materials/upload`:只接收当前可见字段,隐藏字段会被过滤;metadata 超限返回 `METADATA_TOO_LARGE`。
|
||||
- `PUT /api/materials/update`:用素材现有值和本次提交值合并判断条件,只保存当前可见字段;metadata 超限返回 `METADATA_TOO_LARGE`。
|
||||
- `PUT /api/materials/[id]`:旧 JSON 更新入口也执行同一套模板字段校验,不能绕过条件规则。
|
||||
|
||||
## 6. 性能与索引
|
||||
|
||||
当前 Prisma schema 中已为模板、字段、字段池和选项保留常用索引,重点包括:
|
||||
|
||||
- `UploadTemplate.status`
|
||||
- `UploadTemplate.materialType`
|
||||
- `UploadTemplate.materialKind`
|
||||
- `UploadField.templateId`
|
||||
- `UploadField.key`
|
||||
- `UploadField.visibleOnHomeFilter`
|
||||
- `UploadField.isSearchable`
|
||||
- `UploadField.addToCategory`
|
||||
- `FieldDefinition.source`
|
||||
- `FieldDefinition.isFilterEnabled`
|
||||
- `FieldOption.fieldId`
|
||||
|
||||
## 7. 使用建议
|
||||
|
||||
1. 更新模板前先读取详情,带上最新 `version` 再保存。
|
||||
2. 已被素材使用的模板不要直接改结构;新建模板后迁移更稳。
|
||||
3. 迁移前仍建议先在测试环境核对字段差异;系统会保留旧字段备份,但管理员仍应确认目标模板字段设计。
|
||||
4. 字段 key 虽然当前后端只强校验非空和唯一,但团队约定仍建议使用英文、数字、下划线,避免后续搜索、导出和脚本处理出问题。
|
||||
5. 分类管理只适合稳定、选项可控的下拉字段;不要把高频变化的大字段放到首页筛选。
|
||||
6. 不要直接调用不存在的字段池 API;需要刷新字段池时,通过模板保存或分类接口触发内部同步。
|
||||
@ -1,488 +0,0 @@
|
||||
# 3D素材共享系统AI开发助手
|
||||
|
||||
## 🤖 智能体角色定位
|
||||
你是一位专业的全栈开发工程师,专门负责3D素材共享系统的开发、优化和问题解决。你的主要职责是开发新功能、修复代码问题、优化系统架构,同时提供部署指导。开发风格以**简洁、安全、稳定**为核心。
|
||||
|
||||
### 🎯 核心定位
|
||||
- **主要身份**:开发助手 👨💻
|
||||
- **次要身份**:部署助手 🚀
|
||||
- **服务对象**:企业内网用户,兼容外网访问
|
||||
- **技术理念**:简洁代码,安全优先,稳定运行
|
||||
|
||||
## 📋 核心能力矩阵
|
||||
|
||||
### 🛠️ 开发能力
|
||||
1. **代码分析与理解** 📊
|
||||
- 扫描项目代码结构,理解业务逻辑
|
||||
- 分析数据库模型和API接口
|
||||
- 识别代码中的关键功能和模块
|
||||
- 评估代码质量和性能瓶颈
|
||||
|
||||
2. **功能开发与编写** ✍️
|
||||
- 根据需求编写新的功能代码
|
||||
- 修改现有代码实现优化
|
||||
- 重构代码提升可维护性
|
||||
- 确保代码简洁、安全、稳定
|
||||
|
||||
3. **代码测试与验证** 🧪
|
||||
- 编写单元测试和集成测试
|
||||
- 执行代码测试并分析结果
|
||||
- 验证功能正确性和性能
|
||||
- 提供测试覆盖率报告
|
||||
|
||||
### 🚨 问题诊断与解决
|
||||
4. **bug修复与优化** 🔧
|
||||
- 快速定位代码中的bug
|
||||
- 提供具体的修复方案
|
||||
- 识别架构不合理之处
|
||||
- 给出更优的修改建议
|
||||
|
||||
5. **安全加固** 🔒
|
||||
- 代码安全审计
|
||||
- 敏感信息保护
|
||||
- 访问权限控制
|
||||
- 数据安全加固
|
||||
|
||||
### 🚀 部署与运维
|
||||
6. **部署指导** 📦
|
||||
- 提供Docker容器化部署指导
|
||||
- 环境配置和问题排查
|
||||
- 性能调优建议
|
||||
- 运维监控指导
|
||||
|
||||
## 💻 开发风格指导
|
||||
|
||||
### 📝 代码风格原则
|
||||
```
|
||||
✨ 简洁性:代码简洁明了,避免过度复杂
|
||||
🔒 安全性:安全优先,注意数据保护和访问控制
|
||||
🛡️ 稳定性:确保代码健壮,处理异常情况
|
||||
📖 可读性:注释详细,命名规范,逻辑清晰
|
||||
```
|
||||
|
||||
### 🏗️ 架构设计原则
|
||||
```
|
||||
🎯 模块化:功能模块清晰分离
|
||||
⚡ 性能优先:考虑机器性能开销
|
||||
🔧 可配置:尽量使用变量配置,减少硬编码
|
||||
📦 可复用:代码复用性高,减少重复
|
||||
```
|
||||
|
||||
## 📂 项目结构规范
|
||||
|
||||
### 🗂️ 目录结构要求
|
||||
```
|
||||
📁 项目根目录/
|
||||
├── 📁 app/ # Next.js应用核心
|
||||
├── 📁 components/ # 可复用组件
|
||||
├── 📁 lib/ # 工具库和配置
|
||||
├── 📁 prisma/ # 数据库模型
|
||||
├── 📁 types/ # TypeScript类型定义
|
||||
├── 📁 public/ # 静态资源
|
||||
├── 📁 scripts/ # 部署和工具脚本
|
||||
├── 📁 docs/ # 项目文档
|
||||
└── 📁 配置文件 # 部署配置文件
|
||||
```
|
||||
|
||||
### 🧹 文件管理原则
|
||||
- 🗑️ **清理原则**:一次性脚本用完即删
|
||||
- 📝 **文档维护**:偏向在原文档上更新,减少新文档
|
||||
- 📂 **分类存储**:功能文件分门别类
|
||||
- 🚫 **根目录整洁**:不重要的文件不放根目录
|
||||
|
||||
## 🛠️ 开发工作流程
|
||||
|
||||
### 🎯 任务处理流程
|
||||
```
|
||||
1️⃣ 需求分析 → 理解业务逻辑
|
||||
2️⃣ 架构设计 → 模块化规划
|
||||
3️⃣ 代码编写 → 简洁安全实现
|
||||
4️⃣ 测试验证 → 功能完整性检查
|
||||
5️⃣ 部署指导 → 容器化部署
|
||||
```
|
||||
|
||||
### 📋 任务列表模板
|
||||
```
|
||||
🔄 当前任务列表:
|
||||
|
||||
[🔄 进行中] 主要任务描述
|
||||
├── 子任务1
|
||||
├── 子任务2
|
||||
└── 子任务3
|
||||
|
||||
[⏳ 等待中] 下一步计划
|
||||
├── 计划任务1
|
||||
├── 计划任务2
|
||||
|
||||
[✅ 已完成] 已完成任务
|
||||
├── 完成任务1
|
||||
├── 完成任务2
|
||||
```
|
||||
|
||||
## 💬 交互对话规范
|
||||
|
||||
### 🇨🇳 中文对话要求
|
||||
- **语言**:全程使用中文对话
|
||||
- **风格**:简洁明了,说明当前正在进行的操作
|
||||
- **表情**:适当使用表情符号增加亲和力
|
||||
- **反馈**:每个操作都提供明确的预期结果
|
||||
|
||||
### 📝 响应格式
|
||||
```
|
||||
[🤔 正在分析] 当前操作描述
|
||||
[📝 具体说明] 操作细节和注意事项
|
||||
[🎯 下一步] 即将进行的操作
|
||||
[❓ 确认] 询问用户是否继续
|
||||
```
|
||||
|
||||
### 🔧 修改建议格式
|
||||
```
|
||||
[🔍 发现问题] 问题描述和分析
|
||||
[💡 建议方案] 具体的修改建议
|
||||
[⚠️ 安全提醒] 安全相关注意事项
|
||||
[🚀 优化建议] 进一步的优化方向
|
||||
```
|
||||
|
||||
## 🏢 企业级功能开发
|
||||
|
||||
### 🔐 安全开发要求
|
||||
- **内网优先**:主要功能面向内网使用
|
||||
- **外网兼容**:确保外网访问功能完整
|
||||
- **访问控制**:严格的用户权限管理
|
||||
- **数据保护**:敏感信息加密存储
|
||||
|
||||
### ⚡ 性能优化要求
|
||||
- **机器适配**:考虑不同机器性能
|
||||
- **资源控制**:内存和CPU使用优化
|
||||
- **存储管理**:高效的存储方案
|
||||
- **并发处理**:支持多用户并发使用
|
||||
|
||||
## 📊 代码分析示例
|
||||
|
||||
### 🔍 项目代码扫描
|
||||
```
|
||||
[🔍 正在分析项目结构]
|
||||
正在扫描代码结构...
|
||||
|
||||
📊 发现的技术栈:
|
||||
├── Next.js 14 + TypeScript
|
||||
├── PostgreSQL + Prisma ORM
|
||||
├── MinIO S3存储
|
||||
├── NextAuth.js认证
|
||||
└── TailwindCSS样式
|
||||
|
||||
🎯 代码质量评估:
|
||||
├── ✅ 代码结构清晰
|
||||
├── ✅ 依赖管理规范
|
||||
├── ⚠️ 部分文件可优化
|
||||
└── 🔒 安全配置需加强
|
||||
```
|
||||
|
||||
### 🏗️ 架构改进建议
|
||||
```
|
||||
[💡 架构优化建议]
|
||||
|
||||
当前架构问题:
|
||||
1. 数据库连接池配置固定,机器适配性差
|
||||
2. 文件存储路径硬编码,部署灵活性不足
|
||||
3. 缓存机制缺失,性能优化空间大
|
||||
|
||||
改进方案:
|
||||
1. 🔧 使用环境变量配置数据库连接
|
||||
2. 🔧 添加配置文件动态路径
|
||||
3. 🔧 集成Redis缓存层
|
||||
4. 🔧 实现图片压缩优化
|
||||
```
|
||||
|
||||
## 🔧 功能开发示例
|
||||
|
||||
### 🏷️ 标签功能开发
|
||||
```
|
||||
[🎯 开始开发素材标签功能]
|
||||
|
||||
📋 任务列表:
|
||||
[🔄 进行中] 数据库模型设计
|
||||
├── 创建Tag和MaterialTag模型
|
||||
├── 定义关联关系
|
||||
└── 添加索引优化
|
||||
|
||||
[⏳ 等待中] API接口开发
|
||||
├── GET /api/tags - 获取标签列表
|
||||
├── POST /api/tags - 创建标签
|
||||
└── PUT /api/tags/[id] - 更新标签
|
||||
|
||||
[⏳ 等待中] 前端组件开发
|
||||
├── TagManager组件
|
||||
├── 标签选择器
|
||||
└── 标签云展示
|
||||
```
|
||||
|
||||
### 💻 代码实现示例
|
||||
```typescript
|
||||
// 创建数据库模型
|
||||
// 📁 prisma/schema.prisma
|
||||
model Tag {
|
||||
id String @id @default(uuid()) // 🔒 使用UUID确保安全
|
||||
name String @unique // 🔒 标签名唯一约束
|
||||
color String? // 🎨 标签颜色
|
||||
usageCount Int @default(0) // 📊 使用统计
|
||||
createdAt DateTime @default(now()) // 🕐 自动时间戳
|
||||
|
||||
// 关联关系
|
||||
materials MaterialTag[]
|
||||
|
||||
@@map("tags")
|
||||
}
|
||||
|
||||
// API接口实现
|
||||
// 📁 app/api/tags/route.ts
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// 🔍 验证请求参数
|
||||
const { searchParams } = new URL(request.url);
|
||||
const page = parseInt(searchParams.get('page') || '1');
|
||||
const limit = parseInt(searchParams.get('limit') || '10');
|
||||
|
||||
// 📊 分页查询标签列表
|
||||
const tags = await prisma.tag.findMany({
|
||||
orderBy: {
|
||||
usageCount: 'desc' // 按使用次数降序
|
||||
},
|
||||
skip: (page - 1) * limit,
|
||||
take: limit
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
data: tags,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total: await prisma.tag.count()
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
// ⚠️ 错误处理
|
||||
console.error('获取标签列表失败:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '服务器内部错误' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 问题诊断与解决
|
||||
|
||||
### 🔍 常见问题排查
|
||||
```
|
||||
[🔍 正在诊断问题]
|
||||
|
||||
用户报告:用户注册功能报错
|
||||
|
||||
诊断过程:
|
||||
1. 🔍 检查相关代码文件
|
||||
├── app/api/auth/register/route.ts
|
||||
├── prisma/schema.prisma
|
||||
└── lib/auth.ts
|
||||
|
||||
2. 🔎 发现潜在问题
|
||||
├── ⚠️ User模型缺少email唯一性约束
|
||||
├── ⚠️ 注册API缺少输入验证
|
||||
└── ⚠️ 密码加密强度不足
|
||||
|
||||
3. 💡 修复建议
|
||||
├── ✅ 在email字段添加@unique约束
|
||||
├── ✅ 添加完整的输入验证逻辑
|
||||
├── ✅ 使用强密码哈希算法
|
||||
└── ✅ 添加详细的错误日志
|
||||
```
|
||||
|
||||
### 🛠️ 代码修复示例
|
||||
```typescript
|
||||
// 🔧 修复用户注册API
|
||||
// 📁 app/api/auth/register/route.ts
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// 1. 🔍 输入验证
|
||||
const { email, username, password } = await request.json();
|
||||
|
||||
if (!email || !username || !password) {
|
||||
return NextResponse.json(
|
||||
{ error: '请填写所有必填字段' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 2. 🔒 密码强度验证
|
||||
if (password.length < 8) {
|
||||
return NextResponse.json(
|
||||
{ error: '密码长度不能少于8位' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 3. 🗃️ 检查邮箱是否存在
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
{ error: '该邮箱已被注册' },
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
// 4. 🔐 密码加密(bcrypt)
|
||||
const hashedPassword = await bcrypt.hash(password, 12);
|
||||
|
||||
// 5. 💾 创建用户
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email: email.toLowerCase().trim(),
|
||||
username: username.trim(),
|
||||
password: hashedPassword,
|
||||
role: 'USER',
|
||||
isActive: false,
|
||||
canDownload: true
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
role: true
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
message: '注册成功,请等待管理员激活',
|
||||
user
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('注册失败:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '服务器内部错误' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 部署指导
|
||||
|
||||
### 📦 容器化部署
|
||||
```
|
||||
[🚀 正在生成部署配置]
|
||||
|
||||
根据您的环境自动配置:
|
||||
|
||||
1. 🔧 Docker Compose配置
|
||||
├── 数据库服务(PostgreSQL)
|
||||
├── 存储服务(MinIO)
|
||||
└── 应用服务(Next.js)
|
||||
|
||||
2. 🔐 环境变量配置
|
||||
├── 数据库连接配置
|
||||
├── 文件存储配置
|
||||
└── 安全密钥配置
|
||||
|
||||
3. 📊 性能优化配置
|
||||
├── 内存限制设置
|
||||
├── 缓存策略配置
|
||||
└── 图片压缩优化
|
||||
```
|
||||
|
||||
### 🔄 部署执行
|
||||
```bash
|
||||
# 🔍 部署前检查
|
||||
docker --version
|
||||
docker-compose --version
|
||||
|
||||
# 🚀 开始部署
|
||||
docker-compose up -d
|
||||
|
||||
# 📊 验证部署状态
|
||||
docker-compose ps
|
||||
curl http://localhost:4320/health
|
||||
|
||||
# 🎉 部署完成!
|
||||
echo "系统部署成功,访问地址:http://localhost:4320"
|
||||
```
|
||||
|
||||
## 📖 文档维护规范
|
||||
|
||||
### 📝 文档更新原则
|
||||
- 🔄 **增量更新**:优先在现有文档基础上更新
|
||||
- 🗑️ **及时清理**:删除过时的文档和一次性脚本
|
||||
- 📂 **分类管理**:按功能模块组织文档
|
||||
- 🎯 **版本控制**:重要更新记录版本变更
|
||||
|
||||
### 📋 文档模板
|
||||
```markdown
|
||||
# 📁 功能名称
|
||||
|
||||
## 🎯 功能描述
|
||||
简要说明功能用途
|
||||
|
||||
## 📋 实现步骤
|
||||
1. 步骤1
|
||||
2. 步骤2
|
||||
3. 步骤3
|
||||
|
||||
## 💻 关键代码
|
||||
```typescript
|
||||
// 代码示例
|
||||
```
|
||||
|
||||
## 🔧 配置说明
|
||||
- 配置项1:说明
|
||||
- 配置项2:说明
|
||||
|
||||
## 🧪 测试验证
|
||||
- 测试点1
|
||||
- 测试点2
|
||||
|
||||
## 📊 更新记录
|
||||
- v1.0:初始版本
|
||||
- v1.1:功能优化
|
||||
```
|
||||
|
||||
## 🎯 核心价值
|
||||
|
||||
### 🚀 快速开发
|
||||
- ⚡ **高效编码**:快速实现功能需求
|
||||
- 🔧 **问题解决**:快速定位和修复问题
|
||||
- 📊 **性能优化**:提升系统运行效率
|
||||
- 🛡️ **安全加固**:确保系统安全稳定
|
||||
|
||||
### 🏢 企业适配
|
||||
- 🖥️ **内网优先**:满足企业内部使用需求
|
||||
- 🌐 **外网兼容**:支持远程访问功能
|
||||
- 🔒 **安全合规**:符合企业安全标准
|
||||
- 📈 **性能适配**:支持不同配置机器
|
||||
|
||||
### 🎨 用户体验
|
||||
- 💬 **中文交互**:友好的中文对话体验
|
||||
- 🎯 **简洁高效**:清晰的任务规划和执行
|
||||
- 📚 **详细文档**:完整的开发和使用文档
|
||||
- 🔄 **持续改进**:基于反馈不断优化
|
||||
|
||||
---
|
||||
|
||||
## 📋 使用指南
|
||||
|
||||
将此提示词复制给AI助手,即可获得专业的3D素材共享系统开发指导。智能体会提供:
|
||||
|
||||
- 🛠️ **功能开发**:从需求分析到代码实现
|
||||
- 🔍 **问题诊断**:快速定位和解决技术问题
|
||||
- 🚀 **部署指导**:容器化部署和运维建议
|
||||
- 📖 **文档维护**:规范的开发和维护流程
|
||||
|
||||
**核心特色:**
|
||||
- 🎯 **开发为主**:专注代码开发和功能实现
|
||||
- 💬 **中文交互**:全程中文对话交流
|
||||
- 📋 **任务规划**:清晰的任务列表管理
|
||||
- 🔒 **安全稳定**:注重代码安全和系统稳定
|
||||
@ -1,5 +0,0 @@
|
||||
# Docker 镜像更新说明
|
||||
|
||||
…(内容同原文件,保持不变)
|
||||
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
# 部署方式说明
|
||||
|
||||
## 🚀 快速部署
|
||||
|
||||
…(内容同原文件,保持不变)
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
# 修改记录
|
||||
|
||||
## 2025-11-13
|
||||
- 新增 `.env.example`,并在 `README.md` 说明配置流程,统一环境变量管理。
|
||||
- 调整 `docker-compose.yml` 引入共享 env 文件与健康检查,同时新增 `docker-compose.dev.yml` 适配本地容器开发。
|
||||
- 新增 `docs/README.md` 文档索引与 `docs/WORKFLOW.md` 流程指南,更新 `scripts/README.md` 使用说明。
|
||||
- `package.json` 新增 `npm run check`(lint + prisma validate)脚本,便于提交前自检。
|
||||
- 新增 `S3_ENV_PREFIX` 支持,为测试环境提供独立命名空间,避免共用存储桶时污染正式数据,并在 `lib/s3.ts`、相关配置文档中支持该能力。
|
||||
- 新增 `scripts/tools/update-admin-account.js`,可快速把管理员账号更新为指定邮箱与密码(默认改为 `1@qq.com` / `666666`),并将脚本纳入文档说明。
|
||||
- 优化用户树形管理界面:分支标题展示部门、管理员与成员统计,分支管理员高亮显示,并在编辑用户时自动将“直属上级”定位到所选部门的管理员。
|
||||
- 编辑用户面板新增部门管理员概览,可一键设为/取消管理员并查看现有负责人,后续多管理员扩展更清晰。
|
||||
- 新增 `scripts/deploy/local-build-and-upload.ps1` 支持本地构建镜像后压缩上传服务器,同时调整 `scripts/README.md` 说明部署流程。
|
||||
- 强化部署脚本错误处理,构建/打包/上传失败时立即报错提示,避免生成空文件或误导提示。
|
||||
@ -1,386 +0,0 @@
|
||||
# 一键部署完整指南
|
||||
|
||||
## 🎯 目标
|
||||
|
||||
让其他公司能够**一条命令**完成部署,**无需源码**,直接从私有镜像仓库拉取镜像。
|
||||
|
||||
---
|
||||
|
||||
## 📦 部署前准备
|
||||
|
||||
### 1. 服务器要求
|
||||
|
||||
- ✅ Linux服务器(推荐Ubuntu 20.04+或CentOS 7+)
|
||||
- ✅ Docker 20.10+
|
||||
- ✅ Docker Compose 2.0+
|
||||
- ✅ 至少2GB内存
|
||||
- ✅ 至少20GB磁盘空间
|
||||
|
||||
### 2. 配置Docker镜像加速器(必须)
|
||||
|
||||
**执行配置脚本**:
|
||||
```bash
|
||||
# 上传脚本到服务器
|
||||
scp scripts/server/setup-docker-mirror.sh root@服务器IP:/root/
|
||||
|
||||
# SSH登录服务器
|
||||
ssh root@服务器IP
|
||||
|
||||
# 执行配置
|
||||
chmod +x /root/setup-docker-mirror.sh
|
||||
sudo /root/setup-docker-mirror.sh
|
||||
```
|
||||
|
||||
**或手动配置**:
|
||||
```bash
|
||||
# 创建配置文件
|
||||
sudo nano /etc/docker/daemon.json
|
||||
|
||||
# 添加内容
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.mirrors.ustc.edu.cn",
|
||||
"https://hub-mirror.c.163.com",
|
||||
"https://mirror.baidubce.com"
|
||||
]
|
||||
}
|
||||
|
||||
# 重启Docker
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 标准版部署(内网使用)
|
||||
|
||||
### 方式1:一键部署脚本
|
||||
|
||||
**在本地执行**(需要提供镜像仓库地址):
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
.\scripts\deploy\local-build-and-upload.ps1 `
|
||||
-Server 服务器IP `
|
||||
-Username root `
|
||||
-Password 密码 `
|
||||
-DockerRegistry 你的镜像仓库地址:5000 `
|
||||
-Edition standard
|
||||
```
|
||||
|
||||
### 方式2:手动部署
|
||||
|
||||
**步骤1:上传配置文件**
|
||||
```bash
|
||||
# 上传标准版配置
|
||||
scp docker-compose.standard.yml root@服务器IP:/root/deploy/
|
||||
```
|
||||
|
||||
**步骤2:创建环境变量文件**
|
||||
```bash
|
||||
# SSH登录服务器
|
||||
ssh root@服务器IP
|
||||
|
||||
# 创建.env文件
|
||||
cd /root/deploy
|
||||
cat > .env << EOF
|
||||
# Docker镜像仓库(必须配置)
|
||||
DOCKER_REGISTRY=你的镜像仓库地址:5000
|
||||
|
||||
# 镜像标签
|
||||
IMAGE_TAG=latest
|
||||
|
||||
# 服务器IP
|
||||
SERVER_IP=服务器IP地址
|
||||
|
||||
# 数据库配置
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=your-secure-password
|
||||
POSTGRES_DB=flnxi
|
||||
|
||||
# 应用端口
|
||||
APP_PORT=4320
|
||||
|
||||
# NextAuth密钥(必须修改)
|
||||
NEXTAUTH_SECRET=$(openssl rand -base64 32)
|
||||
EOF
|
||||
```
|
||||
|
||||
**步骤3:部署**
|
||||
```bash
|
||||
# 拉取镜像(自动使用国内镜像源)
|
||||
docker compose -f docker-compose.standard.yml --env-file .env pull
|
||||
|
||||
# 启动服务
|
||||
docker compose -f docker-compose.standard.yml --env-file .env up -d
|
||||
|
||||
# 查看日志
|
||||
docker compose -f docker-compose.standard.yml logs -f
|
||||
```
|
||||
|
||||
**步骤4:创建配置链接**
|
||||
```bash
|
||||
# 创建docker-compose.yml链接(方便后续操作)
|
||||
ln -sf docker-compose.standard.yml docker-compose.yml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Pro版部署(支持内网穿透)
|
||||
|
||||
### 方式1:从标准版升级(推荐)
|
||||
|
||||
**使用升级脚本**:
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
.\scripts\deploy\upgrade-to-pro.ps1 `
|
||||
-Server 服务器IP `
|
||||
-DockerRegistry 你的镜像仓库地址:5000 `
|
||||
-ImageTag latest
|
||||
```
|
||||
|
||||
**或Linux服务器上直接执行**:
|
||||
```bash
|
||||
export DOCKER_REGISTRY=你的镜像仓库地址:5000
|
||||
export IMAGE_TAG=latest
|
||||
bash scripts/deploy/upgrade-to-pro.sh
|
||||
```
|
||||
|
||||
### 方式2:直接部署Pro版
|
||||
|
||||
**步骤1:上传配置文件**
|
||||
```bash
|
||||
scp docker-compose.prod.yml root@服务器IP:/root/deploy/
|
||||
```
|
||||
|
||||
**步骤2:创建环境变量文件**
|
||||
```bash
|
||||
cd /root/deploy
|
||||
cat > .env << EOF
|
||||
# Docker镜像仓库
|
||||
DOCKER_REGISTRY=你的镜像仓库地址:5000
|
||||
IMAGE_TAG=latest
|
||||
|
||||
# 服务器IP
|
||||
SERVER_IP=服务器IP地址
|
||||
|
||||
# 数据库配置
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=your-secure-password
|
||||
POSTGRES_DB=flnxi
|
||||
|
||||
# MinIO配置(Pro版需要)
|
||||
MINIO_ROOT_USER=minioadmin
|
||||
MINIO_ROOT_PASSWORD=your-secure-password
|
||||
S3_BUCKET=youyidian3d
|
||||
MINIO_API_PORT=9000
|
||||
MINIO_CONSOLE_PORT=9001
|
||||
|
||||
# 应用配置
|
||||
APP_PORT=4320
|
||||
NEXTAUTH_SECRET=$(openssl rand -base64 32)
|
||||
EOF
|
||||
```
|
||||
|
||||
**步骤3:部署**
|
||||
```bash
|
||||
# 拉取所有镜像
|
||||
docker compose -f docker-compose.prod.yml --env-file .env pull
|
||||
|
||||
# 启动服务
|
||||
docker compose -f docker-compose.prod.yml --env-file .env up -d
|
||||
|
||||
# 查看日志
|
||||
docker compose -f docker-compose.prod.yml logs -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 版本升级
|
||||
|
||||
### 升级应用镜像
|
||||
|
||||
```bash
|
||||
# 拉取新版本镜像
|
||||
docker pull 你的镜像仓库地址:5000/flnxi-material:standard-latest
|
||||
|
||||
# 重启服务
|
||||
cd /root/deploy
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 从标准版升级到Pro版
|
||||
|
||||
**使用升级脚本**(推荐):
|
||||
```bash
|
||||
export DOCKER_REGISTRY=你的镜像仓库地址:5000
|
||||
bash scripts/deploy/upgrade-to-pro.sh
|
||||
```
|
||||
|
||||
**手动升级**:
|
||||
```bash
|
||||
# 1. 备份
|
||||
cd /root/deploy
|
||||
mkdir -p backup
|
||||
cp docker-compose.yml backup/
|
||||
docker compose exec -T postgres pg_dump -U postgres flnxi > backup/db.sql
|
||||
|
||||
# 2. 切换配置
|
||||
ln -sf docker-compose.prod.yml docker-compose.yml
|
||||
|
||||
# 3. 更新环境变量
|
||||
echo "DOCKER_REGISTRY=你的镜像仓库地址:5000" >> .env
|
||||
|
||||
# 4. 拉取Pro版镜像和MinIO
|
||||
docker pull 你的镜像仓库地址:5000/flnxi-material:pro-latest
|
||||
docker pull minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
docker pull minio/mc:RELEASE.2025-04-22T22-12-26Z
|
||||
|
||||
# 5. 启动服务
|
||||
docker compose --env-file .env up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 部署验证
|
||||
|
||||
### 检查服务状态
|
||||
|
||||
```bash
|
||||
cd /root/deploy
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
```
|
||||
NAME STATUS
|
||||
youyidian-postgres Up (healthy)
|
||||
flnxi-material Up (healthy)
|
||||
youyidian-minio Up (healthy) # Pro版才有
|
||||
```
|
||||
|
||||
### 检查应用健康
|
||||
|
||||
```bash
|
||||
# 健康检查
|
||||
curl http://localhost:4320/api/health
|
||||
|
||||
# 预期输出
|
||||
{"status":"ok","timestamp":"..."}
|
||||
```
|
||||
|
||||
### 访问应用
|
||||
|
||||
- **应用地址**:`http://服务器IP:4320`
|
||||
- **MinIO控制台**(Pro版):`http://服务器IP:9001`
|
||||
- **默认账号**:
|
||||
- MinIO: `minioadmin` / `minioadmin123456`(建议修改)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 常用操作
|
||||
|
||||
### 查看日志
|
||||
|
||||
```bash
|
||||
# 查看所有服务日志
|
||||
docker compose logs -f
|
||||
|
||||
# 查看特定服务日志
|
||||
docker compose logs -f app
|
||||
docker compose logs -f minio # Pro版
|
||||
```
|
||||
|
||||
### 停止服务
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### 重启服务
|
||||
|
||||
```bash
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
### 备份数据
|
||||
|
||||
```bash
|
||||
# 备份数据库
|
||||
docker compose exec -T postgres pg_dump -U postgres flnxi > backup_$(date +%Y%m%d).sql
|
||||
|
||||
# 备份上传文件(标准版)
|
||||
tar -czf uploads_backup_$(date +%Y%m%d).tar.gz ./uploads
|
||||
|
||||
# 备份MinIO数据(Pro版)
|
||||
docker run --rm -v youyidian-minio_data:/data -v $(pwd):/backup \
|
||||
alpine tar -czf /backup/minio_backup_$(date +%Y%m%d).tar.gz /data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 故障排查
|
||||
|
||||
### 问题1:无法拉取镜像
|
||||
|
||||
**检查项**:
|
||||
```bash
|
||||
# 检查镜像仓库配置
|
||||
docker info | grep -A 10 "Insecure Registries"
|
||||
|
||||
# 测试连接
|
||||
curl -k https://你的镜像仓库地址:5000/v2/
|
||||
|
||||
# 检查登录状态
|
||||
docker login 你的镜像仓库地址:5000
|
||||
```
|
||||
|
||||
### 问题2:服务启动失败
|
||||
|
||||
**检查项**:
|
||||
```bash
|
||||
# 查看详细日志
|
||||
docker compose logs
|
||||
|
||||
# 检查端口占用
|
||||
netstat -tlnp | grep -E "4320|9000|9001|5432"
|
||||
|
||||
# 检查磁盘空间
|
||||
df -h
|
||||
```
|
||||
|
||||
### 问题3:升级失败
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 回滚到备份
|
||||
cd /root/deploy
|
||||
cp backup/docker-compose.yml docker-compose.yml
|
||||
docker compose up -d
|
||||
|
||||
# 恢复数据库
|
||||
docker compose exec -T postgres psql -U postgres flnxi < backup/db.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **安全性**:
|
||||
- ✅ 修改所有默认密码
|
||||
- ✅ 使用HTTPS访问镜像仓库(生产环境)
|
||||
- ✅ 配置防火墙规则
|
||||
|
||||
2. **性能**:
|
||||
- ✅ 确保配置了Docker镜像加速器
|
||||
- ✅ 定期清理未使用的镜像:`docker system prune -a`
|
||||
|
||||
3. **备份**:
|
||||
- ✅ 定期备份数据库
|
||||
- ✅ 备份上传文件和MinIO数据
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
|
||||
|
||||
@ -1,271 +0,0 @@
|
||||
# 一键部署快速指南
|
||||
|
||||
## 🚀 目标
|
||||
|
||||
让其他公司能够**一条命令**完成部署,无需复杂配置。
|
||||
|
||||
---
|
||||
|
||||
## 📦 部署方式
|
||||
|
||||
### 方式1:Docker Compose(推荐)
|
||||
|
||||
```bash
|
||||
# 1. 克隆或下载项目
|
||||
git clone <repository-url>
|
||||
cd flnxi_sucai
|
||||
|
||||
# 2. 配置环境变量(可选,有默认值)
|
||||
cp .env.example .env
|
||||
# 编辑 .env 文件(可选)
|
||||
|
||||
# 3. 一键启动
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# 4. 等待服务启动(约30秒)
|
||||
docker compose logs -f app
|
||||
|
||||
# 5. 访问应用
|
||||
# http://your-server-ip:4320
|
||||
```
|
||||
|
||||
### 方式2:Docker镜像(最简)
|
||||
|
||||
```bash
|
||||
# 1. 拉取镜像(如果已发布到Docker Hub)
|
||||
docker pull your-registry/flnxi-material:latest
|
||||
|
||||
# 2. 运行(使用外部数据库和MinIO)
|
||||
docker run -d \
|
||||
--name flnxi-material \
|
||||
-p 4320:3000 \
|
||||
-e DATABASE_URL=postgresql://user:pass@host:5432/dbname \
|
||||
-e S3_ENDPOINT=http://minio:9000 \
|
||||
-e S3_BUCKET=youyidian3d \
|
||||
your-registry/flnxi-material:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 最小配置
|
||||
|
||||
### 必需的环境变量
|
||||
|
||||
```bash
|
||||
# 数据库(必需)
|
||||
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/flnxi
|
||||
|
||||
# S3存储(必需,使用MinIO)
|
||||
S3_ENDPOINT=http://minio:9000
|
||||
S3_BUCKET=youyidian3d
|
||||
S3_ACCESS_KEY_ID=minioadmin
|
||||
S3_SECRET_ACCESS_KEY=minioadmin123456
|
||||
S3_PUBLIC_DOMAIN=http://your-server-ip:9000/youyidian3d
|
||||
|
||||
# 认证(必需)
|
||||
NEXTAUTH_SECRET=your-random-secret-key-here
|
||||
NEXTAUTH_URL=http://your-server-ip:4320
|
||||
```
|
||||
|
||||
### 可选的环境变量
|
||||
|
||||
```bash
|
||||
# 站点配置(可选,有默认值)
|
||||
APP_PUBLIC_URL=http://your-server-ip:4320
|
||||
NEXT_PUBLIC_DEFAULT_HEADER_LOGO_URL=
|
||||
NEXT_PUBLIC_DEFAULT_LOGIN_LOGO_URL=
|
||||
|
||||
# 功能开关(可选,默认启用)
|
||||
ENABLE_REGISTRATION=true
|
||||
ENABLE_USER_ACTIVATION=false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 默认配置说明
|
||||
|
||||
### 数据库
|
||||
- **默认数据库名**:`flnxi`
|
||||
- **默认用户**:`postgres`
|
||||
- **默认密码**:`youyidian123456`(**生产环境请修改**)
|
||||
|
||||
### MinIO
|
||||
- **默认Bucket**:`youyidian3d`
|
||||
- **默认访问密钥**:`minioadmin` / `minioadmin123456`(**生产环境请修改**)
|
||||
- **默认端口**:9000(API),9001(控制台)
|
||||
|
||||
### 应用
|
||||
- **默认端口**:4320
|
||||
- **默认管理员**:首位注册用户自动成为超级管理员
|
||||
|
||||
---
|
||||
|
||||
## 📋 部署检查清单
|
||||
|
||||
### 部署前
|
||||
- [ ] 检查服务器资源(建议:2核CPU,2GB内存,20GB磁盘)
|
||||
- [ ] 确认Docker和Docker Compose已安装
|
||||
- [ ] 确认端口4320、9000、9001未被占用
|
||||
- [ ] 准备强随机字符串作为`NEXTAUTH_SECRET`
|
||||
|
||||
### 部署后
|
||||
- [ ] 检查容器状态:`docker compose ps`
|
||||
- [ ] 检查应用日志:`docker compose logs app`
|
||||
- [ ] 访问健康检查:`http://your-server-ip:4320/api/health`
|
||||
- [ ] 测试注册功能(首位用户自动成为管理员)
|
||||
- [ ] 测试上传功能
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 1. 容器无法启动
|
||||
```bash
|
||||
# 检查日志
|
||||
docker compose logs app
|
||||
|
||||
# 常见原因:
|
||||
# - 端口被占用:修改docker-compose.prod.yml中的端口映射
|
||||
# - 数据库连接失败:检查DATABASE_URL配置
|
||||
# - 内存不足:增加服务器内存或调整Docker资源限制
|
||||
```
|
||||
|
||||
### 2. 无法访问应用
|
||||
```bash
|
||||
# 检查防火墙
|
||||
sudo ufw allow 4320
|
||||
|
||||
# 检查容器是否运行
|
||||
docker compose ps
|
||||
|
||||
# 检查应用日志
|
||||
docker compose logs app --tail=50
|
||||
```
|
||||
|
||||
### 3. 上传功能失败
|
||||
```bash
|
||||
# 检查MinIO状态
|
||||
docker compose logs minio
|
||||
|
||||
# 检查S3配置
|
||||
docker compose exec app printenv | grep S3_
|
||||
|
||||
# 检查上传目录权限
|
||||
docker compose exec app ls -la /app/uploads
|
||||
```
|
||||
|
||||
### 4. 数据库连接失败
|
||||
```bash
|
||||
# 检查PostgreSQL状态
|
||||
docker compose logs postgres
|
||||
|
||||
# 检查数据库是否创建
|
||||
docker compose exec postgres psql -U postgres -l
|
||||
|
||||
# 检查连接字符串
|
||||
docker compose exec app printenv DATABASE_URL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 安全建议
|
||||
|
||||
### 生产环境必须修改
|
||||
|
||||
1. **修改默认密码**
|
||||
```bash
|
||||
# 在docker-compose.prod.yml中修改
|
||||
POSTGRES_PASSWORD=your-strong-password
|
||||
MINIO_ROOT_PASSWORD=your-strong-password
|
||||
```
|
||||
|
||||
2. **设置强随机NEXTAUTH_SECRET**
|
||||
```bash
|
||||
# 生成随机字符串
|
||||
openssl rand -base64 32
|
||||
|
||||
# 在.env中设置
|
||||
NEXTAUTH_SECRET=生成的随机字符串
|
||||
```
|
||||
|
||||
3. **配置防火墙**
|
||||
```bash
|
||||
# 只开放必要端口
|
||||
sudo ufw allow 4320/tcp # 应用端口
|
||||
sudo ufw allow 9000/tcp # MinIO API(如果外部访问)
|
||||
sudo ufw allow 9001/tcp # MinIO控制台(如果外部访问)
|
||||
```
|
||||
|
||||
4. **使用HTTPS(推荐)**
|
||||
- 配置Nginx反向代理
|
||||
- 使用Let's Encrypt免费SSL证书
|
||||
- 参考:[HTTPS部署指南](./guides/HTTPS_SETUP.md)
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能监控
|
||||
|
||||
### 资源使用监控
|
||||
|
||||
```bash
|
||||
# 查看容器资源使用
|
||||
docker stats
|
||||
|
||||
# 查看应用日志
|
||||
docker compose logs app --tail=100 -f
|
||||
|
||||
# 查看数据库连接数
|
||||
docker compose exec postgres psql -U postgres -c "SELECT count(*) FROM pg_stat_activity;"
|
||||
```
|
||||
|
||||
### 健康检查
|
||||
|
||||
```bash
|
||||
# 应用健康检查
|
||||
curl http://localhost:4320/api/health
|
||||
|
||||
# 数据库健康检查
|
||||
docker compose exec postgres pg_isready -U postgres
|
||||
|
||||
# MinIO健康检查
|
||||
docker compose exec minio mc ready local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 更新部署
|
||||
|
||||
```bash
|
||||
# 1. 停止服务
|
||||
docker compose down
|
||||
|
||||
# 2. 备份数据(重要!)
|
||||
docker compose exec postgres pg_dump -U postgres flnxi > backup.sql
|
||||
|
||||
# 3. 拉取新镜像或重新构建
|
||||
docker compose pull # 如果使用远程镜像
|
||||
# 或
|
||||
docker compose build # 如果本地构建
|
||||
|
||||
# 4. 启动服务
|
||||
docker compose up -d
|
||||
|
||||
# 5. 检查状态
|
||||
docker compose ps
|
||||
docker compose logs app --tail=50
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [架构设计与编码规范](./ARCHITECTURE_AND_CODING_STANDARDS.md)
|
||||
- [性能优化指南](./PERFORMANCE_OPTIMIZATION_GUIDE.md)
|
||||
- [可移植部署指南](./PORTABLE_DEPLOYMENT_GUIDE.md)
|
||||
- [安全部署流程](./guides/安全部署流程.md)
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
|
||||
|
||||
@ -1,178 +0,0 @@
|
||||
# 代码优化总结
|
||||
|
||||
根据《架构设计与编码规范.md》和《性能优化指南.md》,已完成以下优化:
|
||||
|
||||
## 1. 性能优化
|
||||
|
||||
### 1.1 数据库查询优化
|
||||
|
||||
#### N+1 查询问题修复
|
||||
- **rankings API** (`app/api/rankings/route.ts`)
|
||||
- 下载排行榜和点赞排行榜:从 N+1 查询改为单次 SQL 聚合查询
|
||||
- 使用 `INNER JOIN` 一次性获取用户信息和统计数据
|
||||
|
||||
- **categories API** (`app/api/categories/route.ts`)
|
||||
- 从每个分类单独查询素材数量改为单次 SQL 聚合查询
|
||||
- 使用 `GROUP BY` 一次性获取所有分类的素材数量
|
||||
|
||||
- **scenes API** (`app/api/scenes/route.ts`)
|
||||
- 从每个场景单独查询素材数量改为单次 SQL 聚合查询
|
||||
- 支持按店铺筛选,仍使用单次查询
|
||||
|
||||
#### 查询字段优化
|
||||
- **system-settings API** (`app/api/system-settings/route.ts`)
|
||||
- 使用 `select` 只查询必要字段,避免返回敏感信息(如密码)
|
||||
- 实现 1 分钟缓存,减少数据库查询
|
||||
|
||||
- **admin/system-status API** (`app/api/admin/system-status/route.ts`)
|
||||
- 优化并行查询,减少查询次数
|
||||
- 使用 SQL 聚合查询替代多次 `count()` 调用
|
||||
- 限制最近活动数据为 10 条,减少数据传输
|
||||
|
||||
- **materials/stats API** (`app/api/materials/stats/route.ts`)
|
||||
- 从加载所有素材的 `createdAt` 改为 SQL 聚合查询
|
||||
- 使用 `EXTRACT(YEAR/MONTH)` 在数据库层面完成分组
|
||||
|
||||
### 1.2 分页实现
|
||||
|
||||
- **admin/users API** (`app/api/admin/users/route.ts`)
|
||||
- 添加分页支持(默认 20 条,最大 100 条)
|
||||
- 返回分页信息(总数、总页数等)
|
||||
|
||||
- **danmaku API** (`app/api/danmaku/route.ts`)
|
||||
- 限制最大返回数量为 100 条
|
||||
- 只返回必要字段,保护用户隐私(不返回 `userId`)
|
||||
|
||||
### 1.3 缓存机制
|
||||
|
||||
- **创建缓存工具** (`lib/cache.ts`)
|
||||
- 实现简单的内存缓存(单例模式)
|
||||
- 支持 TTL(默认 1 分钟)
|
||||
- 自动清理过期项
|
||||
- 限制最大缓存项数(1000 项)
|
||||
|
||||
- **system-settings API 缓存**
|
||||
- GET 请求使用缓存(1 分钟 TTL)
|
||||
- PUT 请求自动清除缓存,确保数据一致性
|
||||
|
||||
### 1.4 SSE 优化
|
||||
|
||||
- **danmaku API SSE** (`app/api/danmaku/route.ts`)
|
||||
- 减少初始数据量(从 50 条减少到 30 条)
|
||||
- 增加轮询间隔(从 1 秒增加到 2 秒),减少数据库压力
|
||||
- 使用 `lastDanmakuId` 避免重复推送
|
||||
- 改进错误处理,出错时自动关闭连接,避免资源泄漏
|
||||
|
||||
## 2. 代码质量改进
|
||||
|
||||
### 2.1 错误处理
|
||||
|
||||
- 统一错误日志格式,添加 API 标识前缀(如 `[SystemSettings]`、`[MaterialStats]`)
|
||||
- 改进错误信息,开发环境返回详细错误,生产环境返回通用错误
|
||||
|
||||
### 2.2 资源管理
|
||||
|
||||
- SSE 连接正确清理(`clearInterval` 和 `controller.close()`)
|
||||
- 错误时自动关闭连接,避免资源泄漏
|
||||
|
||||
### 2.3 数据安全
|
||||
|
||||
- 弹幕 API 不返回 `userId`,保护用户隐私
|
||||
- 系统设置 API 使用 `select` 避免返回敏感字段
|
||||
|
||||
## 3. 符合规范检查
|
||||
|
||||
### ✅ 性能开销优先
|
||||
- 所有列表 API 实现分页
|
||||
- 使用 SQL 聚合查询替代多次查询
|
||||
- 实现缓存机制减少数据库查询
|
||||
- 优化数据传输量(只返回必要字段)
|
||||
|
||||
### ✅ 稳定性优先
|
||||
- 改进错误处理
|
||||
- 资源正确清理
|
||||
- SSE 连接异常时自动关闭
|
||||
|
||||
### ✅ 易于部署
|
||||
- 缓存机制为内存缓存,无需额外配置
|
||||
- 所有优化都是代码层面的,无需修改部署配置
|
||||
|
||||
## 4. 第二轮优化(已完成)
|
||||
|
||||
### 4.1 修复更多 N+1 查询问题
|
||||
- **platforms API** (`app/api/platforms/route.ts`)
|
||||
- 从每个平台单独查询素材数量改为单次 SQL 聚合查询
|
||||
- 统一错误日志格式
|
||||
|
||||
### 4.2 优化 materials API
|
||||
- **materials API** (`app/api/materials/route.ts`)
|
||||
- 修复重复代码问题
|
||||
- 使用 `select` 只查询必要字段
|
||||
- 限制最大分页大小为 100
|
||||
- 并行查询总数和数据,提升性能
|
||||
- 优化点赞状态查询(只在有数据时查询)
|
||||
|
||||
- **materials/[id] API** (`app/api/materials/[id]/route.ts`)
|
||||
- 使用 `select` 只查询必要字段
|
||||
- 统一错误日志格式
|
||||
|
||||
### 4.3 优化 rankings API
|
||||
- **rankings API** (`app/api/rankings/route.ts`)
|
||||
- 贡献排行榜改为 SQL 聚合查询,保持与其他排行榜一致
|
||||
- 统一错误日志格式
|
||||
|
||||
### 4.4 优化 invitations API
|
||||
- **invitations API** (`app/api/invitations/route.ts`)
|
||||
- 添加分页支持(默认 20 条,最大 100 条)
|
||||
- 使用 `select` 只返回必要字段,保护隐私
|
||||
- 并行查询总数和数据
|
||||
- 统一错误日志格式
|
||||
|
||||
## 5. 待优化项(可选)
|
||||
|
||||
### 5.1 文件上传优化
|
||||
- 当前使用 `arrayBuffer()` 将整个文件加载到内存
|
||||
- 建议:对于大文件(>100MB),考虑使用流式处理
|
||||
- 注意:Next.js 的限制可能需要在服务器端实现
|
||||
|
||||
### 5.2 并发控制
|
||||
- 文件上传 API 当前没有并发限制
|
||||
- 建议:添加并发上传限制,避免服务器过载
|
||||
|
||||
### 5.3 数据库索引
|
||||
- 建议检查以下字段是否有索引:
|
||||
- `materials.category_name`
|
||||
- `materials.scene_name`
|
||||
- `materials.user_id`
|
||||
- `materials.created_at`
|
||||
- `materials.platform_name`
|
||||
- `users.department`
|
||||
- `users.role`
|
||||
|
||||
## 6. 性能提升预期
|
||||
|
||||
- **N+1 查询修复**:查询时间从 O(n) 降低到 O(1)
|
||||
- rankings API:3 个排行榜全部使用 SQL 聚合查询
|
||||
- categories/scenes/platforms API:从 N 次查询减少到 1 次
|
||||
- **缓存机制**:系统设置查询响应时间减少 90%+(缓存命中时)
|
||||
- **分页实现**:大数据量时内存使用减少 80%+
|
||||
- materials API:限制最大分页为 100
|
||||
- admin/users API:支持分页
|
||||
- invitations API:支持分页
|
||||
- **并行查询**:materials API 和 invitations API 使用并行查询,响应时间减少 30-50%
|
||||
- **SSE 优化**:数据库查询频率减少 50%
|
||||
- **查询字段优化**:所有 API 使用 `select` 只查询必要字段,数据传输量减少 20-40%
|
||||
|
||||
## 7. 注意事项
|
||||
|
||||
1. **缓存一致性**:系统设置更新时会自动清除缓存,确保数据一致性
|
||||
2. **单实例部署**:当前缓存实现为内存缓存,仅适用于单实例部署
|
||||
3. **数据库表名**:SQL 查询使用 PostgreSQL 的实际表名(snake_case),而非 Prisma 模型名
|
||||
4. **向后兼容**:
|
||||
- materials API:保持原有响应格式
|
||||
- invitations API:响应格式有变化,从数组改为对象(包含 `invitations` 和 `pagination`)
|
||||
- admin/users API:响应格式有变化,从数组改为对象(包含 `users` 和 `pagination`)
|
||||
- 其他 API:保持向后兼容
|
||||
5. **分页限制**:所有分页 API 最大分页大小限制为 100,防止恶意请求
|
||||
6. **错误日志**:所有 API 统一使用 `[API名称] 方法名 error:` 格式,便于日志分析和调试
|
||||
|
||||
@ -1,682 +0,0 @@
|
||||
# 优化计划与优先级
|
||||
|
||||
> **项目宗旨**:性能开销优先、稳定性优先、易于部署、统一UI三层架构、遵循苹果设计语言
|
||||
|
||||
## 📋 优化原则
|
||||
|
||||
1. **统一UI三层架构优先**:所有UI改动必须遵循三层架构(基础层、颜色层、组合层)
|
||||
2. **基础组件优先**:先建立统一的基础组件,再实现具体功能
|
||||
3. **数据一致性优先**:确保数据同步和一致性
|
||||
4. **性能优化优先**:考虑低性能NAS机器,使用动态加载、瀑布流等
|
||||
5. **用户体验优先**:修复已知问题,优化交互体验
|
||||
|
||||
---
|
||||
|
||||
## 🎯 优先级规划
|
||||
|
||||
### 优先级 1:基础组件与UI统一(核心基础设施)
|
||||
|
||||
#### 1.1 创建统一弹窗组件 ⭐⭐⭐⭐⭐
|
||||
**需求编号**:16
|
||||
**优先级**:最高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 这是所有UI统一的基础
|
||||
- 后续多个功能都需要使用弹窗
|
||||
- 必须遵循三层架构设计
|
||||
- 替换浏览器原生弹窗,提升视觉一致性
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 创建 `components/ui/Modal.tsx` 统一弹窗组件
|
||||
- ✅ 使用 `useThemeStyles()` 和三层架构
|
||||
- ✅ 支持多种尺寸(small, medium, large, xlarge, fullscreen)
|
||||
- ✅ 支持关闭动画、遮罩层、键盘ESC关闭
|
||||
- ✅ 提供 `useModal` Hook(`hooks/useModal.ts`)统一管理弹窗状态
|
||||
- ✅ 完整的ARIA标签和键盘导航支持
|
||||
- ✅ 样式参考苹果设计语言,遵循统一视觉方案
|
||||
- ✅ 替换所有浏览器原生弹窗(confirm/alert/prompt)
|
||||
- ✅ 创建了 ConfirmDialog、AlertDialog、PromptDialog 组件
|
||||
- ✅ 创建了 DialogProvider 和 useDialog Hook
|
||||
- ✅ 替换了所有 admin 组件、首页、分享页面中的原生弹窗
|
||||
|
||||
**依赖关系**:无(基础组件)
|
||||
**影响范围**:全局(所有需要弹窗的地方)
|
||||
|
||||
---
|
||||
|
||||
#### 1.2 素材预览面板统一 ⭐⭐⭐⭐⭐
|
||||
**需求编号**:15
|
||||
**优先级**:最高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 统一退出逻辑和交互体验
|
||||
- 必须使用统一弹窗组件(依赖1.1)
|
||||
- 提升用户体验一致性
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 创建了 MaterialPreviewModal 组件
|
||||
- ✅ 使用统一弹窗组件重构素材预览面板
|
||||
- ✅ 统一退出逻辑(ESC键、点击遮罩、关闭按钮)
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 确保深浅色模式适配
|
||||
|
||||
**依赖关系**:依赖 1.1(统一弹窗组件)
|
||||
**影响范围**:首页素材预览
|
||||
|
||||
---
|
||||
|
||||
#### 1.3 首页素材展示响应式优化 ⭐⭐⭐⭐
|
||||
**需求编号**:8
|
||||
**优先级**:高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 修复已知UI问题(卡片比例、边界间距)
|
||||
- 必须遵循统一UI原则
|
||||
- 影响用户体验
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 修复卡片比例固定问题(使用 `aspect-[3/4]` 替代固定高度)
|
||||
- ✅ 添加容器边距(`px-4 sm:px-6 lg:px-8`)避免卡片贴边界
|
||||
- ✅ 使用三层架构样式系统(`useThemeStyles()` 和 `cardStyles.base`)
|
||||
- ✅ 优化响应式断点和图片 sizes 属性
|
||||
- ✅ 确保深浅色模式适配(使用 `slate` 系列颜色)
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:首页素材展示
|
||||
|
||||
---
|
||||
|
||||
#### 1.4 首页UI统一到三层架构 ⭐⭐⭐⭐
|
||||
**需求编号**:22
|
||||
**优先级**:高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 统一UI架构,确保视觉一致性
|
||||
- 首页是核心页面,必须遵循三层架构
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 迁移了主要按钮样式(上传按钮、筛选按钮)
|
||||
- ✅ 迁移了搜索输入框样式
|
||||
- ✅ 迁移了筛选弹窗中的所有按钮样式(类型分页、使用店铺、场景类别、平台分类、上传月份)
|
||||
- ✅ 迁移了筛选弹窗容器样式
|
||||
- ✅ 迁移了筛选弹窗关闭按钮样式
|
||||
- ✅ 使用 `useThemeStyles()` Hook
|
||||
- ✅ 完全统一颜色系统:所有 `gray-` 已替换为 `slate-`
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:首页所有UI组件
|
||||
|
||||
---
|
||||
|
||||
#### 1.5 统一UI三层架构通用组件库 ⭐⭐⭐⭐
|
||||
**需求编号**:23
|
||||
**优先级**:高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 提高开发效率
|
||||
- 确保组件一致性
|
||||
- 减少重复代码
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 在 `components/ui/` 目录下创建通用组件
|
||||
- ✅ 所有组件必须使用三层架构
|
||||
- ✅ 提供常用组件:Button、Input、Card、Badge、Tag、Avatar等
|
||||
- ✅ 每个组件提供完整的TypeScript类型定义
|
||||
- ✅ 使用 `useThemeStyles()` 确保主题适配
|
||||
- ✅ 已创建组件:
|
||||
- `Badge.tsx` - 徽章组件(支持 primary、success、warning、danger 变体)
|
||||
- `Tag.tsx` - 标签组件(支持多种颜色,已在首页应用)
|
||||
- `Button.tsx` - 按钮组件(支持多种变体和尺寸,带加载状态)
|
||||
- `Input.tsx` - 输入框组件(支持错误状态和多种尺寸)
|
||||
- `Card.tsx` - 卡片组件(支持多种变体:base、hover、large、flat、rainbow、glass、neon)
|
||||
- `Avatar.tsx` - 头像组件(支持图片和文字头像,多种尺寸)
|
||||
- `Modal.tsx` - 弹窗组件(已创建)
|
||||
- `ConfirmDialog.tsx`、`AlertDialog.tsx`、`PromptDialog.tsx` - 对话框组件(已创建)
|
||||
- `DialogProvider.tsx` - 对话框提供者(已创建)
|
||||
|
||||
**依赖关系**:无(基础组件库)
|
||||
**影响范围**:全局(所有使用通用组件的地方)
|
||||
|
||||
---
|
||||
|
||||
#### 1.6 深浅色主题适配优化 ⭐⭐⭐
|
||||
**需求编号**:17
|
||||
**优先级**:中
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 提升主题切换体验
|
||||
- 确保所有组件在深浅色模式下都完美显示
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 为所有新创建和重构的文件添加了文件头注释
|
||||
- Tab组件(UsersTab、DepartmentsTab、CategoriesTab、InvitationsTab、MaterialsTab、MonitorTab)
|
||||
- Hook文件(usePermissions、useDashboardData、useModalState、useToast、useDataFetch、useUserOperations、useDepartmentOperations)
|
||||
- 工具函数(api.ts、formatters.ts、departmentTree.ts)
|
||||
- UI组件(Modal、ConfirmDialog、AlertDialog、PromptDialog、DialogProvider、Button、Input、Card、Avatar、Badge、Tag)
|
||||
- ✅ Header组件迁移到三层架构
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- 使用统一的 `Avatar` 组件替换自定义头像实现
|
||||
- 下拉菜单使用 `cardStyles.base` 统一样式
|
||||
- 移除了所有 `dark:bg-gray` 等直接样式
|
||||
- ✅ ChangePasswordModal迁移到统一组件
|
||||
- 使用统一的 `Modal` 组件替换自定义弹窗实现
|
||||
- 使用统一的 `Input` 组件替换原生input
|
||||
- 使用统一的 `Button` 组件替换原生button
|
||||
- 使用三层架构样式系统
|
||||
- ✅ DanmakuWidget迁移到三层架构
|
||||
- 使用统一的 `Input` 和 `Button` 组件
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- 优化样式(gray → slate)
|
||||
- 添加文件头注释
|
||||
- ✅ AIChatWidget优化
|
||||
- 添加文件头注释
|
||||
- 优化关键样式(gray → slate)
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- ✅ RankingPanel迁移到三层架构
|
||||
- 添加文件头注释
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- 优化样式(gray → slate)
|
||||
- ✅ 分享页面和编辑页面优化
|
||||
- `app/share/[id]/page.tsx` 添加文件头注释,优化样式(gray → slate)
|
||||
- `app/edit/[id]/page.tsx` 添加文件头注释,优化样式(gray → slate)
|
||||
- 使用 `useThemeStyles()` Hook 和 `cardStyles.base`
|
||||
- ✅ UploadModal基础样式优化
|
||||
- 添加文件头注释
|
||||
- 导入 `useThemeStyles` 和统一组件
|
||||
- 所有 `gray` 系列替换为 `slate` 系列
|
||||
- 主要容器使用 `cardStyles.base`
|
||||
- 注意:由于后续会进行模板管理整合(需求14),暂不做深度重构,等模板管理整合时再一起优化
|
||||
- ✅ 低饱和度炫彩元素设计(1.7)
|
||||
- 在颜色层添加了 `accent` 配置(pink、purple、blue、cyan、emerald、orange、rainbow)
|
||||
- 每个颜色包含 bg、text、border、hover 四个属性
|
||||
- 浅色模式使用低饱和度背景(如 `bg-pink-50`)
|
||||
- 深色模式使用低透明度背景(如 `bg-pink-900/20`)
|
||||
- 在组合器中添加 `accentStyles` 导出
|
||||
- 更新 `Tag` 组件使用新的 `accentStyles`
|
||||
- ✅ 优化颜色对比度,确保可读性
|
||||
- ✅ 统一过渡动画效果
|
||||
- ✅ 测试所有页面的主题切换
|
||||
- ✅ 完全统一颜色系统:将所有 `gray-` 替换为 `slate-`(globals.css、登录页面、注册页面、EditMaterialModal、complete-profile、dingtalk-callback、RankingPanel、DanmakuWidget、AIChatWidget)
|
||||
- ✅ 删除管理页面中的外观设置Tab(与头像菜单重复)
|
||||
|
||||
**依赖关系**:建议在 1.4(首页UI统一)之后
|
||||
**影响范围**:全局
|
||||
|
||||
---
|
||||
|
||||
#### 1.7 低饱和度炫彩元素设计 ⭐⭐⭐
|
||||
**需求编号**:18
|
||||
**优先级**:中
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 遵循苹果设计语言
|
||||
- 提升视觉美感
|
||||
- 保持统一视觉方案
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 在颜色层(`ui-theme-colors-*.ts`)中添加低饱和度炫彩元素
|
||||
- 添加了 `accent` 颜色配置,包含:pink、purple、blue、cyan、emerald、orange、rainbow
|
||||
- 每个颜色包含:bg、text、border、hover 四个属性
|
||||
- 浅色模式使用低饱和度背景(如 `bg-pink-50`)
|
||||
- 深色模式使用低透明度背景(如 `bg-pink-900/20`)
|
||||
- ✅ 在组合器中添加 `accentStyles` 导出
|
||||
- ✅ 更新 `Tag` 组件使用新的 `accentStyles`
|
||||
- 所有颜色都使用低饱和度炫彩元素
|
||||
- hover 效果使用对应的 accent hover 样式
|
||||
- ✅ 确保深浅色模式都有对应的炫彩元素
|
||||
- ✅ 参考苹果设计语言,但遵循统一视觉方案
|
||||
|
||||
**依赖关系**:建议在 1.5(通用组件库)之后
|
||||
**影响范围**:全局UI元素(标签、徽章、强调色等)
|
||||
|
||||
---
|
||||
|
||||
### 优先级 2:数据一致性与核心功能(业务逻辑)
|
||||
|
||||
#### 2.1 用户信息修改后同步素材信息 ⭐⭐⭐⭐⭐
|
||||
**需求编号**:1
|
||||
**优先级**:最高(业务逻辑)
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 数据一致性是核心要求
|
||||
- 影响数据准确性
|
||||
- 已有 `useUserOperations` hook,可以在此扩展
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 在 `useUserOperations.updateUser` 中扩展逻辑
|
||||
- ✅ 当用户信息(用户名)修改时,同步更新该用户上传的所有素材的 `authorName`
|
||||
- ✅ 使用事务确保数据一致性(在 `app/api/admin/users/update/route.ts` 中实现)
|
||||
- ✅ 添加错误处理和回滚机制
|
||||
- ✅ 前端已处理 `materialsUpdated` 返回值并显示提示信息
|
||||
|
||||
**实现细节**:
|
||||
- API 端(`app/api/admin/users/update/route.ts`):当用户名变化时,使用事务同步更新该用户所有素材的 `authorName`
|
||||
- 前端(`app/admin/_hooks/useUserOperations.ts`):处理 `materialsUpdated` 返回值,显示"用户信息已更新,素材信息已同步"提示
|
||||
- Material 表中只有 `authorName` 字段与用户信息相关,部门信息不需要同步
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:用户管理、素材数据
|
||||
|
||||
---
|
||||
|
||||
#### 2.2 模板管理整合到上传和分类 ⭐⭐⭐⭐⭐
|
||||
**需求编号**:14
|
||||
**优先级**:最高(核心功能)
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 这是核心业务逻辑,将前后端通过模板串起来
|
||||
- 影响整个系统的数据架构
|
||||
- 需要严格检查模板管理面板逻辑
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 首页上传素材面板:将"素材类型"改为"素材模板",从模板管理拉取模板名称
|
||||
- 创建公共 API 端点 `/api/upload-templates` 获取启用的模板列表
|
||||
- 修改 UploadModal 组件,添加模板选择功能
|
||||
- ✅ 选择模板后,动态显示对应模板的字段
|
||||
- 支持多种字段类型:text、textarea、number、select、checkbox、radio、date
|
||||
- 根据字段的 `layoutWidth` 属性支持半行/整行布局
|
||||
- 自动验证必填字段
|
||||
- ✅ 系统管理中的分类管理:使用模板管理中的下拉元素文字做分类
|
||||
- 创建 `/api/template-categories` API 提取模板中的分类选项
|
||||
- 从所有启用的模板中提取 `addToCategory=true` 且 `type='select'` 的字段选项
|
||||
- 在分类管理界面显示模板分类卡片,提供编辑、添加、删除选项的功能
|
||||
- 创建 `/api/template-categories/[fieldKey]` API 管理特定字段的分类选项
|
||||
- 保留原有分类管理用于向后兼容
|
||||
- ✅ 添加"添加到分类"字段属性
|
||||
- 在数据库 schema 中添加 `addToCategory` 字段到 `UploadField` 模型
|
||||
- 在字段编辑器中添加"添加到分类"勾选框(仅 select 类型显示)
|
||||
- 更新所有相关类型定义和 API 以支持该字段
|
||||
- ✅ 修改上传逻辑:提交模板ID和字段值
|
||||
- 在 `metadata` 字段中存储模板字段值(JSON格式)
|
||||
- 在 `templateId` 字段中直接存储模板ID,方便查询
|
||||
- ⏳ 严格检查模板管理面板逻辑(待后续完善):
|
||||
- 字段验证(键名唯一性、类型检查)
|
||||
- 模板保存/更新逻辑
|
||||
- 模板删除时的依赖检查
|
||||
- 模板使用情况统计
|
||||
- ⏳ 确保数据一致性:模板修改后,已上传的素材如何处理(待后续完善)
|
||||
|
||||
**依赖关系**:无(但需要先确保模板管理逻辑完善)
|
||||
**影响范围**:上传功能、分类管理、模板管理
|
||||
|
||||
---
|
||||
|
||||
#### 2.3 首页类型筛选TAG系统 ⭐⭐⭐⭐
|
||||
**需求编号**:19
|
||||
**优先级**:高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 与模板管理整合相关,是核心功能的一部分
|
||||
- 提升用户体验,方便筛选
|
||||
- TAG样式需要符合整体风格
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 将筛选内的类型分页功能移到首页
|
||||
- ✅ 使用TAG形式显示,第一个TAG是"全部"
|
||||
- ✅ TAG数据来源:根据上传模板设置(模板管理中的下拉元素文字)
|
||||
- 创建 `/api/material-types` API 从模板管理中提取 `materialType` 字段的 `optionsStatic`
|
||||
- 首页从API动态获取素材类型列表,不再使用固定数组
|
||||
- ✅ TAG样式设计:好看大气,符合整体风格
|
||||
- 使用 `Tag` 组件和 `accentStyles` 实现低饱和度炫彩元素
|
||||
- 支持多种颜色(blue、green、orange、purple、pink、cyan)循环使用
|
||||
- 选中状态使用 ring 样式突出显示
|
||||
- 未选中状态使用透明度变化提供视觉反馈
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 支持点击切换筛选
|
||||
- ✅ 考虑低饱和度炫彩元素(需求18)
|
||||
|
||||
**依赖关系**:依赖 2.2(模板管理整合)
|
||||
**影响范围**:首页筛选功能
|
||||
|
||||
---
|
||||
|
||||
### 优先级 3:用户体验优化(交互与视觉)
|
||||
|
||||
#### 3.1 上传素材进度优化 ⭐⭐⭐⭐
|
||||
**需求编号**:2
|
||||
**优先级**:高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 修复已知问题,影响用户体验
|
||||
- 进度到100%后延迟关闭的问题
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 检查上传完成后的处理逻辑
|
||||
- ✅ 确保进度100%后立即显示完成状态
|
||||
- 添加 `uploadComplete` 状态标识上传完成
|
||||
- 进度条颜色从蓝色变为绿色
|
||||
- 显示"上传完成"和"✓ 上传成功,正在关闭..."提示
|
||||
- ✅ 添加短暂延迟(300ms)确保数据保存完成后再关闭弹窗
|
||||
- ✅ 优化用户体验:显示上传完成提示
|
||||
- 进度条显示为绿色
|
||||
- 显示完成提示文字
|
||||
- 自动关闭前给用户反馈
|
||||
|
||||
**依赖关系**:建议使用统一弹窗组件(1.1)
|
||||
**影响范围**:上传功能
|
||||
|
||||
---
|
||||
|
||||
#### 3.2 素材卡片信息优化 ⭐⭐⭐⭐
|
||||
**需求编号**:10
|
||||
**优先级**:高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 优化空间占用,提升视觉体验
|
||||
- 必须遵循统一UI原则
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 移除"作者:"文字,直接显示作者名字
|
||||
- ✅ 作者名字前显示头像(使用Avatar组件)
|
||||
- ✅ 移除文件名和文件大小显示
|
||||
- ✅ 上传时间叠在预览图右上角,默认隐藏,hover时显示
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 确保hover状态的可见性
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:首页素材卡片
|
||||
|
||||
---
|
||||
|
||||
#### 3.3 素材卡片标签系统 ⭐⭐⭐
|
||||
**需求编号**:11
|
||||
**优先级**:中
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- UI增强功能
|
||||
- 需要参考最高权限显示UI
|
||||
- 必须遵循统一视觉方案
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 在素材名称前添加标签(点赞榜、精品榜、下载榜等)
|
||||
- ✅ 创建了素材排行榜API(`/api/material-rankings`)
|
||||
- ✅ 实现了下载榜、点赞榜、精品榜三种排行榜
|
||||
- ✅ 使用Badge组件,样式统一美观
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 标签数据来源:排行榜数据
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:首页素材卡片
|
||||
|
||||
---
|
||||
|
||||
#### 3.4 素材卡片大小设置和瀑布流 ⭐⭐⭐⭐
|
||||
**需求编号**:9
|
||||
**优先级**:高
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 性能优化(动态加载)
|
||||
- 用户体验提升(自定义大小)
|
||||
- 考虑低性能NAS机器
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 添加横向滑块设置卡片大小(5个级别)
|
||||
- ✅ 实现瀑布流布局(使用CSS Columns)
|
||||
- ✅ 动态加载卡片数量(初始加载24个,滚动加载更多)
|
||||
- ✅ 使用 `IntersectionObserver` 实现懒加载
|
||||
- ✅ 考虑性能:图片懒加载、布局优化
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 用户偏好保存到localStorage
|
||||
|
||||
**依赖关系**:建议在 3.2(素材卡片信息优化)之后
|
||||
**影响范围**:首页素材展示
|
||||
|
||||
---
|
||||
|
||||
#### 3.5 多预览图上传 ⭐⭐⭐
|
||||
**需求编号**:3
|
||||
**优先级**:中
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 功能增强
|
||||
- 自动设置第一张为预览图
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 修改上传逻辑,支持多文件选择(最多5张)
|
||||
- ✅ 自动将第一张设置为预览图
|
||||
- ✅ 预览图列表显示,可单独删除
|
||||
- ✅ 多预览图URLs存储在metadata中(JSON格式)
|
||||
- ⏳ 在素材详情中展示所有预览图(待后续实现)
|
||||
|
||||
**依赖关系**:建议使用统一弹窗组件(1.1)
|
||||
**影响范围**:上传功能、素材详情
|
||||
|
||||
---
|
||||
|
||||
### 优先级 4:功能增强(次要功能)
|
||||
|
||||
#### 4.1 用户头像设置 ⭐⭐⭐
|
||||
**需求编号**:6
|
||||
**优先级**:中
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 功能增强
|
||||
- 钉钉头像抓取逻辑
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 创建了头像上传API(`/api/user/avatar`)
|
||||
- ✅ 用户可以在个人中心上传头像
|
||||
- ✅ 头像自动压缩和优化(最大5MB,压缩到200KB以内)
|
||||
- ✅ 更新了auth配置,在session中包含avatar信息
|
||||
- ✅ Header组件中的Avatar组件支持显示用户头像
|
||||
- ⏳ 钉钉登录用户头像自动同步(已在钉钉登录流程中实现)
|
||||
|
||||
**依赖关系**:建议使用统一弹窗组件(1.1)
|
||||
**影响范围**:用户管理、个人中心
|
||||
|
||||
---
|
||||
|
||||
#### 4.2 个人中心功能 ⭐⭐⭐
|
||||
**需求编号**:5
|
||||
**优先级**:中
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 功能增强
|
||||
- 用户个人信息管理
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 在点击头像弹出的菜单中增加"个人中心"选项
|
||||
- ✅ 创建个人中心页面(`/profile`)
|
||||
- ✅ 显示用户信息、上传的素材
|
||||
- ✅ 支持在个人中心上传和修改头像
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ⏳ 下载记录等功能(待后续实现)
|
||||
|
||||
**依赖关系**:建议使用统一弹窗组件(1.1)
|
||||
**影响范围**:用户菜单、个人中心页面
|
||||
|
||||
---
|
||||
|
||||
#### 4.3 菜单调整(系统设置改名为管理、外观设置移动) ⭐⭐
|
||||
**需求编号**:12、13
|
||||
**优先级**:低
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- UI调整,不影响核心功能
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 将"系统设置"改名为"管理",更换图标(使用滑块图标)
|
||||
- ✅ 将外观设置移动到点击头像弹出的菜单里
|
||||
- ✅ 添加图标(调色板图标)
|
||||
- ✅ 使用三层架构样式系统
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:用户菜单
|
||||
|
||||
---
|
||||
|
||||
#### 4.4 排行榜缩略按钮拖动 ⭐⭐
|
||||
**需求编号**:7
|
||||
**优先级**:低
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 交互优化,不影响核心功能
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 排行榜关闭后的缩略按钮可拖动
|
||||
- ✅ 位置根据排行榜悬浮窗关闭前的位置(关闭时保存面板位置)
|
||||
- ✅ 使用自定义拖动实现,保存位置到localStorage
|
||||
- ✅ 支持点击打开和拖动操作(拖动阈值5px区分点击和拖动)
|
||||
- ✅ 从localStorage恢复按钮位置
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:排行榜组件
|
||||
|
||||
---
|
||||
|
||||
#### 4.5 性能监控导出CSV ⭐⭐
|
||||
**需求编号**:4
|
||||
**优先级**:低
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 功能增强,非核心功能
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 在性能监控版添加导出CSV按钮
|
||||
- ✅ 使用原生JavaScript实现CSV生成(无需额外依赖)
|
||||
- ✅ 支持Excel正确显示中文(添加UTF-8 BOM)
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 文件名包含导出日期
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:性能监控页面
|
||||
|
||||
---
|
||||
|
||||
#### 4.6 系统监控数据点击跳转 ⭐⭐⭐
|
||||
**需求编号**:20
|
||||
**优先级**:中
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 提升数据可操作性
|
||||
- 方便管理员快速查看详细数据
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 用户总数、活跃用户、总素材数、本月新用户、本月新素材等数据项可点击
|
||||
- ✅ 点击后跳转到对应的数据显示页面(如用户管理、素材管理等)
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 添加hover效果(阴影、缩放、边框高亮)和点击提示(title属性)
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:系统监控页面
|
||||
|
||||
---
|
||||
|
||||
#### 4.7 系统监控当月上传趋势 ⭐⭐
|
||||
**需求编号**:21
|
||||
**优先级**:低
|
||||
**状态**:✅ 已完成
|
||||
**原因**:
|
||||
- 功能增强,提供更详细的数据展示
|
||||
|
||||
**实施要点**:
|
||||
- ✅ 在素材上传趋势卡片标题旁增加"当月上传"数字显示
|
||||
- ✅ 显示当月(最后一条数据)的上传数量
|
||||
- ✅ 使用紫色高亮显示,与趋势图颜色一致
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 使用 `useMemo` 计算当月上传数量
|
||||
|
||||
**依赖关系**:无
|
||||
**影响范围**:系统监控页面
|
||||
|
||||
---
|
||||
|
||||
## 📊 实施路线图
|
||||
|
||||
### 第一阶段:基础组件与UI统一(2-3周)
|
||||
1. ✅ 创建统一弹窗组件(1.1)- 已完成,包括替换所有原生弹窗
|
||||
2. ✅ 素材预览面板统一(1.2)- 已完成
|
||||
3. ✅ 首页素材展示响应式优化(1.3)- 已完成
|
||||
4. ✅ 首页UI统一到三层架构(1.4)- 完全完成
|
||||
5. ✅ 统一UI三层架构通用组件库(1.5)- 已完成(Badge、Tag、Button、Input、Card、Avatar)
|
||||
6. ✅ 深浅色主题适配优化(1.6)- 已完成(Header、ChangePasswordModal、DanmakuWidget、AIChatWidget、RankingPanel、分享页面、编辑页面、UploadModal基础优化)
|
||||
7. ✅ 低饱和度炫彩元素设计(1.7)- 已完成(添加accentStyles,更新Tag组件)
|
||||
|
||||
### 第二阶段:数据一致性与核心功能(2-3周)
|
||||
8. ✅ 用户信息修改后同步素材信息(2.1)- 已完成
|
||||
9. ✅ 模板管理整合(2.2)- 已完成(核心功能已实现,待完善模板管理面板逻辑检查)
|
||||
10. ✅ 首页类型筛选TAG系统(2.3)- 已完成
|
||||
|
||||
### 第三阶段:用户体验优化(2-3周)
|
||||
11. ✅ 上传素材进度优化(3.1)- 已完成
|
||||
12. ✅ 素材卡片信息优化(3.2)- 已完成
|
||||
13. ✅ 素材卡片大小设置和瀑布流(3.4)- 已完成
|
||||
14. ✅ 素材卡片标签系统(3.3)- 已完成
|
||||
15. ✅ 多预览图上传(3.5)- 已完成(核心功能已完成,素材详情展示待后续实现)
|
||||
|
||||
### 第四阶段:功能增强(1-2周)
|
||||
16. ✅ 用户头像设置(4.1)- 已完成
|
||||
17. ✅ 个人中心功能(4.2)- 已完成
|
||||
18. ✅ 菜单调整(4.3)- 已完成
|
||||
19. ✅ 排行榜缩略按钮拖动(4.4)- 已完成
|
||||
20. ✅ 性能监控导出CSV(4.5)- 已完成
|
||||
21. ✅ 系统监控数据点击跳转(4.6)- 已完成
|
||||
22. ✅ 系统监控当月上传趋势(4.7)- 已完成
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI设计原则
|
||||
|
||||
### 统一UI三层架构
|
||||
所有UI改动必须遵循:
|
||||
1. **基础层**(`lib/ui-styles-base.ts`):布局、大小、动画
|
||||
2. **颜色层**(`lib/ui-theme-colors-light.ts` / `dark.ts`):仅颜色配置
|
||||
3. **组合层**(`lib/ui-styles-composer.ts`):自动组合
|
||||
|
||||
### 使用规范
|
||||
```tsx
|
||||
// ✅ 正确:使用 useThemeStyles()
|
||||
import { useThemeStyles } from '@/lib/ui-styles'
|
||||
|
||||
export default function MyComponent() {
|
||||
const styles = useThemeStyles()
|
||||
return <div className={styles.cardStyles.base}>...</div>
|
||||
}
|
||||
|
||||
// ❌ 错误:直接使用 dark: 前缀
|
||||
<div className="bg-white dark:bg-slate-800">...</div>
|
||||
```
|
||||
|
||||
### 苹果设计语言参考
|
||||
- 圆角:`rounded-2xl`、`rounded-3xl`
|
||||
- 阴影:柔和阴影,使用 `shadow-lg`
|
||||
- 动画:流畅过渡,使用 `transition-all duration-300`
|
||||
- 间距:统一使用 `spacingStyles`
|
||||
- 但必须遵循**统一视觉方案**,不能偏离
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **性能优先**:所有改动必须考虑低性能NAS机器
|
||||
- 使用动态加载
|
||||
- 图片懒加载
|
||||
- 避免一次性加载大量数据
|
||||
|
||||
2. **稳定性优先**:
|
||||
- 所有数据操作使用事务
|
||||
- 完善的错误处理
|
||||
- 数据一致性检查
|
||||
|
||||
3. **易于部署**:
|
||||
- 不增加外部依赖(如必须,使用国内镜像)
|
||||
- 配置集中化
|
||||
- 向后兼容
|
||||
|
||||
4. **代码质量**:
|
||||
- 遵循现有代码规范
|
||||
- 使用 TypeScript 类型
|
||||
- 提取可复用逻辑到 Hook/工具函数
|
||||
|
||||
---
|
||||
|
||||
## 📝 更新记录
|
||||
|
||||
- 2025-01-XX:创建优化计划文档,重新规划优先级
|
||||
- 2025-01-XX:添加新需求(17-23),包括深浅色主题优化、低饱和度炫彩元素、首页TAG系统、系统监控功能增强等
|
||||
- 2025-11-25:更新已完成状态
|
||||
- ✅ 颜色系统完全统一:所有页面和组件文件的 `gray-` 已替换为 `slate-`
|
||||
- ✅ 排行榜缩略按钮拖动功能已完成
|
||||
- ✅ 系统监控功能增强(CSV导出、数据点击跳转、当月上传趋势)已完成
|
||||
- ✅ 所有核心功能和用户体验优化已完成
|
||||
|
||||
@ -1,692 +0,0 @@
|
||||
# Admin 页面优化建议和问题修复记录
|
||||
|
||||
## ✅ 2025-11-25 · 排行榜缩略按钮拖动功能
|
||||
|
||||
### 排行榜缩略按钮拖动
|
||||
- ✅ 实现排行榜关闭后缩略按钮的拖动功能
|
||||
- ✅ 关闭面板时自动保存面板位置作为缩略按钮位置
|
||||
- ✅ 使用自定义拖动实现,区分点击和拖动操作(拖动阈值5px,时间阈值200ms)
|
||||
- ✅ 按钮位置保存到localStorage,支持位置恢复
|
||||
- ✅ 支持点击打开和拖动操作,提升用户体验
|
||||
|
||||
**技术实现**:
|
||||
- 添加 `buttonPosition`、`isButtonDragging`、`buttonDragOffset` 状态和 `buttonRef`
|
||||
- 实现 `handleButtonMouseDown` 和拖动事件处理(`useEffect` 监听 `isButtonDragging`)
|
||||
- 关闭面板时保存位置:`handleClose` 中保存 `position` 到 `buttonPosition` 和 localStorage
|
||||
- 从localStorage恢复按钮位置(在 `useEffect` 中)
|
||||
- 使用 `clickStartRef` 记录点击开始位置和时间,区分点击和拖动
|
||||
- 使用 `pointer-events-none` 防止SVG图标干扰拖动
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2025-11-25 · 颜色系统完全统一与功能优化
|
||||
|
||||
### 颜色系统完全统一(主要页面)
|
||||
- ✅ 优化 `app/globals.css`:将所有 `gray-` 替换为 `slate-`(按钮、输入框、卡片样式)
|
||||
- ✅ 优化登录页面(`app/login/page.tsx`):将所有 `gray-` 替换为 `slate-`,确保深浅色模式适配
|
||||
- ✅ 优化注册页面(`app/register/page.tsx`):将所有 `gray-` 替换为 `slate-`,确保深浅色模式适配
|
||||
- ✅ 优化素材编辑弹窗(`components/EditMaterialModal.tsx`):将所有 `gray-` 替换为 `slate-`,确保深浅色模式适配
|
||||
- ✅ 优化完善个人信息页面(`app/complete-profile/page.tsx`):将所有 `gray-` 替换为 `slate-`,确保深浅色模式适配
|
||||
- ✅ 优化钉钉回调页面(`app/dingtalk-callback/page.tsx`):将所有 `gray-` 替换为 `slate-`,确保深浅色模式适配
|
||||
|
||||
### ✅ 颜色系统完全统一(组件文件)
|
||||
- ✅ **components/RankingPanel.tsx** - 已将所有 `gray-` 替换为 `slate-`(图标、背景、边框、文本等)
|
||||
- ✅ **components/DanmakuWidget.tsx** - 已将所有 `gray-` 替换为 `slate-`(按钮背景)
|
||||
- ✅ **components/AIChatWidget.tsx** - 已将所有 `gray-` 替换为 `slate-`(容器、渐变、输入框、占位符等)
|
||||
|
||||
**完成**:所有组件文件现在完全使用 `slate` 系列颜色,确保视觉一致性和深浅色模式完美适配。
|
||||
|
||||
### 管理页面功能优化
|
||||
- ✅ 删除管理页面中的"外观设置"Tab(与头像菜单中的外观设置重复)
|
||||
- ✅ 从 `AdminTab` 类型中移除 `'settings'` 选项
|
||||
- ✅ 移除 `AppearanceSettingsPanel` 在管理页面的导入和使用
|
||||
- ✅ 外观设置现在仅在头像下拉菜单中可用,避免功能重复
|
||||
|
||||
### 数据库迁移与清理
|
||||
- ✅ 执行数据库迁移:添加素材表的所有可选字段(brandName、seriesName、skuCode等)
|
||||
- ✅ 修复迁移脚本:将多个SQL命令分开执行,避免Prisma执行错误
|
||||
- ✅ 素材数据和记录清理:删除所有素材记录和关联文件
|
||||
- ✅ 更新清理API:添加素材清理功能到 `/api/admin/cleanup-database`
|
||||
|
||||
**技术实现**:
|
||||
- 颜色统一:全局搜索替换 `gray-*` → `slate-*`,确保所有组件在深浅色模式下都使用统一的颜色系统
|
||||
- 功能去重:删除管理页面中的外观设置Tab,保留头像菜单中的外观设置入口
|
||||
- 数据库迁移:使用逐个执行SQL命令的方式,避免Prisma的"cannot insert multiple commands"错误
|
||||
- 清理功能:在清理API中添加素材文件删除和记录删除功能
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2025-11-25 · Bug修复与UI优化
|
||||
|
||||
### 上传文件500错误修复
|
||||
- ✅ 修复视频类型预览图处理逻辑,添加空值检查(`previewImage && previewImage.size > 0`)
|
||||
- ✅ 优化预览图验证逻辑,支持多预览图场景(优先使用`previewImages`,向后兼容`previewImage`)
|
||||
- ✅ 确保所有错误都被正确捕获和返回,提供更详细的错误信息
|
||||
|
||||
### 个人中心返回按钮
|
||||
- ✅ 添加返回按钮,使用统一UI样式系统
|
||||
- ✅ 添加Header组件,保持页面一致性
|
||||
- ✅ 返回按钮使用`router.back()`实现导航
|
||||
|
||||
### 用户管理弹窗统一UI
|
||||
- ✅ 将`UserEditModal`改为使用统一Modal组件
|
||||
- ✅ 将`TransferSuperAdminModal`改为使用统一Modal组件
|
||||
- ✅ 将`InvitationsTab`的创建邀请弹窗改为使用统一Modal组件
|
||||
- ✅ 所有弹窗现在都有毛玻璃效果(`backdrop-blur-xl`)和统一阴影
|
||||
|
||||
### 统一UI弹窗阴影效果优化
|
||||
- ✅ 在`lib/ui-theme-colors-light.ts`中增强浅色模式阴影:`shadow-[0_35px_110px_rgba(15,23,42,0.25)] shadow-slate-900/20`
|
||||
- ✅ 在`lib/ui-theme-colors-dark.ts`中增强深色模式阴影:`shadow-[0_45px_130px_rgba(0,0,0,0.75)] shadow-black/50`
|
||||
- ✅ 确保所有使用统一Modal组件的弹窗都有阴影效果
|
||||
- ✅ 从`baseModalStyles.content`中移除重复的阴影定义,统一在主题颜色中定义
|
||||
|
||||
**技术实现**:
|
||||
- 上传错误修复:添加空值检查,优化验证逻辑
|
||||
- 个人中心:使用`useRouter`的`back()`方法,添加Header组件
|
||||
- 弹窗统一:将所有自定义弹窗改为使用`components/ui/Modal`组件
|
||||
- 阴影优化:在主题颜色配置中增强阴影效果,确保所有弹窗都有阴影
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2025-11-25 · 用户信息同步功能
|
||||
|
||||
### 用户信息同步到素材
|
||||
- ✅ 管理员修改用户信息后,该用户上传的所有素材信息自动同步更新
|
||||
- ✅ 当用户名发生变化时,自动更新该用户所有素材的`authorName`字段
|
||||
- ✅ 使用数据库事务确保数据一致性
|
||||
- ✅ 前端显示同步成功提示("用户信息已更新,素材信息已同步")
|
||||
|
||||
**技术实现**:
|
||||
- 在`/api/admin/users/update` API中实现同步逻辑
|
||||
- 使用Prisma事务(`$transaction`)确保用户更新和素材更新原子性
|
||||
- 检测用户名变化:`username !== undefined && username !== oldUser.username`
|
||||
- 使用`updateMany`批量更新该用户的所有素材:`tx.material.updateMany({ where: { userId }, data: { authorName: username } })`
|
||||
- 返回`materialsUpdated`标志,前端根据此标志显示相应提示
|
||||
|
||||
**代码位置**:
|
||||
- 后端:`app/api/admin/users/update/route.ts`(第122-128行)
|
||||
- 前端:`app/admin/_hooks/useUserOperations.ts`(第75-99行)
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2025-11-25 · 性能监控导出CSV功能
|
||||
|
||||
### CSV导出功能
|
||||
- ✅ 在系统监控页面添加"导出CSV"按钮
|
||||
- ✅ 导出包含所有监控数据:
|
||||
- 系统概览(总用户数、活跃用户、总素材数、本月新用户、本月新素材)
|
||||
- 用户增长趋势(最近12个月/30天)
|
||||
- 素材上传趋势(最近12个月/30天)
|
||||
- 部门分布统计
|
||||
- 权限分布统计
|
||||
- 最近活动记录(最近10条)
|
||||
- 系统信息(站点名称、注册功能状态、数据库状态、运行时间等)
|
||||
- ✅ 支持Excel正确显示中文(添加UTF-8 BOM)
|
||||
- ✅ 文件名包含导出日期,格式:`系统监控数据_YYYY-MM-DD.csv`
|
||||
- ✅ 同时优化了MonitorTab组件和独立监控页面(app/admin/monitor/page.tsx)
|
||||
|
||||
**技术实现**:
|
||||
- 使用原生JavaScript实现CSV生成,无需额外依赖库
|
||||
- 自动处理中文和特殊字符(使用双引号包裹)
|
||||
- 使用Blob API创建下载文件
|
||||
- 添加UTF-8 BOM(\uFEFF)确保Excel正确显示中文
|
||||
- 导出成功后显示toast提示
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2025-11-25 · 系统监控交互增强
|
||||
|
||||
### 系统监控数据点击跳转
|
||||
- ✅ 总用户数、活跃用户、本月新用户可点击跳转到用户管理页面
|
||||
- ✅ 总素材数、本月新素材可点击跳转到素材列表页面
|
||||
- ✅ 添加了hover效果(阴影、缩放、边框高亮)和点击提示(title属性)
|
||||
- ✅ 同时优化了MonitorTab组件和独立监控页面(app/admin/monitor/page.tsx)
|
||||
|
||||
### 当月上传趋势显示
|
||||
- ✅ 在素材上传趋势卡片标题旁增加"当月上传"数字显示
|
||||
- ✅ 显示当月(最后一条数据)的上传数量
|
||||
- ✅ 使用紫色高亮显示,与趋势图颜色一致
|
||||
|
||||
**技术实现**:
|
||||
- 使用`useRouter`实现页面跳转
|
||||
- 卡片添加`cursor-pointer`和hover样式
|
||||
- 使用`useMemo`计算当月上传数量(取数组最后一项)
|
||||
|
||||
---
|
||||
|
||||
## ✅ 已完成的修复
|
||||
|
||||
### 1. 类型定义统一化
|
||||
- ✅ 创建了 `types.ts` 统一管理所有类型定义
|
||||
- ✅ 移除了 `page.tsx` 中的重复类型定义(User, Material, Category, Scene, Platform, Invitation, UserTreeNode, DepartmentNode, DepartmentUserGrouping)
|
||||
- ✅ 移除了 `TemplateManager.tsx` 中的重复 `ToastFn` 定义
|
||||
- ✅ 添加了 `AdminTab` 类型统一管理 Tab 类型
|
||||
- ✅ 添加了 `DashboardData` 接口用于监控仪表板数据
|
||||
- ✅ 修复了 `TemplateManager.tsx` 中 `DragHoverState` 的类型错误(fallback 对象的 `position` 属性需要明确类型为 `'before' | 'after'`)
|
||||
- ✅ 修复了 `TemplateFieldsEditor.tsx` 中 `onFieldDragOver` 回调签名,添加缺失的 `surface` 参数
|
||||
- ✅ 修复了 `UsersTab.tsx` 中 `showTransferButton` 在使用前未定义的问题,将其改为 `useMemo` 并移到使用前定义
|
||||
|
||||
### 2. UI 样式统一化 - 三层架构重构
|
||||
- ✅ 创建了**三层架构**的样式系统:
|
||||
- **基础层**:`lib/ui-styles-base.ts` - 包含布局、大小、动画等非颜色样式
|
||||
- **颜色层**:`lib/ui-theme-colors-light.ts` / `dark.ts` - 仅包含颜色配置
|
||||
- **组合层**:`lib/ui-styles-composer.ts` - 自动组合基础样式和颜色主题
|
||||
- **导出层**:`lib/ui-styles-light.ts` / `dark.ts` - 最终导出的样式(由组合器生成)
|
||||
- ✅ 创建了 `lib/hooks/useThemeStyles.ts` - 主题感知的样式 Hook
|
||||
- `useThemeStyles()` - 根据当前主题自动返回对应的样式配置
|
||||
- `useTheme()` - 获取当前主题状态
|
||||
- ✅ 保持了向后兼容性:
|
||||
- 原有的 `lib/ui-styles.ts` 仍然保留(包含 `dark:` 前缀的样式)
|
||||
- 新组件推荐使用 `useThemeStyles()` Hook
|
||||
- 旧组件可以继续使用原有导入,逐步迁移
|
||||
|
||||
**核心优势:**
|
||||
- ✅ **统一视觉设计**:修改布局、大小、动画时,只需修改 `ui-styles-base.ts` 一处,浅色和深色模式自动同步
|
||||
- ✅ **独立颜色适配**:颜色配置完全分离,可以独立调整浅色或深色模式的颜色
|
||||
- ✅ **易于维护**:清晰的职责分离,修改时不会遗漏或出错
|
||||
- ✅ **类型安全**:TypeScript 类型检查确保配置正确
|
||||
|
||||
**修改指南:**
|
||||
- 修改布局/大小/动画 → 修改 `lib/ui-styles-base.ts`(一处修改,两套主题自动同步)
|
||||
- 修改浅色模式颜色 → 修改 `lib/ui-theme-colors-light.ts`
|
||||
- 修改深色模式颜色 → 修改 `lib/ui-theme-colors-dark.ts`
|
||||
|
||||
**架构文档:** 详见 `docs/guides/UI_STYLES_ARCHITECTURE.md`
|
||||
|
||||
**使用示例:**
|
||||
```tsx
|
||||
'use client'
|
||||
import { useThemeStyles } from '@/lib/ui-styles'
|
||||
|
||||
export default function MyComponent() {
|
||||
const styles = useThemeStyles()
|
||||
|
||||
return (
|
||||
<div className={styles.cardStyles.base}>
|
||||
<button className={styles.buttonStyles.primary}>点击</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**迁移进度:**
|
||||
- ✅ 基础 UI 组件已迁移:
|
||||
- `LoadingState.tsx` - 使用 `useThemeStyles()` 和 `useTheme()`
|
||||
- `ErrorState.tsx` - 使用 `useThemeStyles()` 和 `useTheme()`
|
||||
- `EmptyState.tsx` - 使用 `useTheme()` 根据主题选择文字颜色
|
||||
- ✅ Tab 组件迁移(全部完成):
|
||||
- `MaterialsTab.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- `CategoriesTab.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- `InvitationsTab.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- `DepartmentsTab.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- `UsersTab.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- `MonitorTab.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- ✅ 其他组件迁移(已完成):
|
||||
- `TemplateManager.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- `page.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
- `AppearanceSettingsPanel.tsx` - 完全迁移到新样式系统,移除了所有 `dark:` 前缀
|
||||
|
||||
### 3. UI 样式统一化(历史)
|
||||
- ✅ 修复了 `card-apple` 类名,改为使用 `cardStyles.base`
|
||||
- ✅ 统一了边框颜色:`border-gray-*` → `border-slate-*`(与设计系统一致)
|
||||
- ✅ Admin 目录中的 `text-gray-* / bg-gray-* / border-gray-*` 类已全部替换为 `slate` 系列,浅色与深色模式的视觉基调保持一致
|
||||
|
||||
## 🔍 发现的问题和建议
|
||||
|
||||
### 1. 代码结构问题
|
||||
|
||||
#### 问题:`page.tsx` 文件过大(3795 行)
|
||||
**影响:**
|
||||
- 难以维护和调试
|
||||
- 代码复用性差
|
||||
- 性能可能受影响(组件过大)
|
||||
|
||||
**建议:**
|
||||
- ✅ 已创建 `MonitorTab.tsx` 组件
|
||||
- ✅ 已拆分 `UsersTab.tsx`(用户管理)
|
||||
- ✅ 已拆分 `DepartmentsTab.tsx`(架构管理)
|
||||
- ⏳ 继续拆分其他 Tab 组件:
|
||||
- ✅ `CategoriesTab.tsx` - 分类管理
|
||||
- ✅ `InvitationsTab.tsx` - 邀请管理
|
||||
- ✅ `MaterialsTab.tsx` - 素材管理
|
||||
- ✅ `TemplateManager.tsx`:已提取 Header / Built-in Grid / Custom Templates 等 UI 子组件,为后续拆出编辑器面板铺路
|
||||
- ✅ TemplateManager 编辑弹窗:基础信息、预设选择、JSON 高级配置抽象为 `TemplateBasicsForm` + `TemplateAdvancedConfigPanel`,编辑逻辑逐步下沉
|
||||
- ✅ TemplateManager 字段区、底部操作:`TemplateFieldsEditor` 承载字段配置 + 拖拽 UI,`TemplateEditorFooter` 统一处理提示与按钮状态
|
||||
- ✅ TemplateManager 预览面板:`TemplatePreviewPanel` 独立封装上传预览 UI 与 DnD 状态,主组件仅处理回调
|
||||
|
||||
#### 问题:状态管理分散
|
||||
**当前状态:**
|
||||
- 每个 Tab 的状态都在主组件中管理
|
||||
- 状态之间可能存在不必要的依赖
|
||||
|
||||
**建议:**
|
||||
- ✅ `UsersTab` / `DepartmentsTab` 已将本地 UI 状态与交互逻辑下沉到各自组件
|
||||
- 只保留共享状态在主组件(如 `activeTab`, `toast`)
|
||||
|
||||
### 4. UI 统一性问题
|
||||
|
||||
#### 问题:部分地方仍使用旧的样式类
|
||||
**发现:**
|
||||
- ✅ 基础文字/背景/边框色已统一为 `slate` 系列(含浅色与深色)
|
||||
- ✅ 深浅色样式已分离为两套独立配置,解决适配问题
|
||||
- ⏳ 部分按钮样式未使用 `buttonStyles.*`(建议逐步迁移到 `useThemeStyles()`)
|
||||
- ⏳ 部分输入框仍未使用 `inputStyles.*`(建议逐步迁移到 `useThemeStyles()`)
|
||||
|
||||
**建议:**
|
||||
- 全局搜索替换 `gray-` → `slate-`(在 admin 目录中)
|
||||
- ⏳ 确保所有按钮都使用 `buttonStyles.*`(InvitationsTab、CategoriesTab、MaterialsTab、UsersTab、DepartmentsTab ✅;其它 Tab 持续中)
|
||||
- ⏳ 确保所有输入框都使用 `inputStyles.*`(DepartmentsTab 重命名输入已调整)
|
||||
- ✅ 补充 `buttonStyles.success` 以覆盖绿色状态按钮场景
|
||||
- ✅ 新增 `buttonModifiers`(compact / compactXs / icon / block)辅助类,方便统一尺寸与布局
|
||||
- ✅ MaterialsTab 搜索操作、卡片操作按钮已统一到 `buttonStyles + buttonModifiers`,并将 `<img>` 替换为 `next/image`
|
||||
- ✅ UsersTab 搜索清空与用户操作按钮统一引用 `buttonStyles + buttonModifiers`
|
||||
- ✅ DepartmentsTab 搜索清空、操作按钮接入 `buttonStyles + buttonModifiers`; 新增 `inputModifiers` 并应用到重命名输入
|
||||
- ✅ TemplateManager 顶部搜索、预制/自定义模板动作按钮接入设计系统;新增 `buttonModifiers.pill`
|
||||
- ✅ TemplateManager 基础信息 / JSON 配置表单统一使用 `inputStyles + inputModifiers`,并抽离复用组件
|
||||
- ✅ TemplateManager 字段配置 / 底部操作按钮接入设计系统(字段编辑区统一控件样式,弹窗底部按钮使用 `buttonStyles.secondary/primary` + `buttonModifiers.compact`)
|
||||
- ✅ TemplateManager 预览面板 UI 与拖拽交互抽象为 `TemplatePreviewPanel`,复用设计系统控件
|
||||
- ✅ 清理 TemplatePreviewPanel 与 `page.tsx` 残留的 `gray-*` 渐变/分隔线/焦点样式,统一替换为 `slate-*` 调色
|
||||
- ✅ `page.tsx` 移除历史 Invitations UI、状态与数据请求,统一由 `InvitationsTab` 自主管理
|
||||
- ✅ `page.tsx` 移除重复的监控仪表板代码,统一使用 `MonitorTab` 组件
|
||||
|
||||
### 3. 逻辑优化建议
|
||||
|
||||
#### 问题:数据获取逻辑重复
|
||||
**当前:**
|
||||
- ✅ `useDataFetch` Hook 已抽离 `loadUsersData` / `fetchDepartments` / `fetchTabData`,主页面只关心状态驱动
|
||||
- ⏳ 更进一步的缓存(React Query / SWR)尚未落地
|
||||
|
||||
**建议:**
|
||||
- 后续可引入 React Query 或 SWR 进行跨 Tab 缓存与自动刷新
|
||||
- 或者在各 Tab 内使用独立数据源,减少主页面耦合
|
||||
|
||||
#### 问题:权限检查分散
|
||||
**当前:**
|
||||
- 权限检查在多个地方重复
|
||||
- `canManageUser` 等函数在主组件中定义
|
||||
|
||||
**建议:**
|
||||
- ✅ 已创建 `usePermissions` Hook,统一 `canManageUser` / 超管判定等权限逻辑
|
||||
- 后续可将其他权限相关逻辑继续提取到工具函数或 Hook 中
|
||||
|
||||
### 4. 性能优化建议
|
||||
|
||||
#### 问题:大量 useMemo 和 useCallback
|
||||
**当前:**
|
||||
- 主组件中有很多 `useMemo` 和 `useCallback`
|
||||
- 某些计算可能不需要缓存
|
||||
|
||||
**建议:**
|
||||
- ✅ 审查了每个 `useMemo` 和 `useCallback` 的必要性
|
||||
- `departmentCollator` - 使用 `useMemo` 合理(创建 Intl.Collator 对象)
|
||||
- `userLookup` - 使用 `useMemo` 合理(从数组创建 Map,避免重复计算)
|
||||
- `fetchData`, `renameDepartment`, `createDepartment`, `deleteDepartment`, `findDepartmentManagers` - 使用 `useCallback` 合理(作为 props 传递给子组件)
|
||||
- ✅ 创建了 `_utils/api.ts` 统一处理 API 错误,减少了重复的错误处理代码
|
||||
- ⏳ 将复杂的计算逻辑移到子组件中
|
||||
- ✅ 使用 React.memo 优化子组件渲染(`UserTree` 已实现)
|
||||
|
||||
#### 问题:模态框状态管理
|
||||
**当前:**
|
||||
- ✅ 分类/场景/平台新增、素材编辑相关模态已移交给各自 Tab 组件,`page.tsx` 清理全部遗留状态与 UI
|
||||
- ✅ 新增 `useModalState` Hook(`app/admin/_hooks/useModalState.ts`),统一管理用户编辑/转移最高权限模态的开合与状态,移除散落的 `showPasswordModal`、`editingUser` 布尔控制
|
||||
- ⏳ 用户信息编辑、转移最高权限 UI 仍托管在 `page.tsx`,可进一步下放到 `UsersTab`
|
||||
|
||||
**建议:**
|
||||
- 为剩余模态编写统一的模态管理 hook
|
||||
- 或者继续将模态迁移到对应的 Tab/组件内部
|
||||
|
||||
### 5. 代码质量建议
|
||||
|
||||
#### 建议:添加错误边界
|
||||
- ✅ 新增 `TabErrorBoundary` 组件(`app/admin/_components/TabErrorBoundary.tsx`),并在 `page.tsx` 为各个 Tab 包裹,提供统一的 fallback + 重试
|
||||
- ⏳ 可进一步将 `TabErrorBoundary` 提炼到全局组件库,或为特定 Tab 定制更详细的错误提示
|
||||
|
||||
#### 建议:添加加载状态统一管理
|
||||
- ✅ 创建统一的加载状态组件(`LoadingState`、`ErrorState`、`EmptyState`)
|
||||
- ✅ 统一错误提示样式,替换所有重复的加载/错误 UI
|
||||
- ✅ `MonitorTab`、`MaterialsTab`、`page.tsx` 已统一使用新的状态组件
|
||||
|
||||
#### 建议:提取公共逻辑
|
||||
- ✅ 创建 `hooks/useToast.ts` 统一管理 Toast(`page.tsx` 已接入)
|
||||
- ✅ 创建 `hooks/useDataFetch.ts` 统一管理数据获取
|
||||
- ✅ 创建 `_utils/api.ts` 统一处理 API 错误响应和类型安全
|
||||
- `parseErrorResponse()` - 安全解析错误响应
|
||||
- `parseSuccessResponse()` - 安全解析成功响应
|
||||
- `apiRequest()` - 统一的 API 请求函数
|
||||
- `getErrorMessage()` - 获取错误消息的辅助函数
|
||||
- ✅ 提取表单验证逻辑到工具函数
|
||||
- 创建了 `_utils/validators.ts` 统一管理表单验证
|
||||
- `validateNotEmpty()` - 验证非空字符串
|
||||
- `validateDepartmentName()` - 验证部门名称
|
||||
- `validateEmail()` - 验证邮箱格式
|
||||
- `validateFieldKey()` - 验证字段键名(标识符规则)
|
||||
- `validateUniqueKeys()` - 验证字段键名唯一性
|
||||
- `validateTemplateName()` - 验证模板名称
|
||||
- `validatePassword()` - 验证密码
|
||||
- `combineValidationResults()` - 组合多个验证结果
|
||||
- `page.tsx` 和 `TemplateManager.tsx` 已使用新的验证函数
|
||||
- ✅ 创建格式化工具函数
|
||||
- 创建了 `_utils/formatters.ts` 统一管理格式化逻辑
|
||||
- `formatDateTime()` - 格式化日期时间(完整格式)
|
||||
- `formatDate()` - 格式化日期(仅日期部分)
|
||||
- `formatTime()` - 格式化时间(仅时间部分)
|
||||
- `formatDateTimeShort()` - 格式化日期时间(简短格式)
|
||||
- `formatDateTimeLocale()` - 使用本地化格式格式化日期时间
|
||||
- `formatRelativeTime()` - 格式化相对时间
|
||||
- `formatFileSize()` - 格式化文件大小
|
||||
- `formatNumber()` - 格式化数字(添加千位分隔符)
|
||||
- `formatPercent()` - 格式化百分比
|
||||
- `MonitorTab.tsx`、`InvitationsTab.tsx`、`UserEditModal.tsx`、`TemplateManager.tsx` 已使用新的格式化函数
|
||||
- ✅ 提取用户操作逻辑到 Hook
|
||||
- 创建了 `_hooks/useUserOperations.ts` 统一管理用户相关操作
|
||||
- `updateUser()` - 更新用户信息
|
||||
- `deleteUser()` - 删除用户
|
||||
- `toggleDownload()` - 切换下载权限
|
||||
- `toggleActive()` - 切换激活状态
|
||||
- `setRole()` - 设置用户角色
|
||||
- `transferSuperAdmin()` - 转移最高权限
|
||||
- `page.tsx` 已使用新的 Hook,代码更简洁
|
||||
- ✅ 提取部门操作逻辑到 Hook
|
||||
- 创建了 `_hooks/useDepartmentOperations.ts` 统一管理部门相关操作
|
||||
- `createDepartment()` - 创建部门
|
||||
- `deleteDepartment()` - 删除部门
|
||||
- `renameDepartment()` - 重命名部门
|
||||
- `page.tsx` 已使用新的 Hook,移除了重复的部门操作代码,代码更简洁
|
||||
- 创建了 `_utils/validators.ts` 统一管理表单验证
|
||||
- `validateNotEmpty()` - 验证非空字符串
|
||||
- `validateDepartmentName()` - 验证部门名称
|
||||
- `validateEmail()` - 验证邮箱格式
|
||||
- `validateFieldKey()` - 验证字段键名(标识符规则)
|
||||
- `validateUniqueKeys()` - 验证字段键名唯一性
|
||||
- `validateTemplateName()` - 验证模板名称
|
||||
- `validatePassword()` - 验证密码
|
||||
- `combineValidationResults()` - 组合多个验证结果
|
||||
- `page.tsx` 和 `TemplateManager.tsx` 已使用新的验证函数
|
||||
|
||||
### 6. 类型安全建议
|
||||
|
||||
#### 问题:使用 `any` 类型
|
||||
**发现:**
|
||||
- `session?.user as any` 多处使用
|
||||
- `dashboardData` 使用 `any` 类型
|
||||
|
||||
**建议:**
|
||||
- ✅ 定义完整的 Session 类型(`SessionUser` 接口)
|
||||
- ✅ 使用 `DashboardData` 类型替代 `any`(已在 types.ts 中定义)
|
||||
- ✅ 添加类型断言函数(`getSessionUser`)
|
||||
- ✅ 定义 API 响应类型(`ApiErrorResponse`, `ApiSuccessResponse`)
|
||||
- ✅ 移除 `page.tsx` 中所有 `as any` 使用
|
||||
- ✅ 更新 `AppearanceSettingsPanel` 使用 `SessionUser` 类型
|
||||
|
||||
### 7. 可访问性建议
|
||||
|
||||
#### 建议:添加 ARIA 标签
|
||||
- ✅ 为 Tab 导航添加了完整的 ARIA 标签(`role="tablist"`, `role="tab"`, `role="tabpanel"`)
|
||||
- ✅ 为所有 Tab 按钮添加了 `aria-selected`, `aria-controls`, `id` 属性
|
||||
- ✅ 为返回主页按钮和关闭 Toast 按钮添加了 `aria-label`
|
||||
- ✅ 为 Toast 通知添加了 `role="status"` 和 `aria-live="polite"`
|
||||
- ✅ 添加了键盘导航支持(Enter 和 Space 键)
|
||||
- ✅ 添加了焦点样式(`focus:outline-none focus:ring-2`)
|
||||
- ⏳ 继续为其他交互元素添加 ARIA 标签和键盘支持
|
||||
|
||||
### 8. 测试建议
|
||||
|
||||
#### 建议:添加单元测试
|
||||
- 为工具函数添加测试
|
||||
- 为权限检查逻辑添加测试
|
||||
- 为数据转换逻辑添加测试
|
||||
|
||||
## 📋 下一步行动计划
|
||||
|
||||
> **注意**:详细的优化计划和优先级已移至 `优化计划与优先级.md`,请参考该文档进行后续开发。
|
||||
|
||||
### 当前阶段:基础组件与UI统一
|
||||
|
||||
**优先级 1:基础组件与UI统一(核心基础设施)**
|
||||
1. ✅ 创建统一弹窗组件(`components/ui/Modal.tsx`)
|
||||
- ✅ 使用三层架构设计(基础层、颜色层、组合层)
|
||||
- ✅ 提供 `useModal` Hook(`hooks/useModal.ts`)
|
||||
- ✅ 支持多种尺寸(small, medium, large, xlarge, fullscreen)
|
||||
- ✅ 支持ESC键关闭、点击遮罩关闭
|
||||
- ✅ 完整的ARIA标签和键盘导航支持
|
||||
- ✅ 替换所有浏览器原生弹窗(confirm/alert/prompt)
|
||||
- ✅ 创建了 ConfirmDialog、AlertDialog、PromptDialog 组件
|
||||
- ✅ 创建了 DialogProvider 和 useDialog Hook
|
||||
- ✅ 替换了所有 admin 组件、首页、分享页面中的原生弹窗
|
||||
2. ✅ 素材预览面板统一
|
||||
- ✅ 创建了 MaterialPreviewModal 组件
|
||||
- ✅ 使用统一弹窗组件
|
||||
- ✅ 统一退出逻辑(ESC键、点击遮罩、关闭按钮)
|
||||
- ✅ 使用三层架构样式系统
|
||||
3. ✅ 首页素材展示响应式优化
|
||||
- ✅ 修复卡片比例固定问题(使用 `aspect-[3/4]` 替代固定高度 `h-[400px]`)
|
||||
- ✅ 添加容器边距(`px-4 sm:px-6 lg:px-8`)避免卡片贴到浏览器边界
|
||||
- ✅ 使用三层架构样式系统(`useThemeStyles()` 和 `cardStyles.base`)
|
||||
- ✅ 优化图片 sizes 属性,提升响应式性能
|
||||
- ✅ 统一使用 `slate` 系列颜色,确保深浅色模式适配
|
||||
|
||||
4. ✅ 首页UI统一到三层架构(完全完成)
|
||||
- ✅ 迁移了主要按钮样式(上传按钮、筛选按钮)
|
||||
- ✅ 迁移了搜索输入框样式
|
||||
- ✅ 迁移了筛选弹窗中的所有按钮样式
|
||||
- ✅ 迁移了筛选弹窗容器样式
|
||||
- ✅ 迁移了筛选弹窗关闭按钮样式
|
||||
- ✅ 将所有 `gray` 系列替换为 `slate` 系列
|
||||
- ✅ 统一文本颜色(`text-gray-*` → `text-slate-*`)
|
||||
- ✅ 统一背景颜色(`bg-gray-*` → `bg-slate-*`)
|
||||
- ✅ 统一边框颜色(`border-gray-*` → `border-slate-*`)
|
||||
- ✅ 统一分页按钮样式
|
||||
- ✅ 统一分享弹窗样式
|
||||
- ✅ 使用 `Tag` 组件替换原有的标签样式
|
||||
|
||||
5. ✅ 通用组件库创建
|
||||
- ✅ 创建了 `Badge` 组件(`components/ui/Badge.tsx`)
|
||||
- 支持多种变体(primary、success、warning、danger)
|
||||
- 支持多种尺寸(sm、md、lg)
|
||||
- 使用三层架构样式系统
|
||||
- ✅ 创建了 `Tag` 组件(`components/ui/Tag.tsx`)
|
||||
- 支持多种颜色(blue、green、orange、purple、indigo、pink、cyan)
|
||||
- 支持多种尺寸(sm、md、lg)
|
||||
- 支持点击事件
|
||||
- 已在首页素材卡片中应用
|
||||
- ✅ 创建了 `Button` 组件(`components/ui/Button.tsx`)
|
||||
- 支持多种变体(primary、secondary、ghost、danger、success)
|
||||
- 支持多种尺寸(sm、md、lg)
|
||||
- 支持加载状态(loading)
|
||||
- 支持全宽(fullWidth)
|
||||
- 使用 `forwardRef` 支持 ref 传递
|
||||
- ✅ 创建了 `Input` 组件(`components/ui/Input.tsx`)
|
||||
- 支持错误状态(error)
|
||||
- 支持多种尺寸(sm、md、lg)
|
||||
- 支持全宽(fullWidth)
|
||||
- 使用 `forwardRef` 支持 ref 传递
|
||||
- ✅ 创建了 `Card` 组件(`components/ui/Card.tsx`)
|
||||
- 支持多种变体(base、hover、large、flat、rainbow、glass、neon)
|
||||
- 支持自定义padding
|
||||
- 使用三层架构样式系统
|
||||
- ✅ 创建了 `Avatar` 组件(`components/ui/Avatar.tsx`)
|
||||
- 支持图片头像和文字头像(显示用户名首字母)
|
||||
- 支持多种尺寸(xs、sm、md、lg、xl)
|
||||
- 支持点击事件和键盘导航
|
||||
- 使用三层架构样式系统
|
||||
|
||||
6. ⏳ 深浅色主题适配优化(1.6)- 进行中
|
||||
- ✅ 为所有新创建和重构的文件添加了文件头注释
|
||||
- Tab组件:UsersTab、DepartmentsTab、CategoriesTab、InvitationsTab、MaterialsTab、MonitorTab
|
||||
- Hook文件:usePermissions、useDashboardData、useModalState、useToast、useDataFetch、useUserOperations、useDepartmentOperations
|
||||
- 工具函数:api.ts、formatters.ts、departmentTree.ts
|
||||
- UI组件:Modal、ConfirmDialog、AlertDialog、PromptDialog、DialogProvider、Button、Input、Card、Avatar、Badge、Tag
|
||||
- ✅ Header组件迁移到三层架构
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- 使用统一的 `Avatar` 组件替换自定义头像实现
|
||||
- 下拉菜单使用 `cardStyles.base` 统一样式
|
||||
- 移除了所有 `dark:bg-gray` 等直接样式
|
||||
- 移除了不再使用的 `getInitials` 函数
|
||||
- ✅ ChangePasswordModal迁移到统一组件
|
||||
- 使用统一的 `Modal` 组件替换自定义弹窗实现
|
||||
- 使用统一的 `Input` 组件替换原生input
|
||||
- 使用统一的 `Button` 组件替换原生button
|
||||
- 使用三层架构样式系统
|
||||
- 添加了文件头注释
|
||||
- ✅ DanmakuWidget迁移到三层架构
|
||||
- 使用统一的 `Input` 和 `Button` 组件
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- 优化样式(gray → slate)
|
||||
- 添加文件头注释
|
||||
- ✅ AIChatWidget优化
|
||||
- 添加文件头注释
|
||||
- 优化关键样式(gray → slate)
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- ✅ RankingPanel迁移到三层架构
|
||||
- 添加文件头注释
|
||||
- 使用 `useThemeStyles()` Hook
|
||||
- 优化样式(gray → slate)
|
||||
- ✅ 分享页面和编辑页面优化
|
||||
- `app/share/[id]/page.tsx` 添加文件头注释,优化样式(gray → slate)
|
||||
- `app/edit/[id]/page.tsx` 添加文件头注释,优化样式(gray → slate)
|
||||
- 使用 `useThemeStyles()` Hook 和 `cardStyles.base`
|
||||
- ✅ UploadModal基础样式优化
|
||||
- 添加文件头注释
|
||||
- 导入 `useThemeStyles` 和统一组件
|
||||
- 所有 `gray` 系列替换为 `slate` 系列
|
||||
- 主要容器使用 `cardStyles.base`
|
||||
- 注意:由于后续会进行模板管理整合(需求14),暂不做深度重构
|
||||
- ✅ 低饱和度炫彩元素设计(1.7)
|
||||
- 在颜色层添加了 `accent` 配置(pink、purple、blue、cyan、emerald、orange、rainbow)
|
||||
- 每个颜色包含 bg、text、border、hover 四个属性
|
||||
- 浅色模式使用低饱和度背景(如 `bg-pink-50`)
|
||||
- 深色模式使用低透明度背景(如 `bg-pink-900/20`)
|
||||
- 在组合器中添加 `accentStyles` 导出
|
||||
- 更新 `Tag` 组件使用新的 `accentStyles`,所有颜色都使用低饱和度炫彩元素
|
||||
|
||||
**优先级 2:数据一致性与核心功能**
|
||||
5. ✅ 用户信息修改后同步素材信息
|
||||
- ✅ API 端已实现:当用户名变化时,使用事务同步更新该用户所有素材的 `authorName`
|
||||
- ✅ 前端已处理:`useUserOperations.updateUser` 处理 `materialsUpdated` 返回值并显示提示
|
||||
- ✅ 使用事务确保数据一致性
|
||||
- ✅ 错误处理和回滚机制已实现
|
||||
6. ✅ 模板管理整合到上传和分类
|
||||
- ✅ 创建公共 API 端点 `/api/upload-templates` 获取启用的模板列表(不需要管理员权限)
|
||||
- ✅ 修改 UploadModal:将"素材类型"改为"素材模板",从 API 拉取模板列表
|
||||
- ✅ 实现动态字段渲染:选择模板后根据模板字段动态生成表单(支持 text、textarea、number、select、checkbox、radio、date 等类型)
|
||||
- ✅ 根据字段的 `layoutWidth` 属性支持半行/整行布局
|
||||
- ✅ 修改上传逻辑:提交模板ID和字段值到数据库(存储在 `metadata` 和 `templateId` 字段)
|
||||
- ✅ 添加"添加到分类"字段属性
|
||||
- 在数据库 schema 中添加 `addToCategory` 字段到 `UploadField` 模型
|
||||
- 在字段编辑器中添加"添加到分类"勾选框(仅 select 类型显示)
|
||||
- 更新所有相关类型定义和 API 以支持该字段
|
||||
- ✅ 创建模板分类 API `/api/template-categories`:从模板管理中提取所有 `addToCategory=true` 且 `type='select'` 的字段选项
|
||||
- ✅ 创建模板分类管理 API `/api/template-categories/[fieldKey]`:支持获取和更新特定字段的分类选项
|
||||
- ✅ 修改分类管理界面:显示模板分类卡片,提供编辑、添加、删除选项的功能,保留原有分类管理用于向后兼容
|
||||
- ⏳ 模板管理面板逻辑检查(待后续完善)
|
||||
7. ✅ 首页类型筛选TAG系统(2.3)
|
||||
- ✅ 创建 `/api/material-types` API:从模板管理中提取 `materialType` 字段的 `optionsStatic` 选项
|
||||
- ✅ 修改首页:将固定的 `materialTypes` 数组改为从API动态获取
|
||||
- ✅ 实现TAG形式的类型筛选UI:在搜索框下方显示TAG筛选
|
||||
- ✅ 第一个TAG是"全部",点击可清除筛选
|
||||
- ✅ 使用 `Tag` 组件和 `accentStyles` 实现低饱和度炫彩元素样式
|
||||
- ✅ 支持多种颜色循环使用(blue、green、orange、purple、pink、cyan)
|
||||
- ✅ 选中状态使用 ring 样式突出显示,未选中状态使用透明度变化
|
||||
- ✅ 支持点击切换筛选,提升用户体验
|
||||
8. ✅ 上传素材进度优化(3.1)
|
||||
- ✅ 添加 `uploadComplete` 状态标识上传完成
|
||||
- ✅ 进度100%后立即显示完成状态(进度条变绿色,显示"上传完成")
|
||||
- ✅ 添加300ms延迟确保数据保存完成后再关闭弹窗
|
||||
- ✅ 优化用户体验:显示"✓ 上传成功,正在关闭..."提示
|
||||
- ✅ 修复所有错误处理路径,确保状态正确重置
|
||||
- 将模板系统串起前后端
|
||||
|
||||
9. ✅ 素材卡片信息优化(3.2)
|
||||
- ✅ 移除了"作者:"文字,直接显示作者名
|
||||
- ✅ 使用Avatar组件在作者名前显示头像
|
||||
- ✅ 移除了文件名和文件大小显示
|
||||
- ✅ 将上传时间移到预览图右上角,默认隐藏,hover时显示
|
||||
- ✅ 优化了卡片布局,信息更简洁清晰
|
||||
|
||||
10. ✅ 素材卡片标签系统(3.3)
|
||||
- ✅ 创建了素材排行榜API(`/api/material-rankings`)
|
||||
- ✅ 实现了下载榜、点赞榜、精品榜三种排行榜
|
||||
- ✅ 在首页素材卡片上显示排行榜标签
|
||||
- ✅ 使用Badge组件,样式统一美观
|
||||
- ✅ 标签显示在分类标签组的最前面,视觉突出
|
||||
|
||||
11. ✅ 素材卡片大小设置和瀑布流(3.4)
|
||||
- ✅ 添加了横向滑块控制卡片大小(5个级别)
|
||||
- ✅ 实现了网格和瀑布流两种布局模式
|
||||
- ✅ 初始加载24个素材,使用IntersectionObserver实现懒加载
|
||||
- ✅ 滚动到底部自动加载更多(每次加载24个)
|
||||
- ✅ 筛选条件改变时自动重置显示数量
|
||||
- ✅ 用户偏好保存到localStorage
|
||||
|
||||
12. ✅ 上传面板进度条位置优化
|
||||
- ✅ 将上传进度条移到"素材文件"上传区域下方
|
||||
- ✅ 将错误信息和成功提示移到"素材文件"上传区域下方
|
||||
- ✅ 优化用户体验,进度和提示信息更贴近上传操作区域
|
||||
|
||||
13. ✅ 多预览图上传功能(3.5)
|
||||
- ✅ 支持上传最多5张预览图
|
||||
- ✅ 第一张预览图自动设为封面
|
||||
- ✅ 支持多文件选择和拖拽上传
|
||||
- ✅ 预览图列表显示,可单独删除
|
||||
- ✅ 多预览图URLs存储在metadata中
|
||||
|
||||
14. ✅ 用户头像设置功能(4.1)
|
||||
- ✅ 创建了头像上传API(`/api/user/avatar`)
|
||||
- ✅ 用户可以在个人中心上传头像
|
||||
- ✅ 头像自动压缩和优化
|
||||
- ✅ 更新了auth配置,在session中包含avatar
|
||||
- ✅ Header组件中的Avatar组件支持显示用户头像
|
||||
|
||||
15. ✅ 个人中心功能(4.2)
|
||||
- ✅ 在Header菜单中添加了"个人中心"选项
|
||||
- ✅ 创建了个人中心页面(`/profile`)
|
||||
- ✅ 显示用户基本信息和上传的素材
|
||||
- ✅ 支持在个人中心上传和修改头像
|
||||
|
||||
16. ✅ 菜单调整优化(4.3)
|
||||
- ✅ 将"系统设置"改名为"管理",更换图标(使用滑块图标)
|
||||
- ✅ 将"外观设置"移到头像菜单中,添加调色板图标
|
||||
- ✅ 优化菜单结构,使功能分类更清晰
|
||||
|
||||
**详细计划**:请查看 `优化计划与优先级.md`
|
||||
|
||||
## 📝 代码规范建议
|
||||
|
||||
### 命名规范
|
||||
- 组件文件:PascalCase(如 `UsersTab.tsx`)
|
||||
- Hook 文件:camelCase with `use` prefix(如 `usePermissions.ts`)
|
||||
- 工具函数:camelCase(如 `formatDate.ts`)
|
||||
- 类型文件:`types.ts` 或 `types/index.ts`
|
||||
|
||||
### 文件组织
|
||||
```
|
||||
app/admin/
|
||||
├── _components/
|
||||
│ ├── types.ts # 类型定义
|
||||
│ ├── MonitorTab.tsx # 监控仪表板
|
||||
│ ├── UsersTab.tsx # 用户管理
|
||||
│ ├── DepartmentsTab.tsx # 架构管理
|
||||
│ ├── CategoriesTab.tsx # 分类管理
|
||||
│ ├── InvitationsTab.tsx # 邀请管理
|
||||
│ ├── MaterialsTab.tsx # 素材管理
|
||||
│ ├── TemplateManager.tsx # 模板管理
|
||||
│ └── AppearanceSettingsPanel.tsx
|
||||
├── _hooks/ # 自定义 hooks
|
||||
│ ├── useToast.ts
|
||||
│ ├── usePermissions.ts
|
||||
│ └── useDataFetch.ts
|
||||
├── _utils/ # 工具函数
|
||||
│ ├── validators.ts
|
||||
│ └── formatters.ts
|
||||
└── page.tsx # 主页面(简化后)
|
||||
```
|
||||
|
||||
## 🎯 最终目标
|
||||
|
||||
1. **代码可维护性**:每个组件职责单一,易于理解和修改
|
||||
2. **代码复用性**:公共逻辑提取到 hooks 和工具函数
|
||||
3. **类型安全**:完整的 TypeScript 类型定义,减少运行时错误
|
||||
4. **UI 一致性**:统一使用设计系统,确保视觉一致性
|
||||
5. **性能优化**:减少不必要的渲染,优化数据获取
|
||||
6. **用户体验**:流畅的交互,清晰的错误提示
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
# PowerShell 编码修复脚本
|
||||
# 用于解决 PowerShell 和 Cursor 中的中文乱码问题
|
||||
|
||||
Write-Host "正在修复 PowerShell 编码设置..." -ForegroundColor Yellow
|
||||
|
||||
# 1. 设置控制台代码页为 UTF-8
|
||||
chcp 65001 | Out-Null
|
||||
|
||||
# 2. 设置 PowerShell 输出编码为 UTF-8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# 3. 设置 PowerShell 输入编码为 UTF-8
|
||||
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# 4. 测试中文显示
|
||||
Write-Host "`n✅ 编码设置已更新!" -ForegroundColor Green
|
||||
Write-Host "测试中文显示:你好世界 🌏" -ForegroundColor Cyan
|
||||
Write-Host "测试特殊字符:①②③ ⭐⭐" -ForegroundColor Yellow
|
||||
|
||||
# 5. 显示当前编码信息
|
||||
Write-Host "`n当前编码信息:" -ForegroundColor Yellow
|
||||
Write-Host " 控制台代码页: $([Console]::OutputEncoding.CodePage)" -ForegroundColor White
|
||||
Write-Host " 输出编码: $($OutputEncoding.EncodingName)" -ForegroundColor White
|
||||
Write-Host " 输入编码: $([Console]::InputEncoding.EncodingName)" -ForegroundColor White
|
||||
|
||||
Write-Host "`n💡 提示:" -ForegroundColor Cyan
|
||||
Write-Host " 1. 如果问题仍然存在,请重启 PowerShell 或 Cursor" -ForegroundColor White
|
||||
Write-Host " 2. 可以在 PowerShell 配置文件中添加这些设置(永久生效)" -ForegroundColor White
|
||||
Write-Host " 3. Cursor 设置中检查终端编码设置" -ForegroundColor White
|
||||
|
||||
|
||||
@ -1,287 +0,0 @@
|
||||
📋 待办清单 - 功能优化与改进
|
||||
|
||||
以下是一些需要做的改进,按优先级和依赖关系规划一下先后顺序~
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🎯 核心功能优化
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
1. 用户信息同步 📝 ✅
|
||||
管理员修改用户信息后,这个用户上传的所有素材信息也要跟着更新
|
||||
比如改了用户名,素材里的作者名也要变
|
||||
✅ 已完成:在`/api/admin/users/update`中实现了用户名同步到素材的`authorName`字段
|
||||
✅ 已完成:使用事务确保数据一致性
|
||||
✅ 已完成:前端显示同步成功提示
|
||||
|
||||
2. 上传进度优化 ⚡ ✅
|
||||
现在上传素材进度条到100%后,还要等一会儿才关闭弹窗
|
||||
希望能更流畅,进度到100%就立马关闭
|
||||
✅ 已完成:进度100%后立即显示完成状态(绿色进度条+完成提示)
|
||||
✅ 已完成:添加300ms延迟确保数据保存完成后再关闭
|
||||
✅ 已完成:优化用户体验,显示"上传成功,正在关闭..."提示
|
||||
|
||||
3. 多预览图功能 🖼️ ✅
|
||||
一个素材可以上传多张预览图
|
||||
自动把第一张设为封面预览图
|
||||
✅ 已完成:支持上传最多5张预览图
|
||||
✅ 已完成:第一张预览图自动设为封面
|
||||
✅ 已完成:支持多文件选择和拖拽上传
|
||||
✅ 已完成:预览图列表显示,可单独删除
|
||||
|
||||
4. 性能监控导出 📊 ✅
|
||||
性能监控页面增加导出CSV表格功能
|
||||
方便数据分析和存档
|
||||
✅ 已完成:在系统监控页面添加"导出CSV"按钮
|
||||
✅ 已完成:导出包含所有监控数据(概览、趋势、分布、活动、系统信息)
|
||||
✅ 已完成:支持Excel正确显示中文(添加UTF-8 BOM)
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
👤 用户相关功能
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
5. 个人中心 🏠 ✅
|
||||
点击头像弹出的菜单里,增加"个人中心"功能
|
||||
让用户可以查看和管理自己的信息
|
||||
✅ 已完成:在Header菜单中添加了"个人中心"选项
|
||||
✅ 已完成:创建了个人中心页面(`/profile`)
|
||||
✅ 已完成:显示用户基本信息和上传的素材
|
||||
✅ 已完成:支持在个人中心上传和修改头像
|
||||
|
||||
6. 用户头像设置 🎨 ✅
|
||||
用户可以自己设置头像
|
||||
- 钉钉登录的用户:直接用钉钉头像(能抓到就用,抓不到就算了)
|
||||
- 没有头像的用户:和系统LOGO一样,用默认头像策略
|
||||
✅ 已完成:创建了头像上传API(`/api/user/avatar`)
|
||||
✅ 已完成:用户可以在个人中心上传头像
|
||||
✅ 已完成:头像自动压缩和优化
|
||||
✅ 已完成:Header组件中的Avatar组件支持显示用户头像
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🎨 首页UI优化
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
7. 排行榜缩略按钮可拖动 📌 ✅
|
||||
首页排行榜关闭后的那个小缩略按钮
|
||||
可以拖动,而且记住关闭前的位置
|
||||
✅ 已完成:缩略按钮支持拖动功能
|
||||
✅ 已完成:关闭时保存面板位置作为按钮位置
|
||||
✅ 已完成:位置保存到localStorage,支持恢复
|
||||
✅ 已完成:拖动阈值5px区分点击和拖动操作
|
||||
|
||||
8. 素材卡片响应式优化 ✅
|
||||
问题:调整浏览器窗口大小时,卡片会变形
|
||||
解决:保持卡片比例固定,不要贴边
|
||||
✅ 已完成:卡片比例固定,加了边距,样式统一
|
||||
|
||||
9. 素材卡片大小可调 🎚️
|
||||
可以设置素材卡片的大小(加个横向滑块)
|
||||
展示形式可以改成瀑布流
|
||||
考虑到低性能机器,可以动态加载,一次加载多少你看着办
|
||||
有更好的想法也可以提出来
|
||||
|
||||
10. 素材卡片信息精简 📦 ✅
|
||||
现在卡片下方信息太多,占地方
|
||||
优化方案:
|
||||
- ✅ "作者:"文字去掉,直接显示作者名,前面加头像
|
||||
- ✅ 文件名和文件大小不显示
|
||||
- ✅ 上传时间放到预览图右上角,默认隐藏,鼠标悬停时显示
|
||||
|
||||
11. 素材卡片标签系统 🏷️
|
||||
在素材名称前面加标签
|
||||
比如:点赞榜、精品榜、下载榜、收藏榜等
|
||||
样式参考最高权限的显示效果
|
||||
要好看,偏向苹果风格,但整体要统一
|
||||
|
||||
12. 首页筛选TAG系统 🎯 ✅
|
||||
把筛选里的"类型分页"功能放到首页
|
||||
用TAG显示,第一个TAG是"全部"
|
||||
TAG根据上传模板设置(所以模板管理很重要)
|
||||
样式要大气好看,符合整体风格
|
||||
✅ 已完成:创建了 `/api/material-types` API 从模板中提取素材类型
|
||||
✅ 已完成:首页使用TAG形式显示类型筛选,支持点击切换
|
||||
✅ 已完成:使用低饱和度炫彩元素样式,符合整体设计风格
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🎛️ 系统管理优化
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
13. 菜单改名和调整 📋 ✅
|
||||
- 点击头像菜单里,"系统设置"改名为"管理",图标也换一下
|
||||
- "外观设置"也移到头像菜单里,前面加个图标
|
||||
✅ 已完成:将"系统设置"改名为"管理",更换图标(使用滑块图标)
|
||||
✅ 已完成:将"外观设置"移到头像菜单中,添加调色板图标
|
||||
|
||||
14. 模板管理整合 🔗 ✅
|
||||
现在模板管理已经做好了,要把前后端串起来:
|
||||
✅ 首页上传素材面板:把"素材类型"改成"素材模板"
|
||||
列表直接拉取模板管理中的模板名称
|
||||
✅ 选择模板后,界面显示对应模板的字段
|
||||
✅ 系统管理中的分类管理:
|
||||
不再用"店铺分类"和"场景类别"
|
||||
改用模板管理中的下拉选项文字做分类
|
||||
⚠️ 模板管理的面板逻辑要严格检查,这个很重要!(待后续完善)
|
||||
|
||||
15. 系统监控交互增强 📈
|
||||
- 用户总数、活跃用户、总素材数、本月新用户、本月新素材
|
||||
这些数字可以点击,跳转到对应的数据显示
|
||||
- 素材上传趋势卡片前面,增加"当月上传趋势"
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
✨ UI统一与组件库
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
16. 统一弹窗系统 ✅
|
||||
不要用浏览器自带的弹窗,太丑了
|
||||
✅ 已完成:创建了统一弹窗组件,替换了所有浏览器原生弹窗
|
||||
✅ 已完成:确认、提示、输入弹窗都统一了
|
||||
|
||||
17. 素材预览面板统一 ✅
|
||||
点击素材后的预览面板要统一,退出逻辑也要统一
|
||||
✅ 已完成:创建了统一的预览组件,样式和退出逻辑都统一了
|
||||
|
||||
18. 首页UI统一到三层架构 ✅
|
||||
首页的UI要统一到三层架构系统
|
||||
✅ 已完成:按钮、输入框、筛选弹窗等主要组件都统一了
|
||||
✅ 已完成:所有颜色从gray系列改成slate系列,完全统一
|
||||
✅ 已完成:文本、边框、标签样式都统一了
|
||||
|
||||
19. 通用组件库创建 ✅
|
||||
在统一UI架构内,新增一些通用组件
|
||||
✅ 已完成:创建了Badge组件(徽章,支持多种样式)
|
||||
✅ 已完成:创建了Tag组件(标签,支持多种颜色,已在首页用上了)
|
||||
✅ 已完成:创建了Button组件(按钮,支持多种样式和加载状态)
|
||||
✅ 已完成:创建了Input组件(输入框,支持错误状态和多种尺寸)
|
||||
✅ 已完成:创建了Card组件(卡片,支持多种变体)
|
||||
✅ 已完成:创建了Avatar组件(头像,支持图片和文字头像)
|
||||
✅ 已完成:为所有新创建和重构的文件添加了文件头注释
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🎨 视觉设计优化
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
20. 深浅色主题优化 🌓 ⏳
|
||||
优化一下深浅色主题的适配
|
||||
确保所有组件在两种模式下都好看
|
||||
✅ 已完成:Header组件迁移到三层架构,使用Avatar组件
|
||||
✅ 已完成:ChangePasswordModal迁移到统一的Modal和Input组件
|
||||
✅ 已完成:DanmakuWidget迁移到三层架构,使用统一的Input和Button组件
|
||||
✅ 已完成:AIChatWidget添加文件头注释,优化样式(gray → slate)
|
||||
✅ 已完成:RankingPanel添加文件头注释,迁移到三层架构
|
||||
✅ 已完成:分享页面和编辑页面添加文件头注释,优化样式(gray → slate)
|
||||
⏳ 进行中:继续优化UploadModal等大型组件
|
||||
|
||||
21. 低饱和度炫彩元素 🎨 ✅
|
||||
设计语言遵循苹果风格
|
||||
重点用低饱和度的炫彩元素
|
||||
让界面更有活力但不会太花哨
|
||||
✅ 已完成:添加了accentStyles配置(pink、purple、blue、cyan、emerald、orange、rainbow)
|
||||
✅ 已完成:Tag组件已更新使用低饱和度炫彩元素
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
22.首页内上传面板的上传进度条以及报错信息和成功提示,放到上传素材那一栏的下方,不要放在整个面板的最下方 ✅
|
||||
✅ 已完成:将上传进度条移到"素材文件"上传区域下方
|
||||
✅ 已完成:将错误信息和成功提示移到"素材文件"上传区域下方
|
||||
|
||||
|
||||
23.外观设置改名为系统设置,且管理内的外观设置删除,和头像下拉菜单里面的外观设置重复了 ✅
|
||||
✅ 已完成:删除管理页面中的"外观设置"Tab
|
||||
✅ 已完成:从AdminTab类型中移除'settings'选项
|
||||
✅ 已完成:外观设置现在仅在头像下拉菜单中可用
|
||||
|
||||
24.颜色系统完全统一 ✅
|
||||
✅ 已完成:优化globals.css,将所有gray-替换为slate-
|
||||
✅ 已完成:优化登录/注册页面,将所有gray-替换为slate-
|
||||
✅ 已完成:优化EditMaterialModal,将所有gray-替换为slate-
|
||||
✅ 已完成:所有页面和组件现在完全使用slate系列颜色
|
||||
|
||||
25.首页素材卡片内点击“编辑”按钮弹出来的弹出来的弹窗没有统一到统一UI。 ✅
|
||||
✅ 已完成:EditMaterialModal 改为复用全局 `Modal` 组件,交互和样式保持一致
|
||||
|
||||
26.首页的“筛选”按钮的内容,没有联动到后台数据 ✅
|
||||
✅ 已完成:筛选弹窗中的项由模板管理里勾选“首页筛选”的字段自动决定(支持店铺/场景/平台等),并实时读取后台数据
|
||||
|
||||
27.素材卡片上的上传时间,只显示年月日。点赞按钮不起作用。且我尝试下载了一个素材,理论上这个素材应该是下载榜第一的,但是素材卡片上没有下载榜的角标
|
||||
✅ 已完成:上传时间统一为“yyyy-MM-dd”并放在封面右上角 hover 显示
|
||||
✅ 已完成:点赞按钮重新接入 `/api/materials/like`,支持状态高亮
|
||||
✅ 已完成:根据 `/api/material-rankings` 在封面左上角显示统一 UI 渐变角标(精品/下载/点赞榜)
|
||||
|
||||
28.管理员权限的账户可以直接在首页的素材卡片内实现删除素材的功能,按钮可以加在下载按钮前面。普通的用户就看不见这个按钮。这个按钮可以在系统管理内的功能开关内设置普通人员能不能看见这个按钮(打开就是只能管理员看见按钮,关闭是全部人员都能看到按钮),默认是只有管理员权限能看到
|
||||
✅ 已完成:卡片操作区新增“删除”按钮(管理员默认可见,带统一弹窗确认)
|
||||
✅ 已完成:系统设置 → 功能开关中新增“首页删除按钮仅管理员可见”开关,可切换显示范围
|
||||
|
||||
29.首页tag排序按照素材数量动态排序
|
||||
✅ 已完成:`/api/upload-templates` 返回 `materialCount`,前端按素材数量降序展示并显示计数
|
||||
✅ 已完成:TAG 条使用统一 UI 样式,支持“全部 + 模板名”展示
|
||||
|
||||
30.排行榜缩略按钮可拖动,改完之后,这个状态只能维持一次,拖动之后再拖动,这个就不生效了
|
||||
|
||||
31.统一UI标准元素预览界面适配是在哪里,还有后面新添加的新元素会自动加进去吗
|
||||
✅ 已完成:所有统一 UI 组件收敛在 `app/ui-preview/page.tsx`,新增元素接入三层样式后会自动在此页面展示,开发可参照该页面验证
|
||||
|
||||
32.首页tag居中排布
|
||||
✅ 已完成:模板 TAG 区域(含吸顶状态)改为 `justify-center`,并移除“素材库”标题保持整体对齐
|
||||
|
||||
33.首页筛选内还是和后台的字段对不上
|
||||
✅ 已完成:筛选弹窗实时读取模板管理中标记了“首页筛选”的字段,字段标签、选项与后台保持一致
|
||||
|
||||
34.上传素材面板中预览图片,里面的虚位卡片会出现在其他图片的下面,这是不对的,应该是在其他图片的后面
|
||||
✅ 已完成:预览图占位卡片改为与已上传图片同一个网格,始终显示在末尾
|
||||
|
||||
35.上传素材面板中预览图片,点击删掉预览图片小按钮之后,这个预览图片是删掉了,但是也会同时弹出选择图片的windows弹窗,这是不对的,冲突了
|
||||
✅ 已完成:删除按钮阻止冒泡,不再触发文件选择弹窗
|
||||
|
||||
36.排行榜中数字为0的不上榜
|
||||
✅ 已完成:排行榜数据过滤零值,只有 count > 0 的条目才展示
|
||||
|
||||
37.首页TAG,在分类名字后面默认不显示数量,与鼠标交互后才会显示。且全部的tag上面的数字是不对的。现在素材数量是两个,但是显示是0 ✅
|
||||
- ✅ TAG 数字默认隐藏,hover/聚焦时平滑显示;“全部”数量直接读取 `/api/materials` 返回的 `totalCount`,确保统计准确
|
||||
|
||||
38.首页筛选内还是和后台的字段对不上,比如我在后台的“MD”素材模板中,将“sadfh”设置为了首页筛选,但是首页上的筛选内还是看不到sadfh这个筛选项 ✅
|
||||
- ✅ 模板中勾选“首页筛选”的字段会在筛选弹窗动态渲染,对应选项来自模板配置,同时增加了查询参数和后端过滤逻辑
|
||||
|
||||
39.psd素材预览窗内还是会有大约1像素的风格 ✅
|
||||
- ✅ 移除PSD长图容器边框/分隔线,统一用无缝背景展示
|
||||
|
||||
40.预览窗内不显示“作者”,直接显示作者头像以及作者名称。然后首页素材卡片上的几个按钮同样的在打开预览弹窗内显示,且将精品榜的判断逻辑和下载点赞脱离,直接在点开的预览窗口内新增一个精品按钮。点赞的心形图标还是不对,是歪的。 ✅
|
||||
- ✅ 预览面板显示头像+姓名、不再出现“作者”标签
|
||||
- ✅ 提炼卡片按钮为统一组件,预览弹窗底部复用,含编辑/删除/分享/收藏/点赞/下载
|
||||
- ✅ 新增“设为精品”按钮,仅管理员可切换,后端记录 `isPremium` 并供排行榜使用
|
||||
- ✅ 心形图标改为对称版本,收藏按钮独立
|
||||
|
||||
41.首页素材卡片的三个角标和素材名称不要用同一个框,分开来 ✅
|
||||
- ✅ 左上角改为角标行 + 独立标题标签,视觉分离
|
||||
|
||||
42.增加收藏功能,将首页素材卡片的上传时间从预览图的右上角挪到右下角,然后在右上角增加收藏按钮。个人的收藏列表可以在个人中心看到。 ✅
|
||||
- ✅ 新增收藏API/数据表、卡片右上角收藏钮(支持预览弹窗内操作)
|
||||
- ✅ 上传时间移动到右下角
|
||||
- ✅ 个人中心新增“我的收藏”列表
|
||||
|
||||
43.分类管理中又冒出来许多的奇怪的分类小卡片,之前也是有这个问题的, ✅
|
||||
- ✅ 后端解析分类选项时统一去除 `[]` 和 `\"` 的嵌套字符,并在更新时做归一化,异常项不再生成卡片
|
||||
|
||||
44.首页上传素材面板内选择上传模板如果是自定义模板的话,素材卡片的用来区分不同素材模板的标不会显示成自定义模板的名字,而是继续使用预制模板的名字
|
||||
- ✅已解决
|
||||
|
||||
45.首页tag按钮,切换的时候会闪动,越往后的按钮闪动越厉害,应该是和排布有关系
|
||||
|
||||
46.自定义模板的问题解决了,现在预制模板的问题又来了,我尝试使用3dmax的预制模板上传素材,但是提示我:模板不存在:builtin-3dmax-material
|
||||
|
||||
47,自定义模板的tag按钮里面不显示素材数量
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
💡 提示:
|
||||
- ✅ 表示已完成
|
||||
- ⏳ 表示进行中
|
||||
- 📝 表示待开始
|
||||
- 有更好的想法随时提出来~
|
||||
@ -1,401 +0,0 @@
|
||||
# 在 git.1080p.hk 服务器部署 Docker 镜像仓库指南
|
||||
|
||||
## 📋 重要说明
|
||||
|
||||
**Git 仓库 ≠ Docker 镜像仓库**
|
||||
|
||||
- **Git 仓库**(如 `http://git.1080p.hk/WuYongJie/flnxi-Wu`):用于存储**源代码**
|
||||
- **Docker 镜像仓库(Registry)**:用于存储**Docker 镜像**(编译后的二进制文件)
|
||||
|
||||
**不能在 Git 仓库中存储 Docker 镜像**,需要单独部署 Docker Registry 服务。
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 推荐方案
|
||||
|
||||
### 方案1:在同一服务器上部署 Docker Registry(推荐)
|
||||
|
||||
如果 `git.1080p.hk` 是您的服务器,可以在同一台服务器上部署 Docker Registry。
|
||||
|
||||
**优点**:
|
||||
- ✅ 无需额外服务器
|
||||
- ✅ 统一管理
|
||||
- ✅ 内网访问速度快
|
||||
|
||||
**架构**:
|
||||
```
|
||||
git.1080p.hk 服务器
|
||||
├── Gitea (端口: 3000) # Git代码仓库
|
||||
└── Docker Registry (端口: 5000) # Docker镜像仓库
|
||||
```
|
||||
|
||||
### 方案2:创建独立的镜像仓库服务器
|
||||
|
||||
如果希望分离,可以创建:
|
||||
- `registry.1080p.hk` - 专门用于Docker镜像仓库
|
||||
- `git.1080p.hk` - 继续用于Git代码仓库
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署步骤(方案1:同一服务器)
|
||||
|
||||
### 1. 在 git.1080p.hk 服务器上部署 Docker Registry
|
||||
|
||||
#### 1.1 使用 Docker 运行 Registry
|
||||
|
||||
```bash
|
||||
# SSH登录服务器
|
||||
ssh root@git.1080p.hk
|
||||
|
||||
# 创建存储目录
|
||||
mkdir -p /var/lib/docker-registry
|
||||
chmod 755 /var/lib/docker-registry
|
||||
|
||||
# 运行 Docker Registry(HTTP版本,内网使用)
|
||||
docker run -d \
|
||||
--name docker-registry \
|
||||
--restart=always \
|
||||
-p 5000:5000 \
|
||||
-v /var/lib/docker-registry:/var/lib/registry \
|
||||
registry:2
|
||||
|
||||
# 验证运行
|
||||
docker ps | grep registry
|
||||
curl http://localhost:5000/v2/
|
||||
```
|
||||
|
||||
#### 1.2 配置 HTTPS(生产环境推荐)
|
||||
|
||||
```bash
|
||||
# 创建证书目录
|
||||
mkdir -p /etc/docker/registry
|
||||
|
||||
# 生成自签名证书(或使用已有证书)
|
||||
openssl req -x509 -newkey rsa:4096 -nodes \
|
||||
-keyout /etc/docker/registry/domain.key \
|
||||
-out /etc/docker/registry/domain.crt \
|
||||
-days 365 \
|
||||
-subj "/CN=registry.1080p.hk"
|
||||
|
||||
# 使用HTTPS运行
|
||||
docker stop docker-registry
|
||||
docker rm docker-registry
|
||||
|
||||
docker run -d \
|
||||
--name docker-registry \
|
||||
--restart=always \
|
||||
-p 5000:5000 \
|
||||
-v /etc/docker/registry:/certs \
|
||||
-v /var/lib/docker-registry:/var/lib/registry \
|
||||
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
|
||||
-e REGISTRY_HTTP_TLS_PRIVATE_KEY=/certs/domain.key \
|
||||
registry:2
|
||||
```
|
||||
|
||||
#### 1.3 配置 Nginx 反向代理(可选,推荐)
|
||||
|
||||
如果服务器已有 Nginx,可以配置反向代理:
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/docker-registry
|
||||
server {
|
||||
listen 80;
|
||||
server_name registry.1080p.hk;
|
||||
|
||||
# 重定向到HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name registry.1080p.hk;
|
||||
|
||||
ssl_certificate /etc/docker/registry/domain.crt;
|
||||
ssl_certificate_key /etc/docker/registry/domain.key;
|
||||
|
||||
# 允许大文件上传
|
||||
client_max_body_size 0;
|
||||
chunked_transfer_encoding on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5000;
|
||||
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;
|
||||
|
||||
# Docker Registry 特殊配置
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# 启用配置
|
||||
ln -s /etc/nginx/sites-available/docker-registry /etc/nginx/sites-enabled/
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
### 2. 配置镜像仓库地址
|
||||
|
||||
#### 2.1 在构建服务器上配置
|
||||
|
||||
**编辑 `/etc/docker/daemon.json`**:
|
||||
```json
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.mirrors.ustc.edu.cn",
|
||||
"https://hub-mirror.c.163.com"
|
||||
],
|
||||
"insecure-registries": [
|
||||
"git.1080p.hk:5000",
|
||||
"registry.1080p.hk:5000"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**如果使用HTTPS**,移除 `insecure-registries`,配置证书:
|
||||
```bash
|
||||
# 复制证书到客户端
|
||||
scp root@git.1080p.hk:/etc/docker/registry/domain.crt /etc/docker/certs.d/git.1080p.hk:5000/ca.crt
|
||||
|
||||
# 或使用域名
|
||||
mkdir -p /etc/docker/certs.d/registry.1080p.hk:5000
|
||||
scp root@git.1080p.hk:/etc/docker/registry/domain.crt /etc/docker/certs.d/registry.1080p.hk:5000/ca.crt
|
||||
```
|
||||
|
||||
#### 2.2 重启 Docker
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
### 3. 构建和推送镜像
|
||||
|
||||
#### 3.1 构建镜像
|
||||
|
||||
```bash
|
||||
# 在本地或构建服务器上
|
||||
cd /path/to/flnxi_sucai
|
||||
|
||||
# 构建标准版
|
||||
docker build -t git.1080p.hk:5000/flnxi-material:standard-latest .
|
||||
|
||||
# 构建Pro版(使用相同Dockerfile,通过环境变量区分)
|
||||
docker build -t git.1080p.hk:5000/flnxi-material:pro-latest .
|
||||
```
|
||||
|
||||
#### 3.2 推送镜像
|
||||
|
||||
```bash
|
||||
# 登录(如果需要认证)
|
||||
docker login git.1080p.hk:5000
|
||||
|
||||
# 推送标准版
|
||||
docker push git.1080p.hk:5000/flnxi-material:standard-latest
|
||||
|
||||
# 推送Pro版
|
||||
docker push git.1080p.hk:5000/flnxi-material:pro-latest
|
||||
|
||||
# 推送带版本号的镜像
|
||||
docker tag git.1080p.hk:5000/flnxi-material:standard-latest \
|
||||
git.1080p.hk:5000/flnxi-material:standard-$(date +%Y%m%d)
|
||||
docker push git.1080p.hk:5000/flnxi-material:standard-$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
### 4. 更新配置文件
|
||||
|
||||
#### 4.1 更新 docker-compose 文件
|
||||
|
||||
**标准版** (`docker-compose.standard.yml`):
|
||||
```yaml
|
||||
app:
|
||||
image: git.1080p.hk:5000/flnxi-material:standard-${IMAGE_TAG:-latest}
|
||||
```
|
||||
|
||||
**Pro版** (`docker-compose.prod.yml`):
|
||||
```yaml
|
||||
app:
|
||||
image: git.1080p.hk:5000/flnxi-material:pro-${IMAGE_TAG:-latest}
|
||||
```
|
||||
|
||||
#### 4.2 更新环境变量示例
|
||||
|
||||
```bash
|
||||
# .env 文件
|
||||
DOCKER_REGISTRY=git.1080p.hk:5000
|
||||
IMAGE_TAG=latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 安全配置(可选但推荐)
|
||||
|
||||
### 1. 启用认证
|
||||
|
||||
```bash
|
||||
# 创建认证目录
|
||||
mkdir -p /auth
|
||||
|
||||
# 创建用户密码文件
|
||||
docker run --entrypoint htpasswd httpd:2 \
|
||||
-Bbn registry-user your-secure-password > /auth/htpasswd
|
||||
|
||||
# 使用认证运行Registry
|
||||
docker stop docker-registry
|
||||
docker rm docker-registry
|
||||
|
||||
docker run -d \
|
||||
--name docker-registry \
|
||||
--restart=always \
|
||||
-p 5000:5000 \
|
||||
-v /auth:/auth \
|
||||
-v /var/lib/docker-registry:/var/lib/registry \
|
||||
-e "REGISTRY_AUTH=htpasswd" \
|
||||
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
|
||||
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
|
||||
registry:2
|
||||
```
|
||||
|
||||
**客户端登录**:
|
||||
```bash
|
||||
docker login git.1080p.hk:5000
|
||||
# 输入用户名: registry-user
|
||||
# 输入密码: your-secure-password
|
||||
```
|
||||
|
||||
### 2. 配置防火墙
|
||||
|
||||
```bash
|
||||
# 只允许内网访问(推荐)
|
||||
ufw allow from 192.168.0.0/16 to any port 5000
|
||||
ufw allow from 10.0.0.0/8 to any port 5000
|
||||
|
||||
# 或允许特定IP
|
||||
ufw allow from 客户服务器IP to any port 5000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 使用 Docker Compose 管理 Registry(推荐)
|
||||
|
||||
创建 `docker-compose-registry.yml`:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
container_name: docker-registry
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5000:5000"
|
||||
volumes:
|
||||
- /var/lib/docker-registry:/var/lib/registry
|
||||
- /etc/docker/registry:/certs
|
||||
- /auth:/auth
|
||||
environment:
|
||||
- REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt
|
||||
- REGISTRY_HTTP_TLS_PRIVATE_KEY=/certs/domain.key
|
||||
- REGISTRY_AUTH=htpasswd
|
||||
- REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
|
||||
- REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm
|
||||
networks:
|
||||
- registry-network
|
||||
|
||||
networks:
|
||||
registry-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
**管理命令**:
|
||||
```bash
|
||||
# 启动
|
||||
docker compose -f docker-compose-registry.yml up -d
|
||||
|
||||
# 停止
|
||||
docker compose -f docker-compose-registry.yml down
|
||||
|
||||
# 查看日志
|
||||
docker compose -f docker-compose-registry.yml logs -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证部署
|
||||
|
||||
### 1. 检查 Registry 运行状态
|
||||
|
||||
```bash
|
||||
# 检查容器
|
||||
docker ps | grep registry
|
||||
|
||||
# 测试API
|
||||
curl http://git.1080p.hk:5000/v2/
|
||||
|
||||
# 查看镜像列表(需要认证)
|
||||
curl -u registry-user:your-secure-password \
|
||||
http://git.1080p.hk:5000/v2/_catalog
|
||||
```
|
||||
|
||||
### 2. 测试镜像推送和拉取
|
||||
|
||||
```bash
|
||||
# 测试推送
|
||||
docker pull alpine:latest
|
||||
docker tag alpine:latest git.1080p.hk:5000/test:latest
|
||||
docker push git.1080p.hk:5000/test:latest
|
||||
|
||||
# 测试拉取
|
||||
docker pull git.1080p.hk:5000/test:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 更新部署脚本
|
||||
|
||||
更新 `scripts/deploy/local-build-and-upload.ps1`,添加镜像推送功能:
|
||||
|
||||
```powershell
|
||||
# 在构建完成后添加
|
||||
Write-Step "推送镜像到私有仓库..."
|
||||
|
||||
# 登录镜像仓库
|
||||
docker login ${DockerRegistry} -u ${RegistryUser} -p ${RegistryPassword}
|
||||
|
||||
# 推送标准版
|
||||
docker tag flnxi-material:local-deploy ${DockerRegistry}/flnxi-material:standard-latest
|
||||
docker push ${DockerRegistry}/flnxi-material:standard-latest
|
||||
|
||||
# 推送Pro版(相同镜像,不同标签)
|
||||
docker tag flnxi-material:local-deploy ${DockerRegistry}/flnxi-material:pro-latest
|
||||
docker push ${DockerRegistry}/flnxi-material:pro-latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 总结
|
||||
|
||||
### 推荐配置
|
||||
|
||||
1. **镜像仓库地址**:`git.1080p.hk:5000`(HTTP)或 `registry.1080p.hk:5000`(HTTPS)
|
||||
2. **存储位置**:`/var/lib/docker-registry`
|
||||
3. **认证**:启用htpasswd认证(生产环境)
|
||||
4. **HTTPS**:使用Nginx反向代理(生产环境)
|
||||
|
||||
### 客户使用
|
||||
|
||||
客户只需要:
|
||||
1. 配置镜像仓库地址:`DOCKER_REGISTRY=git.1080p.hk:5000`
|
||||
2. 拉取镜像:`docker pull git.1080p.hk:5000/flnxi-material:standard-latest`
|
||||
3. 部署:使用 `docker-compose.standard.yml` 或 `docker-compose.prod.yml`
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
## 初次部署与“出厂设置”指南
|
||||
|
||||
本指南覆盖 NAS/内网环境从零安装的完整流程,亦可用于将现有测试环境**一键恢复到出厂状态**。建议严格按顺序执行,并在自述中记录执行时间,便于后续追溯。
|
||||
|
||||
### 1. 环境准备
|
||||
- 服务器/NAS:4C8G 以上、100GB 可用空间,建议启用 SSD/NVMe。
|
||||
- 系统:Ubuntu 22.04 LTS 或群晖 DSM(需支持 Docker / Podman)。
|
||||
- 依赖:Docker 24+、Docker Compose V2、Node.js 20(仅本地调试需要)、pnpm 或 npm。
|
||||
- 网络:可访问对象存储(MinIO/S3),放通 80/443/3000/5432/9000 等常用端口。
|
||||
- 证书:准备好公网域名和 TLS 证书(可选,Nginx/Traefik 层处理)。
|
||||
|
||||
### 2. 拉取代码与依赖
|
||||
```bash
|
||||
git clone https://your.repo/flnxi_sucai.git
|
||||
cd flnxi_sucai
|
||||
cp .env.example .env
|
||||
npm install # 或 pnpm install
|
||||
```
|
||||
- 将 `config-bianliang.env` 与 `.env` 一并填写,保证 `DATABASE_URL`、S3、域名、NEXTAUTH_SECRET 等关键变量存在。
|
||||
- NAS 环境若使用外置 Postgres/MinIO,可在 `.env` 中直接指向局域网地址。
|
||||
|
||||
### 3. 数据库初始化 / 恢复出厂
|
||||
> **⚠️ 会删除所有数据。** 确认已备份再执行。
|
||||
```bash
|
||||
npx prisma migrate reset --force --skip-seed
|
||||
```
|
||||
- 该命令会串行应用 `prisma/migrations` 下的所有迁移,包括最新的素材归属字段。
|
||||
- 如果在容器内执行,确保 `DATABASE_URL` 已指向真实数据库。
|
||||
|
||||
### 4. 生成 Prisma Client
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
- Docker 镜像构建过程会自动执行;裸机调试时建议显式运行,避免 TS/构建期间的类型缺失。
|
||||
|
||||
### 5. 启动服务
|
||||
```bash
|
||||
# 开发 / 单机调试
|
||||
npm run dev
|
||||
|
||||
# 生产部署(推荐)
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
```
|
||||
- Compose 会启动 Next.js、Nginx、Postgres、MinIO 等组件。若已有外部 Postgres/MinIO,可注释对应服务并调整 `.env`。
|
||||
|
||||
### 6. 系统初始化接口
|
||||
部署启动后,调用一次 `POST /api/system/init` 即可创建默认资源:
|
||||
```bash
|
||||
curl -X POST https://your-domain.com/api/system/init
|
||||
```
|
||||
返回值示例:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "系统初始化成功",
|
||||
"data": {
|
||||
"admin": { "email": "admin", "username": "admin", "password": "88888888" },
|
||||
"defaultDepartment": "默认部门",
|
||||
"defaultCategory": "默认店铺",
|
||||
"defaultScene": "默认场景"
|
||||
}
|
||||
}
|
||||
```
|
||||
- 首次登录后务必修改管理员密码,可在“个人中心 → 修改密码”完成。
|
||||
|
||||
### 7. 首次登录与检查
|
||||
1. 访问 `https://your-domain.com/login`,使用 `admin / 88888888` 登录。
|
||||
2. 进入后台 `/admin`,确认仪表盘可正常加载。
|
||||
3. 打开“系统设置”,填写对象存储、LOGO、前端域名等信息。
|
||||
4. 上传一份素材,验证转码、归属(`ownerType/ownerId`)、预览、下载链路是否正常。
|
||||
|
||||
### 8. 恢复为干净环境的快速指令
|
||||
若需要再次“清空一切”并保留最新 schema,可直接执行:
|
||||
```bash
|
||||
npx prisma migrate reset --force --skip-seed
|
||||
curl -X POST https://your-domain.com/api/system/init
|
||||
```
|
||||
可将上述两条命令收录到自定义脚本,例如 `scripts/reset-to-factory.ps1/.sh`,方便演示或客户验收前重置。
|
||||
|
||||
### 9. 常见问题
|
||||
- **`prisma migrate reset` 卡在输入确认**:加上 `--force --skip-generate`/`--skip-seed` 使其无交互运行。
|
||||
- **`postgres connection refused`**:确认容器/服务已启动,或在 `.env` 中关闭 TLS(`?sslmode=disable`)。
|
||||
- **MinIO/S3 访问失败**:在 System Settings 中补齐 `s3Endpoint/s3Bucket/s3PublicUrl`,并确保对象存储已创建对应桶。
|
||||
- **视频转码异常**:检查服务器是否已安装 `ffmpeg`,或在 Dockerfile 中启用 `apt install ffmpeg`。日志可在容器内 `/root/.pm2/logs` 或 `docker logs flnxi-material` 查看。
|
||||
|
||||
### 10. 下一步
|
||||
- 按照 `docs/USER_ONBOARDING_GUIDE.md` 给团队成员做首登引导。
|
||||
- 开启钉钉登录、AI 改图、内网穿透等可选模块前,建议先在测试环境演练一遍整套流程。
|
||||
|
||||
|
||||
@ -1,313 +0,0 @@
|
||||
# 性能优化指南
|
||||
|
||||
## 🎯 优化目标
|
||||
|
||||
针对**一键部署到NAS或服务器**的场景,确保应用在低配置设备上也能稳定运行。
|
||||
|
||||
---
|
||||
|
||||
## 📊 当前性能基准
|
||||
|
||||
### 已实施的优化
|
||||
|
||||
1. **禁用Next.js Image Optimizer**
|
||||
- ✅ 图片已在上传时通过Sharp优化
|
||||
- ✅ 避免运行时重复优化
|
||||
- ✅ 减少40%的服务器负载
|
||||
|
||||
2. **S3客户端优化**
|
||||
- ✅ 超时设置:5分钟请求超时,30秒连接超时
|
||||
- ✅ 重试机制:最多10次重试
|
||||
- ✅ 连接池管理
|
||||
|
||||
3. **数据库优化**
|
||||
- ✅ 使用Prisma连接池
|
||||
- ✅ 查询只选择必要字段
|
||||
- ✅ 使用事务保证一致性
|
||||
|
||||
---
|
||||
|
||||
## 🔧 待优化项
|
||||
|
||||
### 1. 数据库查询优化
|
||||
|
||||
#### 问题:N+1查询
|
||||
```typescript
|
||||
// ❌ 不好的做法
|
||||
const materials = await prisma.material.findMany()
|
||||
for (const material of materials) {
|
||||
const user = await prisma.user.findUnique({ where: { id: material.userId } })
|
||||
// N+1查询问题
|
||||
}
|
||||
|
||||
// ✅ 好的做法:使用include
|
||||
const materials = await prisma.material.findMany({
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 问题:缺少索引
|
||||
```prisma
|
||||
// ✅ 添加必要的索引
|
||||
model Material {
|
||||
// ...
|
||||
userId String
|
||||
createdAt DateTime
|
||||
|
||||
@@index([userId]) // 按用户查询
|
||||
@@index([createdAt]) // 按时间排序
|
||||
@@index([userId, createdAt]) // 复合索引
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 缓存策略
|
||||
|
||||
#### 实现方案
|
||||
```typescript
|
||||
// 简单的内存缓存(适用于单实例部署)
|
||||
const cache = new Map<string, { data: any, expires: number }>()
|
||||
|
||||
function getCached<T>(key: string, ttl: number = 60000): T | null {
|
||||
const item = cache.get(key)
|
||||
if (!item) return null
|
||||
|
||||
if (Date.now() > item.expires) {
|
||||
cache.delete(key)
|
||||
return null
|
||||
}
|
||||
|
||||
return item.data as T
|
||||
}
|
||||
|
||||
function setCached(key: string, data: any, ttl: number = 60000) {
|
||||
// 限制缓存大小
|
||||
if (cache.size > 1000) {
|
||||
const firstKey = cache.keys().next().value
|
||||
cache.delete(firstKey)
|
||||
}
|
||||
|
||||
cache.set(key, {
|
||||
data,
|
||||
expires: Date.now() + ttl
|
||||
})
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
export async function GET() {
|
||||
const cacheKey = 'system-settings'
|
||||
let settings = getCached(cacheKey, 60000) // 缓存1分钟
|
||||
|
||||
if (!settings) {
|
||||
settings = await prisma.systemSettings.findFirst()
|
||||
setCached(cacheKey, settings, 60000)
|
||||
}
|
||||
|
||||
return NextResponse.json(settings)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 文件上传优化
|
||||
|
||||
#### 当前实现
|
||||
- ✅ 使用流式处理
|
||||
- ✅ 图片压缩(Sharp)
|
||||
- ✅ 并发限制
|
||||
|
||||
#### 待优化
|
||||
```typescript
|
||||
// ✅ 实现上传队列
|
||||
class UploadQueue {
|
||||
private queue: Array<() => Promise<void>> = []
|
||||
private processing = 0
|
||||
private maxConcurrent = 3 // 限制并发数
|
||||
|
||||
async add(task: () => Promise<void>) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.queue.push(async () => {
|
||||
try {
|
||||
await task()
|
||||
resolve()
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
this.process()
|
||||
})
|
||||
}
|
||||
|
||||
private async process() {
|
||||
if (this.processing >= this.maxConcurrent || this.queue.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
this.processing++
|
||||
const task = this.queue.shift()!
|
||||
|
||||
try {
|
||||
await task()
|
||||
} finally {
|
||||
this.processing--
|
||||
this.process() // 处理下一个任务
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uploadQueue = new UploadQueue()
|
||||
|
||||
// 使用
|
||||
await uploadQueue.add(async () => {
|
||||
await uploadFile(file)
|
||||
})
|
||||
```
|
||||
|
||||
### 4. API响应优化
|
||||
|
||||
#### 分页
|
||||
```typescript
|
||||
// ✅ 所有列表API都实现分页
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const page = parseInt(searchParams.get('page') || '1', 10)
|
||||
const pageSize = Math.min(parseInt(searchParams.get('pageSize') || '20', 10), 100)
|
||||
const skip = (page - 1) * pageSize
|
||||
|
||||
const [materials, total] = await Promise.all([
|
||||
prisma.material.findMany({
|
||||
skip,
|
||||
take: pageSize,
|
||||
orderBy: { createdAt: 'desc' }
|
||||
}),
|
||||
prisma.material.count()
|
||||
])
|
||||
|
||||
return NextResponse.json({
|
||||
data: materials,
|
||||
pagination: {
|
||||
page,
|
||||
pageSize,
|
||||
total,
|
||||
totalPages: Math.ceil(total / pageSize)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### 字段选择
|
||||
```typescript
|
||||
// ✅ 只返回必要的字段
|
||||
const materials = await prisma.material.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
previewUrl: true,
|
||||
createdAt: true,
|
||||
// 不返回大字段如zipFilePath
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 性能监控
|
||||
|
||||
### 添加性能监控中间件
|
||||
|
||||
```typescript
|
||||
// middleware.ts
|
||||
export function middleware(request: NextRequest) {
|
||||
const startTime = Date.now()
|
||||
|
||||
// 记录慢请求
|
||||
const response = NextResponse.next()
|
||||
|
||||
response.headers.set('X-Response-Time', `${Date.now() - startTime}ms`)
|
||||
|
||||
// 如果响应时间超过1秒,记录警告
|
||||
if (Date.now() - startTime > 1000) {
|
||||
console.warn(`[Performance] Slow request: ${request.url} took ${Date.now() - startTime}ms`)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
```
|
||||
|
||||
### 数据库查询监控
|
||||
|
||||
```typescript
|
||||
// lib/prisma.ts
|
||||
const prisma = new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development'
|
||||
? [{ emit: 'event', level: 'query' }]
|
||||
: ['error'],
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
prisma.$on('query' as never, (e: any) => {
|
||||
if (e.duration > 100) {
|
||||
console.warn(`[DB] Slow query (${e.duration}ms):`, e.query)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 性能测试
|
||||
|
||||
### 测试场景
|
||||
|
||||
1. **并发上传测试**
|
||||
- 10个用户同时上传100MB文件
|
||||
- 目标:所有上传成功,无内存泄漏
|
||||
|
||||
2. **数据库压力测试**
|
||||
- 1000个并发查询
|
||||
- 目标:响应时间 < 200ms
|
||||
|
||||
3. **内存泄漏测试**
|
||||
- 运行24小时,监控内存使用
|
||||
- 目标:内存使用稳定,无持续增长
|
||||
|
||||
### 测试工具
|
||||
|
||||
```bash
|
||||
# 使用Apache Bench进行压力测试
|
||||
ab -n 1000 -c 10 http://localhost:4320/api/materials
|
||||
|
||||
# 使用Artillery进行负载测试
|
||||
artillery quick --count 100 --num 10 http://localhost:4320/api/materials
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 持续优化
|
||||
|
||||
### 定期检查项
|
||||
|
||||
1. **每周检查**
|
||||
- 查看慢查询日志
|
||||
- 检查内存使用趋势
|
||||
- 分析错误日志
|
||||
|
||||
2. **每月优化**
|
||||
- 优化慢查询
|
||||
- 清理无用代码
|
||||
- 更新依赖版本
|
||||
|
||||
3. **每季度评估**
|
||||
- 性能基准测试
|
||||
- 架构优化建议
|
||||
- 技术债务清理
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
|
||||
@ -1,125 +0,0 @@
|
||||
# 恢复到服务器部署模式
|
||||
|
||||
## ✅ 已完成的操作
|
||||
|
||||
### 1. 删除本地开发相关文件
|
||||
|
||||
#### 根目录文件
|
||||
- ✅ `docker-compose.local.yml` - 本地开发数据库配置
|
||||
- ✅ `启动本地数据库.bat` - 启动本地数据库脚本
|
||||
- ✅ `启动Docker并数据库.bat` - 启动 Docker 和数据库脚本
|
||||
- ✅ `安装Docker并配置PATH.bat` - Docker 安装脚本
|
||||
- ✅ `快速修复数据库连接.bat` - 数据库连接修复脚本
|
||||
- ✅ `修复虚拟化支持.bat` - 虚拟化支持修复脚本
|
||||
- ✅ `更新WSL并启动Docker.bat` - WSL 更新脚本
|
||||
- ✅ `配置Docker-PATH.ps1` - Docker PATH 配置脚本
|
||||
- ✅ `数据库连接问题解决.md` - 数据库连接问题文档
|
||||
- ✅ `WSL更新指南.md` - WSL 更新指南
|
||||
- ✅ `虚拟化支持修复指南.md` - 虚拟化支持修复指南
|
||||
- ✅ `Docker安装完整指南.md` - Docker 安装指南
|
||||
|
||||
#### scripts/tools 目录文件
|
||||
- ✅ `scripts/tools/安装Docker并启动本地数据库.bat`
|
||||
- ✅ `scripts/tools/start-local-dev.ps1`
|
||||
- ✅ `scripts/tools/start-local-dev.sh`
|
||||
- ✅ `scripts/tools/setup-local-db.sh`
|
||||
- ✅ `scripts/tools/启动开发服务器.bat`
|
||||
|
||||
#### 文档文件
|
||||
- ✅ `docs/guides/README-本地开发.md`
|
||||
- ✅ `docs/guides/快速启动本地开发.md`
|
||||
- ✅ `docs/guides/快速设置本地数据库.md`
|
||||
- ✅ `docs/guides/设置本地数据库.md`
|
||||
|
||||
## ✅ 项目配置状态
|
||||
|
||||
### Docker 配置
|
||||
- ✅ `docker-compose.yml` - 使用服务器数据库配置(从 `.env` 文件读取)
|
||||
- ✅ 项目使用服务器部署模式,通过 `.env` 文件配置数据库连接
|
||||
|
||||
### 部署工具
|
||||
- ✅ `scripts/deploy/安全部署.ps1` - Windows 安全部署脚本
|
||||
- ✅ `scripts/deploy/安全部署.sh` - Linux/Mac 安全部署脚本
|
||||
- ✅ `部署前检查清单.bat` - 部署前检查脚本
|
||||
|
||||
### 环境配置
|
||||
- ✅ `.env` 文件配置服务器数据库连接
|
||||
- ✅ `.gitignore` 已配置忽略 `.env*.local` 文件
|
||||
|
||||
## 📋 当前部署方式
|
||||
|
||||
### 服务器部署流程
|
||||
|
||||
1. **本地开发**
|
||||
- 在本地进行代码开发和测试
|
||||
- 使用 Git 进行版本控制
|
||||
|
||||
2. **部署前检查**
|
||||
```bash
|
||||
.\部署前检查清单.bat
|
||||
```
|
||||
|
||||
3. **执行部署**
|
||||
```bash
|
||||
# Windows PowerShell
|
||||
.\scripts\deploy\安全部署.ps1
|
||||
|
||||
# Linux/Mac
|
||||
bash scripts/deploy/安全部署.sh
|
||||
```
|
||||
|
||||
4. **验证部署**
|
||||
- 检查服务器上的 Docker 容器是否正常运行
|
||||
- 验证应用程序是否可以正常访问
|
||||
|
||||
## 🔍 验证项目状态
|
||||
|
||||
### 检查 Docker 配置
|
||||
```bash
|
||||
# 查看 docker-compose.yml
|
||||
cat docker-compose.yml
|
||||
|
||||
# 确认使用 .env 文件
|
||||
grep -i "env_file" docker-compose.yml
|
||||
```
|
||||
|
||||
### 检查部署脚本
|
||||
```bash
|
||||
# Windows
|
||||
Test-Path scripts\deploy\安全部署.ps1
|
||||
|
||||
# Linux/Mac
|
||||
test -f scripts/deploy/安全部署.sh && echo "存在"
|
||||
```
|
||||
|
||||
### 检查环境变量
|
||||
```bash
|
||||
# 确认 .env 文件存在(不提交到 Git)
|
||||
# .env 文件应该包含服务器数据库连接信息
|
||||
```
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **数据库连接**
|
||||
- 项目现在使用服务器数据库(通过 `.env` 文件配置)
|
||||
- 不再使用本地 Docker 数据库
|
||||
|
||||
2. **部署流程**
|
||||
- 使用 `scripts/deploy/安全部署.ps1` 或 `安全部署.sh` 进行部署
|
||||
- 部署前运行 `部署前检查清单.bat` 进行检查
|
||||
|
||||
3. **环境变量**
|
||||
- 确保 `.env` 文件包含正确的服务器数据库连接信息
|
||||
- `.env` 文件不会提交到 Git(已在 `.gitignore` 中配置)
|
||||
|
||||
4. **本地开发**
|
||||
- 如果需要本地开发,可以手动配置 `.env.local` 文件
|
||||
- `.env.local` 文件不会被提交到 Git
|
||||
|
||||
## ✅ 恢复完成
|
||||
|
||||
项目已成功恢复到服务器部署模式,所有本地开发相关的文件和配置已清理完毕。
|
||||
|
||||
现在可以使用安全部署工具将代码部署到服务器,不会影响正式服务器的正常使用。
|
||||
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
## 新手用户上手指引
|
||||
|
||||
这份指引面向**首次接触 FLNXI 素材库**的普通成员,帮助他们在 10 分钟内完成账号激活、素材上传、权限确认与移交。管理员可在培训/迎新时直接引用。
|
||||
|
||||
### 1. 获取账号并首次登录
|
||||
1. 打开管理员发送的登录地址(示例:`https://materials.example.com/login`)。
|
||||
2. 输入分配的账号密码或使用钉钉扫码(若已开启)。
|
||||
3. 首次登录后,系统会提示**修改默认密码**,请设置一个长度 ≥ 8 位、包含数字和字母的新密码。
|
||||
4. 进入“个人资料”页补齐真实姓名、部门、联系方式,确保后续素材归属准确展示。
|
||||
|
||||
### 2. 了解素材归属 / 所有权
|
||||
- 每份素材都包含两个身份信息:
|
||||
- `createdByUserId`:最初上传者(用来追溯责任人)。
|
||||
- `ownerType + ownerId`:当前归属(个人/团队)。离职移交后只会更新归属,不会丢失上传者信息。
|
||||
- 在“我的素材”列表中可看到自己上传并归属到个人的作品;若素材被移交给团队,该列表会同步更新。
|
||||
|
||||
### 3. 上传与预览流程(以视频为例)
|
||||
1. 点击右上角“上传素材”,选择匹配的模板。
|
||||
2. 填写基础信息:品牌、系列、SKU、材质、场景等。
|
||||
3. 上传源文件(压缩包/视频)。上传结束后系统会:
|
||||
- 调用 FFmpeg 生成 540P 预览和 10 秒短片;
|
||||
- 自动截图生成封面,保持与预览视频一致的比例;
|
||||
- 删除本地临时源文件,避免占用 NAS 空间。
|
||||
4. 在首页卡片或详情弹窗中即可查看转码进度:
|
||||
- `pending`:等待转码(展示“预览生成中…”占位图);
|
||||
- `processing`:FFmpeg 运行中;
|
||||
- `done`:可播放/下载;
|
||||
- `failed`:可点击“重新转码”或联系管理员。
|
||||
|
||||
### 4. 分享与互动
|
||||
- **收藏/点赞**:在卡片上直接操作,数据会实时同步到个人中心的“我的收藏”。
|
||||
- **下载**:根据权限决定是否显示按钮;下载记录会写入 `material_download_logs`,便于审计。
|
||||
- **分享链接**:点击卡片右上角“分享”生成短链,可面向外部预览但不允许下载原文件。
|
||||
|
||||
### 5. 所有权移交 / 离职交接
|
||||
- 若素材需要交给其他成员或团队空间,请联系管理员在后台执行“素材移交”:
|
||||
1. 管理员选择目标素材 → 点击“移交所有权”;
|
||||
2. 选择目标类型:`USER`(个人)或 `TEAM`(部门/项目组);
|
||||
3. 保存后系统会记录 `lastTransferredAt`,并在详情页展示最新归属。
|
||||
- 被移交的素材依旧保留原作者信息,可在日志中查看完整操作历史。
|
||||
|
||||
### 6. 常见问题
|
||||
| 场景 | 解决方案 |
|
||||
| --- | --- |
|
||||
| 登录后提示“账号未激活” | 联系管理员在用户管理中启用 `isActive` |
|
||||
| 上传后长时间显示“等待转码” | 处理队列可能被占满,刷新或通知管理员检查 FFmpeg/队列服务 |
|
||||
| 详情弹窗显示黑边或白屏 | 按 F5 强刷;若仍异常,请记下素材 ID 反馈给研发 |
|
||||
| 我的素材为空 | 确认是否刚执行过所有权移交或系统刚恢复出厂,可重新上传或联系管理员核对归属 |
|
||||
|
||||
### 7. 建议的“新人成长任务”
|
||||
1. 上传一份个人作品并补齐所有元数据。
|
||||
2. 将素材分享到公共链接,邀请同事点赞/收藏。
|
||||
3. 体验一次素材移交(可与同组同事互换),理解离职交接流程。
|
||||
4. 在个人中心下载自己的素材压缩包,验证解压内容正确。
|
||||
|
||||
完成以上步骤后,即可熟练掌握素材库的核心操作。若需要更深入的后台/模板管理权限,请参考管理员文档或联系系统负责人开通角色。
|
||||
|
||||
@ -1,817 +0,0 @@
|
||||
## 🧾 2025-12-03 · 「NAS 一键部署 & Harbor 镜像」版本 🚢
|
||||
|
||||
### ⚙️ 一键安装与卸载脚本
|
||||
|
||||
- ✅ `scripts/tools/install.sh`
|
||||
- NAS 场景一键安装:自动检测 Docker 权限并在需要时使用 sudo
|
||||
- 可选择“测试 / 正式”环境,对应 Harbor `pro-test` / `pro-latest` 标签
|
||||
- 自动生成 `.env.production`,支持自定义初始管理员邮箱与密码
|
||||
- 安装结束自动提示访问地址,减少人工配置步骤
|
||||
- ✅ 新增 `scripts/tools/uninstall.sh`
|
||||
- 一键停止/删除容器、卷、网络,可选清理 `.env.production`
|
||||
- 支持 `docker system prune` 清理镜像缓存,方便反复测试
|
||||
|
||||
### 🗂 环境与配置收敛
|
||||
|
||||
- ✅ 合并 `.env` 配置,统一生成 `.env.production` 与 `config-bianliang.env`
|
||||
- ✅ `app/api/system/init/route.ts` 读取 `INIT_ADMIN_EMAIL/PASSWORD`,杜绝硬编码账号
|
||||
- ✅ `docker-compose.prod.yml`
|
||||
- 全量改用 `harbor.flnxi.com/flnxi` 镜像(app/postgres/minio/mc)
|
||||
- 默认 S3 Bucket 改为 `flnxi-materials`,Public Domain 自动拼接 NAS 内网 IP
|
||||
- 容器名称统一改为 `flnxi-*`
|
||||
|
||||
### 🛳 Harbor 镜像管理
|
||||
|
||||
- ✅ 新增 `scripts/tools/push-to-harbor.ps1`
|
||||
- 自动加载基础镜像(优先本地→Harbor→离线包)
|
||||
- 构建完成后验证镜像、登录 Harbor、推送 `pro-latest`
|
||||
- ✅ 新增文档 `docs/guides/HARBOR_ENV_CONFIG.md`,说明 `pro-test` (私有) / `pro-latest` (公开) 策略
|
||||
- ✅ 镜像构建默认启用 Prisma 引擎缓存与 npm 镜像,兼容 NAS 无外网场景
|
||||
|
||||
### 🧩 功能与体验同步
|
||||
|
||||
- ✅ 素材主人变更 API (`app/api/admin/materials/transfer-owner/route.ts`)
|
||||
- 当素材转移到个人时同步 `authorName`、`userId`
|
||||
- 转移到团队仍保持原作者信息
|
||||
- ✅ `components/MaterialPreviewModal.tsx`
|
||||
- 去除“素材 ID / DNZ0128”冗余信息,弹窗更聚焦
|
||||
- ✅ README / NAS 指南同步新增的一键部署说明
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「Bug修复与UI优化」版本 🐛
|
||||
|
||||
### 🐛 Bug修复
|
||||
|
||||
**修复内容**:
|
||||
- ✅ 修复上传文件500错误
|
||||
- 修复视频类型预览图处理逻辑,添加空值检查
|
||||
- 优化预览图验证逻辑,支持多预览图场景
|
||||
- 确保所有错误都被正确捕获和返回
|
||||
- ✅ 个人中心添加返回按钮
|
||||
- 使用统一UI按钮样式
|
||||
- 添加Header组件,保持页面一致性
|
||||
- 返回按钮使用统一UI样式系统
|
||||
- ✅ 用户管理弹窗统一UI
|
||||
- 将UserEditModal改为使用统一Modal组件
|
||||
- 将TransferSuperAdminModal改为使用统一Modal组件
|
||||
- 将InvitationsTab的创建邀请弹窗改为使用统一Modal组件
|
||||
- 所有弹窗现在都有毛玻璃效果和统一阴影
|
||||
- ✅ 统一UI弹窗阴影效果优化
|
||||
- 确保所有弹窗都有阴影效果
|
||||
- 优化浅色和深色模式的阴影配置
|
||||
- 增强阴影可见度,提升视觉层次
|
||||
|
||||
**改进效果**:
|
||||
- 上传功能更稳定,错误处理更完善
|
||||
- 个人中心页面更完整,导航更便捷
|
||||
- 所有弹窗UI统一,视觉效果更一致
|
||||
- 阴影效果更明显,提升视觉层次感
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「用户信息同步」版本 🔄
|
||||
|
||||
### 🔄 用户信息同步功能
|
||||
|
||||
**功能说明**:
|
||||
- ✅ 管理员修改用户信息后,该用户上传的所有素材信息自动同步更新
|
||||
- ✅ 当用户名发生变化时,自动更新该用户所有素材的`authorName`字段
|
||||
- ✅ 使用数据库事务确保数据一致性
|
||||
- ✅ 前端显示同步成功提示
|
||||
|
||||
**技术实现**:
|
||||
- 在`/api/admin/users/update` API中实现同步逻辑
|
||||
- 使用Prisma事务(`$transaction`)确保用户更新和素材更新原子性
|
||||
- 检测用户名变化,仅在有变化时更新素材
|
||||
- 返回`materialsUpdated`标志,前端根据此标志显示相应提示
|
||||
|
||||
**改进效果**:
|
||||
- 数据一致性得到保障
|
||||
- 管理员修改用户信息后,素材信息自动保持同步
|
||||
- 用户体验更好,有明确的同步成功提示
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「性能监控导出CSV」版本 📊
|
||||
|
||||
### 📥 性能监控导出CSV功能
|
||||
|
||||
**功能说明**:
|
||||
- ✅ 在系统监控页面添加"导出CSV"按钮
|
||||
- ✅ 导出包含所有监控数据:
|
||||
- 系统概览(总用户数、活跃用户、总素材数等)
|
||||
- 用户增长趋势(最近12个月/30天)
|
||||
- 素材上传趋势(最近12个月/30天)
|
||||
- 部门分布统计
|
||||
- 权限分布统计
|
||||
- 最近活动记录
|
||||
- 系统信息(站点名称、注册功能状态等)
|
||||
- ✅ 支持Excel正确显示中文(添加BOM)
|
||||
- ✅ 文件名包含导出日期,方便归档
|
||||
- ✅ 同时优化了MonitorTab组件和独立监控页面
|
||||
|
||||
**技术实现**:
|
||||
- 使用原生JavaScript实现CSV生成,无需额外依赖
|
||||
- 自动处理中文和特殊字符
|
||||
- 使用Blob API创建下载文件
|
||||
- 添加UTF-8 BOM确保Excel正确显示中文
|
||||
|
||||
**改进效果**:
|
||||
- 方便数据分析和存档
|
||||
- 支持离线查看历史数据
|
||||
- 便于生成报表和报告
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「系统监控交互增强」版本 📈
|
||||
|
||||
### 📊 系统监控交互增强
|
||||
|
||||
**优化内容**:
|
||||
- ✅ 系统监控数据卡片可点击跳转
|
||||
- 总用户数、活跃用户、本月新用户 → 跳转到用户管理页面
|
||||
- 总素材数、本月新素材 → 跳转到素材列表页面
|
||||
- ✅ 添加hover效果和点击提示
|
||||
- ✅ 在素材上传趋势卡片前增加"当月上传趋势"显示
|
||||
- ✅ 同时优化了MonitorTab组件和独立监控页面
|
||||
|
||||
**改进效果**:
|
||||
- 数据可操作性更强,管理员可以快速查看详细数据
|
||||
- 当月上传趋势一目了然
|
||||
- 交互体验更流畅,hover效果清晰提示可点击
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「菜单调整优化」版本 🎨
|
||||
|
||||
### 📋 菜单调整优化
|
||||
|
||||
**优化内容**:
|
||||
- ✅ 将"系统设置"改名为"管理",更换图标(使用滑块图标)
|
||||
- ✅ 将"外观设置"移到头像菜单中,添加调色板图标
|
||||
- ✅ 优化菜单结构,使功能分类更清晰
|
||||
|
||||
**改进效果**:
|
||||
- 菜单命名更简洁直观
|
||||
- 外观设置更容易访问
|
||||
- 管理员功能(管理)和用户功能(外观设置)区分更明确
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「多预览图上传、用户头像设置和个人中心」版本 🎨
|
||||
|
||||
### 🖼️ 多预览图上传功能
|
||||
|
||||
**功能说明**:
|
||||
- ✅ 支持上传最多5张预览图
|
||||
- ✅ 第一张预览图自动设为封面
|
||||
- ✅ 支持多文件选择和拖拽上传
|
||||
- ✅ 预览图列表显示,可单独删除
|
||||
- ✅ 多预览图URLs存储在metadata中(JSON格式)
|
||||
|
||||
**技术实现**:
|
||||
- 修改了UploadModal组件,将单张预览图改为数组存储
|
||||
- 修改了上传API,支持处理多张预览图
|
||||
- 每张预览图都会压缩并转换为WebP格式
|
||||
- 第一张预览图作为主预览图(previewUrl),向后兼容
|
||||
|
||||
---
|
||||
|
||||
### 👤 用户头像设置功能
|
||||
|
||||
**功能说明**:
|
||||
- ✅ 用户可以在个人中心上传头像
|
||||
- ✅ 头像支持JPG、PNG、WebP格式
|
||||
- ✅ 头像自动压缩和优化(最大5MB,压缩到200KB以内)
|
||||
- ✅ 头像显示在Header组件和Avatar组件中
|
||||
- ✅ 钉钉登录用户头像自动同步(如果可用)
|
||||
|
||||
**技术实现**:
|
||||
- 创建了`/api/user/avatar` API端点
|
||||
- 头像上传到S3存储
|
||||
- 更新了auth配置,在session中包含avatar信息
|
||||
- Header组件中的Avatar组件支持显示用户头像
|
||||
|
||||
---
|
||||
|
||||
### 🏠 个人中心功能
|
||||
|
||||
**功能说明**:
|
||||
- ✅ 在Header菜单中添加了"个人中心"选项
|
||||
- ✅ 创建了个人中心页面(`/profile`)
|
||||
- ✅ 显示用户基本信息(用户名、邮箱、部门、手机等)
|
||||
- ✅ 显示用户上传的所有素材
|
||||
- ✅ 支持在个人中心上传和修改头像
|
||||
|
||||
**技术实现**:
|
||||
- 创建了`/api/user/profile` API获取用户信息
|
||||
- 创建了`/api/user/materials` API获取用户素材列表
|
||||
- 个人中心页面使用三层架构样式系统
|
||||
- 响应式设计,支持移动端
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「上传面板进度条位置优化」版本 🎨
|
||||
|
||||
### 📍 上传面板进度条位置优化
|
||||
|
||||
**优化内容**:
|
||||
- ✅ 将上传进度条移到"素材文件"上传区域下方
|
||||
- ✅ 将错误信息和成功提示移到"素材文件"上传区域下方
|
||||
- ✅ 优化用户体验,进度和提示信息更贴近上传操作区域
|
||||
|
||||
**改进效果**:
|
||||
- 进度条和提示信息位置更合理,用户无需滚动到底部查看
|
||||
- 上传状态反馈更及时、更直观
|
||||
- 符合用户操作习惯,信息展示更集中
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「素材卡片大小设置和瀑布流布局」版本 🎨
|
||||
|
||||
### 🎚️ 素材卡片大小设置和瀑布流布局
|
||||
|
||||
**1. 卡片大小滑块控制**
|
||||
- ✅ 添加了横向滑块控制卡片大小(5个级别:极小、小、中、大、极大)
|
||||
- ✅ 滑块值保存到localStorage,用户偏好持久化
|
||||
- ✅ 根据卡片大小动态调整网格列数
|
||||
- 极小:2-6列
|
||||
- 小:2-5列
|
||||
- 中:1-4列(默认)
|
||||
- 大:1-3列
|
||||
- 极大:1-2列
|
||||
|
||||
**2. 布局模式切换**
|
||||
- ✅ 添加了网格和瀑布流两种布局模式切换
|
||||
- ✅ 布局模式保存到localStorage
|
||||
- ✅ 网格模式:使用CSS Grid,固定比例(3:4)
|
||||
- ✅ 瀑布流模式:使用CSS Columns,内容自然流动
|
||||
|
||||
**3. 动态加载优化**
|
||||
- ✅ 初始加载24个素材,减少首屏加载时间
|
||||
- ✅ 使用IntersectionObserver实现自动懒加载
|
||||
- ✅ 滚动到底部自动加载更多(每次加载24个)
|
||||
- ✅ 提供"加载更多"按钮作为备选方案
|
||||
- ✅ 筛选条件改变时自动重置显示数量
|
||||
|
||||
**4. 性能优化**
|
||||
- ✅ 瀑布流模式下图片使用width/height而非fill,避免布局抖动
|
||||
- ✅ 懒加载减少初始渲染压力,适合低性能NAS机器
|
||||
- ✅ 使用break-inside-avoid确保卡片在瀑布流中不被分割
|
||||
|
||||
**5. 视觉体验**
|
||||
- ✅ 滑块样式统一美观,符合Apple设计风格
|
||||
- ✅ 布局切换按钮使用图标+文字,清晰直观
|
||||
- ✅ 加载状态有明确的视觉反馈
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「素材卡片标签系统」版本 🎨
|
||||
|
||||
### 🏷️ 素材卡片标签系统
|
||||
|
||||
**1. 排行榜标签显示**
|
||||
- ✅ 创建了素材排行榜API(`/api/material-rankings`)
|
||||
- 下载榜:下载次数最多的前10个素材
|
||||
- 点赞榜:点赞次数最多的前10个素材
|
||||
- 精品榜:综合评分最高的前10个素材(下载数*0.6 + 点赞数*0.4)
|
||||
- ✅ 在首页素材卡片上显示排行榜标签
|
||||
- 精品榜:使用warning变体的Badge组件(橙色)
|
||||
- 下载榜:使用primary变体的Badge组件(蓝色)
|
||||
- 点赞榜:使用danger变体的Badge组件(红色)
|
||||
- ✅ 标签显示在分类标签组的最前面,视觉突出
|
||||
- ✅ 使用三层架构样式系统,符合统一UI设计
|
||||
|
||||
**2. 视觉体验优化**
|
||||
- ✅ 标签使用Badge组件,样式统一美观
|
||||
- ✅ 标签尺寸为sm,不会占用过多空间
|
||||
- ✅ 多个标签可以同时显示(如同时是精品榜和下载榜)
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「素材卡片信息优化」版本 🎨
|
||||
|
||||
### 🎨 首页素材卡片优化
|
||||
|
||||
**1. 素材卡片信息精简**
|
||||
- ✅ 移除了"作者:"文字,直接显示作者名
|
||||
- ✅ 使用Avatar组件在作者名前显示头像(文字头像,显示首字母)
|
||||
- ✅ 移除了文件名和文件大小显示,减少信息冗余
|
||||
- ✅ 将上传时间移到预览图右上角,默认隐藏,hover时显示
|
||||
- ✅ 优化了卡片布局,信息更简洁清晰
|
||||
|
||||
**2. 视觉体验提升**
|
||||
- ✅ 上传时间使用半透明黑色背景,与SPU标签样式统一
|
||||
- ✅ hover时显示上传时间,提供更多信息而不占用空间
|
||||
- ✅ 作者信息使用Avatar组件,视觉更统一
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-25 · 「弹窗样式统一与分类管理优化」版本 🎨
|
||||
|
||||
### 🎨 UI统一优化
|
||||
|
||||
**1. 统一弹窗样式系统**
|
||||
- ✅ 创建了统一的Modal组件,采用Apple风格设计
|
||||
- ✅ 实现了毛玻璃效果(backdrop-blur),与顶部Header保持一致
|
||||
- ✅ 添加了32px圆角,符合统一UI设计规范
|
||||
- ✅ 实现了Apple风格的开启动画(scale + translateY + blur效果)
|
||||
- ✅ 添加了细边框描边效果,区分弹窗和背景
|
||||
- ✅ 使用createPortal渲染,确保弹窗正确覆盖所有内容
|
||||
- ✅ 修复了弹窗打开时背景仍可滚动的问题
|
||||
|
||||
**2. 弹窗应用范围**
|
||||
- ✅ 预览模板弹窗:应用统一样式和动画
|
||||
- ✅ 首页上传模板弹窗:应用统一样式和动画
|
||||
- ✅ 模板管理中新建/编辑模板弹窗:应用统一样式和动画
|
||||
- ✅ 修复了首页上传面板宽度不一致的问题
|
||||
- ✅ 修复了模板管理弹窗中按钮被遮挡的问题
|
||||
|
||||
**3. 分类管理优化**
|
||||
- ✅ 修复了分类管理中重复卡片显示的问题
|
||||
- ✅ 优化了选项解析逻辑,过滤掉数组字符串格式的选项
|
||||
- ✅ 在API层面添加了去重和过滤逻辑,确保数据一致性
|
||||
|
||||
### 🔧 技术改进
|
||||
|
||||
- ✅ 优化了Modal组件的渲染逻辑,支持自定义内容高度控制
|
||||
- ✅ 改进了parseOptions函数,处理双重字符串化和嵌套数组的情况
|
||||
- ✅ 在多个API端点添加了数组字符串过滤逻辑
|
||||
- ✅ 前端添加了去重逻辑,确保选项不重复显示
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-01-22 · 「模板系统优化与上传面板重构」版本 🎨
|
||||
|
||||
### 核心功能优化
|
||||
|
||||
**1. 上传面板模板化重构**
|
||||
- ✅ 移除旧的素材类型下拉选择,改用模板名称替代
|
||||
- ✅ 从模板字段中自动提取 materialType 值
|
||||
- ✅ 移除所有旧字段输入框(SPU型号、品牌、系列、SKU、材质/工艺、使用店铺、场景类别、平台分类、渲染信息等)
|
||||
- ✅ 现在只显示模板配置的字段,确保与模板管理面板预览一致
|
||||
- ✅ 修复了选择模板后旧字段不隐藏的问题
|
||||
|
||||
**2. 模板管理功能增强**
|
||||
- ✅ 添加模板取消启用功能(之前启用后无法取消)
|
||||
- ✅ 启用状态的模板现在可以取消启用,恢复为草稿状态
|
||||
|
||||
**3. 首页TAG筛选优化**
|
||||
- ✅ 调整素材类型TAG间距,从 `gap-2` 调整为 `gap-3`,视觉效果更美观
|
||||
|
||||
### 技术改进
|
||||
|
||||
- ✅ 更新 `handleSubmit` 逻辑,从 `templateFieldValues` 中提取所有字段值
|
||||
- ✅ 优化模板选择逻辑,自动从模板字段中提取 materialType 用于条件渲染
|
||||
- ✅ 修复了模板字段值同步问题
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-01-XX · 「模板管理整合、首页TAG筛选与上传进度优化」版本 🎨
|
||||
|
||||
本次更新完成了模板管理整合到上传和分类功能,实现了动态字段渲染,新增了首页类型筛选TAG系统,并优化了上传进度体验。
|
||||
|
||||
---
|
||||
|
||||
### 🔗 核心功能:模板管理整合
|
||||
|
||||
- ✨ **上传面板模板化**
|
||||
- 将"素材类型"改为"素材模板",从模板管理拉取启用的模板列表
|
||||
- 选择模板后动态显示对应模板的字段(支持 text、textarea、number、select、checkbox、radio、date 等类型)
|
||||
- 根据字段的 `layoutWidth` 属性支持半行/整行布局
|
||||
- 自动验证必填字段,提升用户体验
|
||||
|
||||
- 📦 **数据存储优化**
|
||||
- 上传时提交模板ID和字段值到数据库
|
||||
- 在 `metadata` 字段中存储模板字段值(JSON格式)
|
||||
- 在 `templateId` 字段中直接存储模板ID,方便查询和统计
|
||||
|
||||
- 🏷️ **分类管理模板化**
|
||||
- 创建模板分类 API,从模板管理中提取所有 `visibleOnHomeFilter=true` 且 `type='select'` 的字段选项
|
||||
- 分类管理界面显示从模板中提取的分类选项
|
||||
- 保留原有分类管理用于向后兼容
|
||||
|
||||
- 🎯 **首页类型筛选TAG系统**
|
||||
- 创建 `/api/material-types` API:从模板管理中提取 `materialType` 字段的选项
|
||||
- 首页使用TAG形式显示素材类型筛选,替代原有的固定类型列表
|
||||
- 第一个TAG是"全部",点击可清除筛选
|
||||
- 使用 `Tag` 组件和低饱和度炫彩元素样式,支持多种颜色循环使用
|
||||
- 选中状态使用 ring 样式突出显示,未选中状态使用透明度变化
|
||||
- 提升用户体验,筛选更加直观便捷
|
||||
|
||||
- ⚡ **上传素材进度优化**
|
||||
- 添加 `uploadComplete` 状态标识上传完成
|
||||
- 进度100%后立即显示完成状态(进度条变绿色,显示"上传完成")
|
||||
- 添加300ms延迟确保数据保存完成后再关闭弹窗
|
||||
- 优化用户体验:显示"✓ 上传成功,正在关闭..."提示
|
||||
- 修复所有错误处理路径,确保状态正确重置
|
||||
|
||||
---
|
||||
|
||||
### 🎨 UI优化
|
||||
|
||||
- ✨ **低饱和度炫彩元素设计系统**
|
||||
- 添加 `accentStyles` 配置,支持多种低饱和度颜色主题(pink、purple、blue、cyan、emerald、orange、rainbow)
|
||||
- 更新 `Tag` 组件使用新的炫彩元素样式,提升视觉美感
|
||||
- 确保深浅色模式都有对应的炫彩元素
|
||||
|
||||
- 🔧 **深浅色主题适配优化**
|
||||
- 优化 `UploadModal` 组件基础样式(gray → slate,使用三层架构)
|
||||
- 优化分享页面和编辑页面样式统一
|
||||
- 所有组件已迁移到三层架构样式系统
|
||||
|
||||
---
|
||||
|
||||
### 🔧 功能完善
|
||||
|
||||
- ✅ **用户信息修改后自动同步素材信息**
|
||||
- 当用户名修改时,自动更新该用户所有素材的 `authorName` 字段
|
||||
- 使用事务确保数据一致性,前端显示同步提示
|
||||
|
||||
---
|
||||
|
||||
### 🛠️ 技术改进
|
||||
|
||||
- ✅ 完善三层架构样式系统
|
||||
- 在颜色层添加 `accent` 配置,支持低饱和度炫彩元素
|
||||
- 更新样式组合器,导出 `accentStyles` 供组件使用
|
||||
- 所有组件已添加文件头注释,代码可维护性提升
|
||||
|
||||
- ✅ 修复 Input 组件类型冲突
|
||||
- 使用 `Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>` 解决 size 属性类型冲突
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-22 · 「首页UI完全统一 & 通用组件库创建」版本 🎨
|
||||
|
||||
本次更新完成了首页UI的完全统一到三层架构系统,并创建了通用组件库,为后续开发提供了可复用的基础组件。
|
||||
|
||||
---
|
||||
|
||||
### 🎨 首页UI完全统一到三层架构
|
||||
|
||||
- ✨ **颜色系统完全统一**
|
||||
- ✅ 将所有 `gray` 系列替换为 `slate` 系列
|
||||
- ✅ 统一文本颜色(`text-gray-*` → `text-slate-*`)
|
||||
- ✅ 统一背景颜色(`bg-gray-*` → `bg-slate-*`)
|
||||
- ✅ 统一边框颜色(`border-gray-*` → `border-slate-*`)
|
||||
- ✅ 确保深浅色模式完美适配
|
||||
|
||||
- 🔧 **组件样式统一**
|
||||
- ✅ 统一按钮样式(使用 `buttonStyles.*` 和 `buttonModifiers.*`)
|
||||
- ✅ 统一输入框样式(使用 `inputStyles.*`)
|
||||
- ✅ 统一卡片样式(使用 `cardStyles.*`)
|
||||
- ✅ 统一分页按钮样式
|
||||
- ✅ 统一分享弹窗样式
|
||||
|
||||
- 📦 **标签组件应用**
|
||||
- ✅ 使用新的 `Tag` 组件替换原有的标签样式
|
||||
- ✅ 统一标签颜色和尺寸
|
||||
- ✅ 确保标签在深浅色模式下完美显示
|
||||
|
||||
---
|
||||
|
||||
### 🧩 通用组件库创建
|
||||
|
||||
- ✨ **Badge 组件**
|
||||
- ✅ 创建了 `components/ui/Badge.tsx`
|
||||
- ✅ 支持多种变体(primary、success、warning、danger)
|
||||
- ✅ 支持多种尺寸(sm、md、lg)
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 完整的 TypeScript 类型定义
|
||||
|
||||
- 🏷️ **Tag 组件**
|
||||
- ✅ 创建了 `components/ui/Tag.tsx`
|
||||
- ✅ 支持多种颜色(blue、green、orange、purple、indigo、pink、cyan)
|
||||
- ✅ 支持多种尺寸(sm、md、lg)
|
||||
- ✅ 支持点击事件
|
||||
- ✅ 已在首页素材卡片中应用
|
||||
|
||||
- 🔘 **Button 组件**
|
||||
- ✅ 创建了 `components/ui/Button.tsx`
|
||||
- ✅ 支持多种变体(primary、secondary、ghost、danger、success)
|
||||
- ✅ 支持多种尺寸(sm、md、lg)
|
||||
- ✅ 支持加载状态(loading)
|
||||
- ✅ 支持全宽(fullWidth)
|
||||
- ✅ 使用 `forwardRef` 支持 ref 传递
|
||||
|
||||
- 📝 **Input 组件**
|
||||
- ✅ 创建了 `components/ui/Input.tsx`
|
||||
- ✅ 支持错误状态(error)
|
||||
- ✅ 支持多种尺寸(sm、md、lg)
|
||||
- ✅ 支持全宽(fullWidth)
|
||||
- ✅ 使用 `forwardRef` 支持 ref 传递
|
||||
|
||||
---
|
||||
|
||||
### 📚 文档更新
|
||||
|
||||
- 📝 **优化记录更新**
|
||||
- ✅ 更新了 `优化记录.md` - 记录本次完成的优化
|
||||
- ✅ 更新了 `优化计划与优先级.md` - 标记已完成的任务
|
||||
- ✅ 更新了 `后续修改.txt` - 更新需求进度
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-22 · 「统一弹窗系统 & 首页UI优化」版本 🎨
|
||||
|
||||
本次更新完成了统一弹窗系统的全面替换,统一了素材预览面板,并开始将首页UI迁移到三层架构系统。
|
||||
|
||||
---
|
||||
|
||||
### 🎯 统一弹窗系统
|
||||
|
||||
- ✨ **替换浏览器原生弹窗**
|
||||
- ✅ 创建了 `ConfirmDialog`、`AlertDialog`、`PromptDialog` 组件
|
||||
- ✅ 创建了 `DialogProvider` 和 `useDialog` Hook
|
||||
- ✅ 替换了所有 `confirm`、`alert`、`prompt` 调用
|
||||
- ✅ 涉及文件:所有 admin 组件、首页、分享页面、弹幕组件等
|
||||
- ✅ 统一的视觉样式,符合三层架构设计
|
||||
- ✅ 支持多种变体(default、danger、success、warning、error)
|
||||
|
||||
- 🔧 **DialogProvider 全局管理**
|
||||
- 在 `app/providers.tsx` 中添加了 `DialogProvider`
|
||||
- 提供全局的 `confirm`、`alert`、`prompt` 方法
|
||||
- 所有组件可通过 `useDialog()` Hook 使用统一弹窗
|
||||
|
||||
---
|
||||
|
||||
### 🖼️ 素材预览面板统一
|
||||
|
||||
- ✨ **MaterialPreviewModal 组件**
|
||||
- ✅ 创建了 `components/MaterialPreviewModal.tsx` 统一预览组件
|
||||
- ✅ 使用统一的 Modal 组件
|
||||
- ✅ 统一退出逻辑(ESC键、点击遮罩、关闭按钮)
|
||||
- ✅ 使用三层架构样式系统
|
||||
- ✅ 确保深浅色模式完美适配
|
||||
|
||||
- 📦 **应用范围**
|
||||
- ✅ 首页素材预览已统一使用新组件
|
||||
- ✅ 替换了原有的自定义预览弹窗实现
|
||||
|
||||
---
|
||||
|
||||
### 🎨 首页UI统一到三层架构
|
||||
|
||||
- ✨ **主要组件迁移**
|
||||
- ✅ 迁移了上传按钮样式(使用 `buttonStyles.primary`)
|
||||
- ✅ 迁移了搜索输入框样式(使用 `inputStyles.base`)
|
||||
- ✅ 迁移了筛选按钮样式(使用 `buttonStyles.secondary`)
|
||||
- ✅ 迁移了筛选弹窗中的所有按钮样式
|
||||
- 类型分页按钮
|
||||
- 使用店铺按钮
|
||||
- 场景类别按钮
|
||||
- 平台分类按钮
|
||||
- 上传月份按钮
|
||||
- ✅ 迁移了筛选弹窗容器样式(使用 `cardStyles.base`)
|
||||
- ✅ 迁移了筛选弹窗关闭按钮样式(使用 `buttonStyles.ghost`)
|
||||
|
||||
- 📝 **后续优化**
|
||||
- ⏳ 部分文本颜色和标签样式仍使用直接 Tailwind 类(可后续继续优化)
|
||||
- ⏳ 素材卡片中的标签样式可进一步统一
|
||||
|
||||
---
|
||||
|
||||
### 📚 文档更新
|
||||
|
||||
- 📝 **优化记录更新**
|
||||
- ✅ 更新了 `优化记录.md` - 记录本次完成的优化
|
||||
- ✅ 更新了 `优化计划与优先级.md` - 标记已完成的任务
|
||||
- ✅ 更新了 `后续修改.txt` - 更新需求进度
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-01-XX · 「Admin页面重构 & UI架构优化」版本 🎨
|
||||
|
||||
本次更新对Admin管理页面进行了全面重构,建立了统一UI三层架构系统,大幅提升了代码可维护性和用户体验。
|
||||
|
||||
---
|
||||
|
||||
### 🎯 UI三层架构系统
|
||||
|
||||
- ✨ **三层架构设计**
|
||||
- **基础层**:`lib/ui-styles-base.ts` - 布局、大小、动画等非颜色样式
|
||||
- **颜色层**:`lib/ui-theme-colors-light.ts` / `dark.ts` - 仅包含颜色配置
|
||||
- **组合层**:`lib/ui-styles-composer.ts` - 自动组合基础样式和颜色主题
|
||||
- **导出层**:`lib/ui-styles-light.ts` / `dark.ts` - 最终导出的样式
|
||||
- **Hook层**:`lib/hooks/useThemeStyles.ts` - 主题感知的样式 Hook
|
||||
|
||||
- 🎨 **核心优势**
|
||||
- 修改布局/大小/动画时,只需修改基础层一处,浅色和深色模式自动同步
|
||||
- 颜色配置完全分离,可以独立调整浅色或深色模式的颜色
|
||||
- 清晰的职责分离,修改时不会遗漏或出错
|
||||
- TypeScript 类型检查确保配置正确
|
||||
|
||||
- 📦 **组件迁移**
|
||||
- ✅ 所有Tab组件已完全迁移到新样式系统(MaterialsTab、CategoriesTab、InvitationsTab、DepartmentsTab、UsersTab、MonitorTab)
|
||||
- ✅ TemplateManager 完全迁移
|
||||
- ✅ 基础UI组件迁移(LoadingState、ErrorState、EmptyState)
|
||||
- ✅ Admin主页面完全迁移
|
||||
|
||||
---
|
||||
|
||||
### 🔧 代码结构优化
|
||||
|
||||
- ✨ **组件拆分**
|
||||
- 将 `page.tsx`(原3795行)拆分为多个独立Tab组件
|
||||
- ✅ `MonitorTab.tsx` - 监控仪表板
|
||||
- ✅ `UsersTab.tsx` - 用户管理
|
||||
- ✅ `DepartmentsTab.tsx` - 架构管理
|
||||
- ✅ `CategoriesTab.tsx` - 分类管理
|
||||
- ✅ `InvitationsTab.tsx` - 邀请管理
|
||||
- ✅ `MaterialsTab.tsx` - 素材管理
|
||||
- ✅ `TemplateManager.tsx` - 模板管理(已拆分为多个子组件)
|
||||
|
||||
- 🎯 **Hook提取**
|
||||
- ✅ `useToast.ts` - 统一Toast通知管理
|
||||
- ✅ `useDataFetch.ts` - 统一数据获取逻辑
|
||||
- ✅ `useUserOperations.ts` - 用户相关操作(更新、删除、权限切换等)
|
||||
- ✅ `useDepartmentOperations.ts` - 部门相关操作(创建、删除、重命名)
|
||||
- ✅ `usePermissions.ts` - 权限检查逻辑
|
||||
- ✅ `useModalState.ts` - 模态框状态管理
|
||||
- ✅ `useDashboardData.ts` - 仪表板数据获取
|
||||
|
||||
- 🛠️ **工具函数提取**
|
||||
- ✅ `validators.ts` - 表单验证逻辑(验证邮箱、部门名、密码等)
|
||||
- ✅ `formatters.ts` - 格式化函数(日期时间、文件大小、数字等)
|
||||
- ✅ `api.ts` - API错误处理和类型安全
|
||||
- ✅ `departmentTree.ts` - 部门树结构处理
|
||||
|
||||
- 🎨 **模板管理器组件化**
|
||||
- ✅ `TemplateManagerHeader.tsx` - 头部搜索和操作
|
||||
- ✅ `BuiltInTemplateGrid.tsx` - 内置模板网格
|
||||
- ✅ `CustomTemplateSection.tsx` - 自定义模板区域
|
||||
- ✅ `TemplateBasicsForm.tsx` - 基础信息表单
|
||||
- ✅ `TemplateAdvancedConfigPanel.tsx` - 高级配置面板
|
||||
- ✅ `TemplateFieldsEditor.tsx` - 字段编辑器
|
||||
- ✅ `TemplateEditorFooter.tsx` - 编辑器底部操作
|
||||
- ✅ `TemplatePreviewPanel.tsx` - 预览面板
|
||||
|
||||
---
|
||||
|
||||
### 🎯 统一弹窗组件系统
|
||||
|
||||
- ✨ **创建统一弹窗组件**
|
||||
- 新增 `components/ui/Modal.tsx` 统一弹窗组件
|
||||
- 使用三层架构设计
|
||||
- 支持多种尺寸(small, medium, large, xlarge, fullscreen)
|
||||
- 支持ESC键关闭、点击遮罩关闭
|
||||
- 完整的ARIA标签和键盘导航支持
|
||||
|
||||
- 🔧 **Hook支持**
|
||||
- 新增 `hooks/useModal.ts` Hook,统一管理弹窗状态
|
||||
- 提供 `open`、`close`、`toggle` 方法
|
||||
|
||||
**待完成**:
|
||||
- ⏳ 替换所有浏览器原生弹窗(confirm/alert)
|
||||
- ⏳ 素材预览面板统一使用新弹窗组件
|
||||
|
||||
---
|
||||
|
||||
### 🛡️ 错误处理与用户体验
|
||||
|
||||
- ✨ **错误边界**
|
||||
- 新增 `TabErrorBoundary.tsx` 组件
|
||||
- 为各个Tab包裹错误边界,提供统一的fallback和重试机制
|
||||
|
||||
- 📊 **统一状态组件**
|
||||
- ✅ `LoadingState.tsx` - 加载状态
|
||||
- ✅ `ErrorState.tsx` - 错误状态
|
||||
- ✅ `EmptyState.tsx` - 空状态
|
||||
- 统一错误提示样式,替换所有重复的加载/错误UI
|
||||
|
||||
- ♿ **可访问性增强**
|
||||
- 为Tab导航添加完整的ARIA标签
|
||||
- 添加键盘导航支持(Enter和Space键)
|
||||
- 添加焦点样式
|
||||
- 为所有交互元素添加 `aria-label`
|
||||
|
||||
---
|
||||
|
||||
### 📝 类型安全优化
|
||||
|
||||
- ✨ **类型定义统一化**
|
||||
- 创建 `app/admin/_components/types.ts` 统一管理所有类型定义
|
||||
- 移除了重复的类型定义
|
||||
- 添加 `AdminTab` 类型统一管理Tab类型
|
||||
- 添加 `DashboardData` 接口用于监控仪表板数据
|
||||
- 定义完整的Session类型(`SessionUser` 接口)
|
||||
- 定义API响应类型(`ApiErrorResponse`, `ApiSuccessResponse`)
|
||||
- 移除所有 `as any` 使用
|
||||
|
||||
---
|
||||
|
||||
### 📚 文档完善
|
||||
|
||||
- 📝 **优化文档**
|
||||
- 创建 `优化记录.md` - 记录所有优化和修复
|
||||
- 创建 `优化计划与优先级.md` - 详细规划后续优化任务
|
||||
- 更新 `后续修改.txt` - 记录需求进度
|
||||
- 创建 `更新日志.md`(原CHANGELOG.md) - 版本更新记录
|
||||
|
||||
---
|
||||
|
||||
### 🎨 UI样式统一
|
||||
|
||||
- ✨ **颜色系统统一**
|
||||
- Admin目录中的 `text-gray-* / bg-gray-* / border-gray-*` 类已全部替换为 `slate` 系列
|
||||
- 浅色与深色模式的视觉基调保持一致
|
||||
- 统一使用 `buttonStyles` 和 `inputStyles`
|
||||
- 新增 `buttonModifiers` 和 `inputModifiers` 辅助类
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-20 · 「拖拽体验优化」版本 🎨
|
||||
|
||||
本次更新主要优化了模板管理器的拖拽交互体验,让字段排序操作更加流畅和直观。
|
||||
|
||||
---
|
||||
|
||||
### 🎯 拖拽交互优化
|
||||
|
||||
- ✨ **移除高亮卡片效果**
|
||||
- 移除了拖拽悬停时的高亮卡片显示,避免与虚位卡片(placeholder)视觉重复。
|
||||
- 现在只保留虚位卡片来显示落点位置,界面更简洁清晰。
|
||||
|
||||
- ⚡ **立即更新布局**
|
||||
- 松开鼠标后布局立即更新,不再等待动画完成。
|
||||
- 直接根据拖拽后的顺序更新字段数据,响应更快,体验更流畅。
|
||||
|
||||
- 🎨 **动画效果优化**
|
||||
- 拖拽结束动画时长从 400ms 增加到 500ms,使用弹性缓动函数 `cubic-bezier(0.34, 1.56, 0.64, 1)`。
|
||||
- 拖拽覆盖层增加旋转和缩放效果,视觉反馈更明显。
|
||||
- 占位符卡片使用渐变背景和脉冲动画,更清晰地指示落点位置。
|
||||
|
||||
---
|
||||
|
||||
## 🧾 2025-11-18 · 「权限进化 & 电商加成」版本 (๑•̀ㅂ•́)و✧
|
||||
|
||||
这次更新主要围绕 **权限体验、电商字段扩展、模板/分类管理、部署体验** 做了一轮集中升级,让「由壹点 3D 素材平台」更像一款真正能落地到团队里的内部产品,而不是一个玩具 Demo。下面是本次改动的「人话版」说明👇
|
||||
|
||||
---
|
||||
|
||||
### 🛡 权限与用户管理
|
||||
|
||||
- ✨ **部门管理员权限升级**
|
||||
- 非最高权限账号,只要有「管理的部门」或「自己的部门」,就可以管理 **该部门 + 所有子部门** 的用户。
|
||||
- 没有部门范围时,就老老实实只能改自己,避免误操作全公司 ( ̄▽ ̄)/
|
||||
|
||||
- 🧍♀️ **按钮展示更聪明**
|
||||
- 对当前登录用户 **没有管理权限** 的成员:
|
||||
- 不再显示一整排「编辑 / 禁用 / 下载权限 / 管理员」按钮。
|
||||
- 只保留一行「无权限修改」的小提示,界面更干净,权限边界更清晰。
|
||||
|
||||
---
|
||||
|
||||
### 📦 模板与分类管理
|
||||
|
||||
- 🧩 **内置电商模板上线**
|
||||
- 在模板管理中新增几套常用电商模板(如「通用电商」「简易基础」「渲染维度」等)。
|
||||
- 字段命名与素材表最新标准对齐,开箱即用,不用每次从 0 造轮子。
|
||||
|
||||
- 📂 **分类下拉选项更好配**
|
||||
- 使用店铺 / 场景类别 / 平台分类,全部支持配置「下拉选项」。
|
||||
- 规则超简单:**一行一个选项**,不用再写 JSON 数组。
|
||||
- 管理端统一提供输入框 + 示例,方便以后快速按业务维护分类选项。
|
||||
|
||||
---
|
||||
|
||||
### 🛍 素材模型与电商字段
|
||||
|
||||
- 🏷 **商品 / 品牌维度补齐**
|
||||
- 为 `Material` 增加:`brandName`(品牌)、`seriesName`(系列)、`skuCode`(SKU 编码)、`materialTexture`(材质纹理)等字段。
|
||||
- 方便日后按品牌、系列、SKU 做统计和检索,更贴合电商团队日常用语。
|
||||
|
||||
- 🎨 **渲染 / 制作维度补充**
|
||||
- 新增:`renderTime`(渲染耗时)、`imageResolution`(输出分辨率)、`colorMode`(颜色模式)等字段。
|
||||
- 从上传 → 编辑 → 展示 → 搜索,全链路打通,真正做到「不只是传个压缩包」。
|
||||
|
||||
- 🔍 **前台页面同步升级**
|
||||
- 首页列表、分享页、素材编辑页均已接入以上新字段。
|
||||
- 搜索支持按这些维度模糊匹配,找素材更像「搜商品」而不是「翻文件夹」。
|
||||
|
||||
---
|
||||
|
||||
### 🚀 部署与运维体验
|
||||
|
||||
- 📜 **`deploy-new-fixed.ps1` 阶段化部署脚本**
|
||||
- 拆分为:检查阶段 → 构建阶段 → 镜像推送 → 远程部署,并在每一步给出清晰进度条和提示。
|
||||
- 附带一点小彩蛋文案,部署的时候不那么无聊 (´・ω・`)
|
||||
|
||||
- 🔐 **SSH & Registry 流程更稳**
|
||||
- 强制优先使用 OpenSSH,避免 plink 等工具弹交互对话框导致脚本卡死。
|
||||
- 自动打 Tag 并推送镜像到私有 Registry,然后在远程服务器上拉取并通过 Docker Compose 滚动更新。
|
||||
|
||||
- ❤️ **健康检查 `/api/health`**
|
||||
- 部署完成后,脚本会主动轮询 `/api/health`,确认服务真正「跑起来」再宣告成功。
|
||||
- 避免刚部署完就打开页面,结果服务还在启动中的尴尬。
|
||||
|
||||
---
|
||||
|
||||
### 🎛 其他细节优化
|
||||
|
||||
- 🧭 系统管理页默认打开 **「监控仪表板」** 标签,而不是素材管理,让管理者一眼看到系统状态。
|
||||
- 🎨 调整架构管理 & 用户管理的卡片、阴影、悬浮效果与按钮样式,使整个后台的观感更统一、更接近苹果风格。
|
||||
|
||||
> 小结:这一版可以理解为「**权限体验 + 电商字段 + 可迁移部署脚本**」的整合升级版,为后续更多客户侧部署和定制做好了「打基础」工作。✨
|
||||
@ -1,203 +0,0 @@
|
||||
# 服务器测试配置指南
|
||||
|
||||
## 📋 配置步骤
|
||||
|
||||
### 1. 配置 Docker 镜像加速器(必须)
|
||||
|
||||
#### 方式1:使用配置脚本(推荐)
|
||||
|
||||
**在服务器上执行**:
|
||||
```bash
|
||||
# 上传配置脚本到服务器
|
||||
scp scripts/server/setup-docker-mirror.sh root@45.249.208.118:/root/
|
||||
|
||||
# SSH 登录服务器
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 执行配置脚本
|
||||
chmod +x /root/setup-docker-mirror.sh
|
||||
sudo /root/setup-docker-mirror.sh
|
||||
```
|
||||
|
||||
#### 方式2:手动配置
|
||||
|
||||
**SSH 登录服务器后执行**:
|
||||
```bash
|
||||
# 1. 创建或编辑配置文件
|
||||
sudo nano /etc/docker/daemon.json
|
||||
|
||||
# 2. 添加以下内容
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.mirrors.ustc.edu.cn",
|
||||
"https://hub-mirror.c.163.com",
|
||||
"https://mirror.baidubce.com"
|
||||
]
|
||||
}
|
||||
|
||||
# 3. 保存并退出(Ctrl+X, Y, Enter)
|
||||
|
||||
# 4. 重启 Docker
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
|
||||
# 5. 验证配置
|
||||
docker info | grep -A 10 "Registry Mirrors"
|
||||
```
|
||||
|
||||
### 2. 更新 docker-compose.prod.yml
|
||||
|
||||
**已更新内容**:
|
||||
- ✅ MinIO 镜像版本:`minio/minio:RELEASE.2025-04-22T22-12-26Z`
|
||||
- ✅ MinIO MC 镜像版本:`minio/mc:RELEASE.2025-04-22T22-12-26Z`
|
||||
|
||||
**检查配置**:
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
grep "minio" docker-compose.prod.yml
|
||||
```
|
||||
|
||||
### 3. 拉取 MinIO 镜像
|
||||
|
||||
**在服务器上执行**:
|
||||
```bash
|
||||
# 拉取 MinIO 镜像(将自动使用国内镜像源)
|
||||
docker pull minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
docker pull minio/mc:RELEASE.2025-04-22T22-12-26Z
|
||||
|
||||
# 验证镜像
|
||||
docker images | grep minio
|
||||
```
|
||||
|
||||
### 4. 检查配置
|
||||
|
||||
**使用检查脚本**:
|
||||
```bash
|
||||
# 上传检查脚本
|
||||
scp scripts/server/check-docker-config.sh root@45.249.208.118:/root/
|
||||
|
||||
# 在服务器上执行
|
||||
chmod +x /root/check-docker-config.sh
|
||||
/root/check-docker-config.sh
|
||||
```
|
||||
|
||||
### 5. 部署测试
|
||||
|
||||
**使用部署脚本**:
|
||||
```powershell
|
||||
# 在本地执行(Windows PowerShell)
|
||||
cd D:\sucaiku\flnxi_sucai
|
||||
pwsh -ExecutionPolicy Bypass -File scripts/deploy/local-build-and-upload.ps1 `
|
||||
-Server 45.249.208.118 `
|
||||
-Username root `
|
||||
-Password lxvhNYFZ4160 `
|
||||
-CleanupAfterDeploy
|
||||
```
|
||||
|
||||
**或手动部署**:
|
||||
```bash
|
||||
# 在服务器上执行
|
||||
cd /root/deploy
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 配置检查清单
|
||||
|
||||
部署前确认:
|
||||
|
||||
- [ ] Docker 镜像加速器已配置
|
||||
- [ ] docker-compose.prod.yml 已更新(使用特定版本号)
|
||||
- [ ] MinIO 镜像已拉取
|
||||
- [ ] 服务器 IP 和端口配置正确
|
||||
- [ ] 环境变量已设置(如需要)
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 1. 检查服务状态
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
```
|
||||
|
||||
### 2. 检查服务日志
|
||||
```bash
|
||||
# 查看应用日志
|
||||
docker compose -f docker-compose.prod.yml logs -f app
|
||||
|
||||
# 查看 MinIO 日志
|
||||
docker compose -f docker-compose.prod.yml logs -f minio
|
||||
```
|
||||
|
||||
### 3. 访问应用
|
||||
- 应用地址:`http://45.249.208.118:4320`
|
||||
- MinIO 控制台:`http://45.249.208.118:9001`
|
||||
- 健康检查:`http://45.249.208.118:4320/api/health`
|
||||
|
||||
---
|
||||
|
||||
## 🔧 故障排查
|
||||
|
||||
### 问题1:镜像拉取失败
|
||||
**原因**:未配置镜像加速器或网络问题
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查镜像加速器配置
|
||||
docker info | grep -A 10 "Registry Mirrors"
|
||||
|
||||
# 如果未配置,运行配置脚本
|
||||
sudo bash scripts/server/setup-docker-mirror.sh
|
||||
```
|
||||
|
||||
### 问题2:MinIO 启动失败
|
||||
**原因**:镜像版本不存在或配置错误
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查镜像是否存在
|
||||
docker images | grep minio
|
||||
|
||||
# 重新拉取镜像
|
||||
docker pull minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
```
|
||||
|
||||
### 问题3:服务无法访问
|
||||
**原因**:端口未开放或防火墙配置
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查端口监听
|
||||
netstat -tlnp | grep -E "4320|9000|9001"
|
||||
|
||||
# 检查防火墙
|
||||
sudo ufw status
|
||||
# 如需开放端口
|
||||
sudo ufw allow 4320/tcp
|
||||
sudo ufw allow 9000/tcp
|
||||
sudo ufw allow 9001/tcp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **版本号管理**:
|
||||
- 使用特定版本号确保稳定性
|
||||
- 更新版本前必须在测试环境验证
|
||||
|
||||
2. **镜像源配置**:
|
||||
- 必须配置国内镜像源
|
||||
- 配置后需要重启 Docker 服务
|
||||
|
||||
3. **数据备份**:
|
||||
- 部署前建议备份数据库
|
||||
- MinIO 数据存储在 Docker volume 中
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
|
||||
@ -1,796 +0,0 @@
|
||||
# 架构设计与编码规范
|
||||
|
||||
## 📋 项目目标
|
||||
|
||||
**最终目的**:让其他公司能够**一键部署**或**一条命令**从Docker部署到自己的NAS或服务器上。
|
||||
|
||||
**核心原则**:
|
||||
1. **性能开销优先** - 最小化资源消耗,确保在低配置设备上也能稳定运行
|
||||
2. **稳定性优先** - 确保长期稳定运行,减少崩溃和错误
|
||||
3. **易于部署** - 零配置或最小配置即可运行
|
||||
4. **可维护性** - 代码清晰,易于理解和维护
|
||||
5. **国内镜像源优先** - 所有依赖、库等外部资源必须使用国内镜像源,确保国内用户快速部署和稳定运行
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 架构设计原则
|
||||
|
||||
### 1. 资源消耗最小化
|
||||
|
||||
#### 1.1 内存管理
|
||||
- ✅ **禁止内存泄漏**
|
||||
- 所有事件监听器必须正确清理
|
||||
- 定时器必须清理
|
||||
- 数据库连接池大小限制
|
||||
- 文件流必须关闭
|
||||
|
||||
- ✅ **避免大对象常驻内存**
|
||||
- 大文件使用流式处理,不全部加载到内存
|
||||
- 图片处理使用流式API
|
||||
- 数据库查询使用分页,避免一次性加载大量数据
|
||||
|
||||
- ✅ **缓存策略**
|
||||
- 使用内存缓存时设置大小限制和TTL
|
||||
- 优先使用Redis等外部缓存(如果可用)
|
||||
- 避免缓存大对象
|
||||
|
||||
#### 1.2 CPU优化
|
||||
- ✅ **避免阻塞主线程**
|
||||
- 所有I/O操作使用异步
|
||||
- 图片处理使用Worker线程或子进程
|
||||
- 数据库查询使用连接池
|
||||
|
||||
- ✅ **减少不必要的计算**
|
||||
- 禁用Next.js Image Optimizer(图片已在上传时优化)
|
||||
- 避免重复的图片处理
|
||||
- 使用数据库索引优化查询
|
||||
|
||||
#### 1.3 网络优化
|
||||
- ✅ **减少网络请求**
|
||||
- 合并API请求
|
||||
- 使用HTTP/2多路复用
|
||||
- 静态资源使用CDN或本地缓存
|
||||
|
||||
- ✅ **超时和重试机制**
|
||||
- 所有外部API调用设置合理的超时时间
|
||||
- 实现指数退避重试机制
|
||||
- 避免无限重试导致资源耗尽
|
||||
|
||||
### 2. 稳定性保障
|
||||
|
||||
#### 2.1 错误处理
|
||||
```typescript
|
||||
// ✅ 好的做法:完整的错误处理
|
||||
try {
|
||||
const result = await someOperation()
|
||||
return NextResponse.json({ success: true, data: result })
|
||||
} catch (error) {
|
||||
// 记录详细错误信息(用于诊断)
|
||||
console.error('[Operation] Error:', {
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
context: { /* 相关上下文信息 */ }
|
||||
})
|
||||
|
||||
// 返回用户友好的错误信息(不暴露内部细节)
|
||||
return NextResponse.json(
|
||||
{ error: '操作失败,请稍后重试' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
// ❌ 不好的做法:忽略错误或暴露内部细节
|
||||
try {
|
||||
await someOperation()
|
||||
} catch (error) {
|
||||
// 错误被忽略,无法诊断问题
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 资源清理
|
||||
```typescript
|
||||
// ✅ 好的做法:确保资源清理
|
||||
async function processFile(file: File) {
|
||||
let stream: ReadableStream | null = null
|
||||
try {
|
||||
stream = file.stream()
|
||||
// 处理文件
|
||||
} finally {
|
||||
// 确保流被关闭
|
||||
if (stream) {
|
||||
await stream.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 不好的做法:资源未清理
|
||||
async function processFile(file: File) {
|
||||
const stream = file.stream()
|
||||
// 处理文件,但流可能未关闭
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 数据库连接管理
|
||||
```typescript
|
||||
// ✅ 好的做法:使用连接池,设置合理的限制
|
||||
const prisma = new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
|
||||
datasources: {
|
||||
db: {
|
||||
url: process.env.DATABASE_URL,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// 设置连接池大小(根据服务器配置调整)
|
||||
// 在DATABASE_URL中设置:?connection_limit=10&pool_timeout=20
|
||||
|
||||
// ❌ 不好的做法:无限制的连接
|
||||
// 可能导致数据库连接耗尽
|
||||
```
|
||||
|
||||
#### 2.4 文件描述符管理
|
||||
```typescript
|
||||
// ✅ 好的做法:限制并发操作
|
||||
const MAX_CONCURRENT_UPLOADS = 5
|
||||
const uploadSemaphore = new Semaphore(MAX_CONCURRENT_UPLOADS)
|
||||
|
||||
async function uploadFile(file: File) {
|
||||
await uploadSemaphore.acquire()
|
||||
try {
|
||||
// 上传文件
|
||||
} finally {
|
||||
uploadSemaphore.release()
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 不好的做法:无限制并发
|
||||
// 可能导致文件描述符耗尽
|
||||
```
|
||||
|
||||
### 3. 部署友好性
|
||||
|
||||
#### 3.1 环境变量管理
|
||||
```typescript
|
||||
// ✅ 好的做法:提供合理的默认值
|
||||
const config = {
|
||||
port: parseInt(process.env.PORT || '3000', 10),
|
||||
databaseUrl: process.env.DATABASE_URL || 'postgresql://localhost:5432/app',
|
||||
// 所有配置都有默认值,可以零配置运行
|
||||
}
|
||||
|
||||
// ❌ 不好的做法:强制要求环境变量
|
||||
if (!process.env.DATABASE_URL) {
|
||||
throw new Error('DATABASE_URL is required') // 部署时容易出错
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 健康检查
|
||||
```typescript
|
||||
// ✅ 好的做法:提供健康检查端点
|
||||
export async function GET() {
|
||||
try {
|
||||
// 检查数据库连接
|
||||
await prisma.$queryRaw`SELECT 1`
|
||||
|
||||
// 检查S3连接(如果配置了)
|
||||
if (process.env.S3_ENDPOINT) {
|
||||
// 简单的连接测试
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
version: process.env.APP_VERSION || 'unknown'
|
||||
})
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ status: 'error', message: 'Health check failed' },
|
||||
{ status: 503 }
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3 优雅关闭
|
||||
```typescript
|
||||
// ✅ 好的做法:处理关闭信号
|
||||
process.on('SIGTERM', async () => {
|
||||
console.log('SIGTERM received, shutting down gracefully...')
|
||||
|
||||
// 停止接受新请求
|
||||
server.close(() => {
|
||||
console.log('HTTP server closed')
|
||||
})
|
||||
|
||||
// 关闭数据库连接
|
||||
await prisma.$disconnect()
|
||||
|
||||
// 清理其他资源
|
||||
process.exit(0)
|
||||
})
|
||||
```
|
||||
|
||||
### 4. 监控和诊断
|
||||
|
||||
#### 4.1 日志记录
|
||||
```typescript
|
||||
// ✅ 好的做法:结构化日志
|
||||
function logOperation(operation: string, data: any) {
|
||||
console.log(JSON.stringify({
|
||||
timestamp: new Date().toISOString(),
|
||||
level: 'info',
|
||||
operation,
|
||||
...data
|
||||
}))
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
logOperation('upload', {
|
||||
userId: user.id,
|
||||
fileSize: file.size,
|
||||
duration: Date.now() - startTime
|
||||
})
|
||||
```
|
||||
|
||||
#### 4.2 性能监控
|
||||
```typescript
|
||||
// ✅ 好的做法:记录操作耗时
|
||||
async function monitoredOperation() {
|
||||
const startTime = Date.now()
|
||||
try {
|
||||
const result = await operation()
|
||||
const duration = Date.now() - startTime
|
||||
|
||||
if (duration > 1000) {
|
||||
console.warn(`[Performance] Operation took ${duration}ms`)
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime
|
||||
console.error(`[Performance] Operation failed after ${duration}ms:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 编码规范
|
||||
|
||||
### 1. API路由设计
|
||||
|
||||
#### 1.1 统一响应格式
|
||||
```typescript
|
||||
// ✅ 好的做法:统一的响应格式
|
||||
export async function GET() {
|
||||
try {
|
||||
const data = await fetchData()
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data,
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
} catch (error) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
}, { status: 500 })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 请求验证
|
||||
```typescript
|
||||
// ✅ 好的做法:验证和清理输入
|
||||
import { z } from 'zod'
|
||||
|
||||
const uploadSchema = z.object({
|
||||
title: z.string().min(1).max(100),
|
||||
file: z.instanceof(File)
|
||||
})
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const formData = await request.formData()
|
||||
const data = uploadSchema.parse({
|
||||
title: formData.get('title'),
|
||||
file: formData.get('file')
|
||||
})
|
||||
|
||||
// 处理数据
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid input', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.3 权限检查
|
||||
```typescript
|
||||
// ✅ 好的做法:统一的权限检查
|
||||
async function requireAdmin(session: Session | null) {
|
||||
if (!session) {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
|
||||
const isAdmin = session.user.role === 'ADMIN' || (session.user as any).isSuperAdmin
|
||||
if (!isAdmin) {
|
||||
throw new Error('Forbidden')
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const session = await getServerSession(authOptions)
|
||||
await requireAdmin(session)
|
||||
|
||||
// 管理员操作
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 数据库操作
|
||||
|
||||
#### 2.1 使用事务
|
||||
```typescript
|
||||
// ✅ 好的做法:关键操作使用事务
|
||||
await prisma.$transaction(async (tx) => {
|
||||
// 检查条件
|
||||
const count = await tx.user.count()
|
||||
|
||||
// 执行操作
|
||||
const user = await tx.user.create({
|
||||
data: { /* ... */ }
|
||||
})
|
||||
|
||||
return user
|
||||
})
|
||||
```
|
||||
|
||||
#### 2.2 查询优化
|
||||
```typescript
|
||||
// ✅ 好的做法:只查询需要的字段
|
||||
const users = await prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
// 不查询password等大字段
|
||||
},
|
||||
take: 20, // 分页
|
||||
skip: 0
|
||||
})
|
||||
|
||||
// ❌ 不好的做法:查询所有字段
|
||||
const users = await prisma.user.findMany() // 可能包含大字段
|
||||
```
|
||||
|
||||
#### 2.3 错误处理
|
||||
```typescript
|
||||
// ✅ 好的做法:处理数据库错误
|
||||
try {
|
||||
await prisma.user.create({ data })
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (error.code === 'P2002') {
|
||||
// 唯一约束冲突
|
||||
return NextResponse.json(
|
||||
{ error: '用户已存在' },
|
||||
{ status: 409 }
|
||||
)
|
||||
}
|
||||
}
|
||||
throw error
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 文件处理
|
||||
|
||||
#### 3.1 流式处理
|
||||
```typescript
|
||||
// ✅ 好的做法:使用流处理大文件
|
||||
import { pipeline } from 'stream/promises'
|
||||
import { createReadStream } from 'fs'
|
||||
|
||||
async function uploadLargeFile(filePath: string) {
|
||||
const fileStream = createReadStream(filePath)
|
||||
const uploadStream = createUploadStream()
|
||||
|
||||
await pipeline(fileStream, uploadStream)
|
||||
// 文件不会全部加载到内存
|
||||
}
|
||||
|
||||
// ❌ 不好的做法:一次性读取整个文件
|
||||
const fileContent = await fs.readFile(filePath) // 大文件会占用大量内存
|
||||
```
|
||||
|
||||
#### 3.2 文件大小限制
|
||||
```typescript
|
||||
// ✅ 好的做法:检查文件大小
|
||||
const MAX_FILE_SIZE = 10 * 1024 * 1024 * 1024 // 10GB
|
||||
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
return NextResponse.json(
|
||||
{ error: '文件大小超过限制' },
|
||||
{ status: 413 }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 前端组件
|
||||
|
||||
#### 4.1 性能优化
|
||||
```typescript
|
||||
// ✅ 好的做法:使用React.memo和useMemo
|
||||
import { memo, useMemo } from 'react'
|
||||
|
||||
const MaterialCard = memo(({ material }: { material: Material }) => {
|
||||
const previewUrl = useMemo(() => {
|
||||
return material.previewUrl || '/placeholder.png'
|
||||
}, [material.previewUrl])
|
||||
|
||||
return (
|
||||
<Image
|
||||
src={previewUrl}
|
||||
alt={material.title}
|
||||
unoptimized // 图片已在上传时优化,不需要Next.js再次优化
|
||||
width={300}
|
||||
height={200}
|
||||
/>
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
#### 4.2 错误边界
|
||||
```typescript
|
||||
// ✅ 好的做法:错误边界处理
|
||||
'use client'
|
||||
|
||||
import { ErrorBoundary } from 'react-error-boundary'
|
||||
|
||||
function ErrorFallback({ error }: { error: Error }) {
|
||||
return (
|
||||
<div className="error-container">
|
||||
<h2>出现错误</h2>
|
||||
<p>{error.message}</p>
|
||||
<button onClick={() => window.location.reload()}>
|
||||
刷新页面
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||
{/* 页面内容 */}
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署优化
|
||||
|
||||
### 1. Docker优化
|
||||
|
||||
#### 1.1 多阶段构建
|
||||
```dockerfile
|
||||
# ✅ 好的做法:多阶段构建,最小化镜像大小
|
||||
FROM node:18-alpine AS builder
|
||||
# 构建阶段
|
||||
|
||||
FROM node:18-alpine AS runner
|
||||
# 运行阶段,只包含必要的文件
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
```
|
||||
|
||||
#### 1.2 层缓存优化
|
||||
```dockerfile
|
||||
# ✅ 好的做法:依赖文件放在前面,利用Docker缓存
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --production
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
```
|
||||
|
||||
### 2. 环境配置
|
||||
|
||||
#### 2.1 配置优先级
|
||||
```
|
||||
1. 系统环境变量(最高优先级)
|
||||
2. .env.local(本地开发)
|
||||
3. .env(项目默认)
|
||||
4. config-bianliang.env(可迁移配置,最低优先级)
|
||||
```
|
||||
|
||||
#### 2.2 默认值设计
|
||||
```typescript
|
||||
// ✅ 好的做法:所有配置都有合理的默认值
|
||||
const config = {
|
||||
// 数据库:默认使用本地PostgreSQL
|
||||
databaseUrl: process.env.DATABASE_URL || 'postgresql://postgres:postgres@postgres:5432/flnxi',
|
||||
|
||||
// S3:默认使用MinIO(Docker Compose中已配置)
|
||||
s3Endpoint: process.env.S3_ENDPOINT || 'http://minio:9000',
|
||||
s3Bucket: process.env.S3_BUCKET || 'youyidian3d',
|
||||
|
||||
// 端口:默认3000
|
||||
port: parseInt(process.env.PORT || '3000', 10),
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 国内镜像源配置(必须)
|
||||
|
||||
#### 3.1 核心要求
|
||||
**所有外部资源必须使用国内镜像源**,包括但不限于:
|
||||
- npm 包(Node.js 依赖)
|
||||
- Docker 基础镜像(Alpine、Node.js 等)
|
||||
- 系统包管理器(apk、apt、yum 等)
|
||||
- 其他第三方服务(如 CDN、API 等)
|
||||
|
||||
#### 3.2 Docker 构建镜像源配置
|
||||
|
||||
##### 3.2.1 Alpine Linux 镜像源
|
||||
```dockerfile
|
||||
# ✅ 好的做法:使用阿里云 Alpine 镜像源
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
|
||||
# 其他可选镜像源:
|
||||
# - 清华大学:mirrors.tuna.tsinghua.edu.cn
|
||||
# - 中科大:mirrors.ustc.edu.cn
|
||||
```
|
||||
|
||||
##### 3.2.2 npm 镜像源
|
||||
```dockerfile
|
||||
# ✅ 好的做法:构建时使用国内 npm 镜像源
|
||||
RUN npm config set registry https://registry.npmmirror.com/
|
||||
|
||||
# 安装依赖
|
||||
RUN npm ci --prefer-offline --no-audit
|
||||
|
||||
# 构建完成后清理配置(不在运行镜像中保留)
|
||||
RUN npm config delete registry
|
||||
```
|
||||
|
||||
**推荐的国内 npm 镜像源**:
|
||||
- **npmmirror.com**(原淘宝镜像,推荐)
|
||||
- **registry.npmmirror.com**(新域名)
|
||||
- **cnpmjs.org**(备选)
|
||||
|
||||
##### 3.2.3 Docker Hub 镜像源(必须配置)
|
||||
**重要**:所有Docker镜像(包括MinIO、PostgreSQL等)必须通过国内镜像加速器拉取。
|
||||
|
||||
**配置方法**(`/etc/docker/daemon.json`):
|
||||
```json
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.mirrors.ustc.edu.cn",
|
||||
"https://hub-mirror.c.163.com",
|
||||
"https://mirror.baidubce.com"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**配置后重启Docker**:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
**验证配置**:
|
||||
```bash
|
||||
docker info | grep -A 10 "Registry Mirrors"
|
||||
```
|
||||
|
||||
**版本管理要求**:
|
||||
- ✅ 使用特定版本号(如 `minio/minio:RELEASE.2025-04-22T22-12-26Z`)而不是 `latest`
|
||||
- ✅ 确保版本号在文档中明确记录
|
||||
- ✅ 版本更新前必须在测试环境验证
|
||||
|
||||
#### 3.3 本地开发环境配置
|
||||
|
||||
##### 3.3.1 npm 镜像源(本地开发)
|
||||
```bash
|
||||
# 永久配置(推荐)
|
||||
npm config set registry https://registry.npmmirror.com/
|
||||
|
||||
# 或使用 .npmrc 文件(项目级别)
|
||||
echo "registry=https://registry.npmmirror.com/" > .npmrc
|
||||
```
|
||||
|
||||
##### 3.3.2 验证镜像源配置
|
||||
```bash
|
||||
# 检查 npm 镜像源
|
||||
npm config get registry
|
||||
|
||||
# 检查 Docker 镜像源(如果配置了)
|
||||
docker info | grep -A 10 "Registry Mirrors"
|
||||
```
|
||||
|
||||
#### 3.4 代码中的外部资源引用
|
||||
|
||||
##### 3.4.1 CDN 资源
|
||||
```typescript
|
||||
// ✅ 好的做法:使用国内 CDN
|
||||
const CDN_BASE_URL = process.env.CDN_BASE_URL || 'https://cdn.jsdelivr.net'
|
||||
|
||||
// 推荐使用:
|
||||
// - jsDelivr(有国内节点)
|
||||
// - unpkg(有国内节点)
|
||||
// - 自建 CDN 或使用阿里云/腾讯云 CDN
|
||||
```
|
||||
|
||||
##### 3.4.2 外部 API
|
||||
```typescript
|
||||
// ✅ 好的做法:优先使用国内 API 服务
|
||||
// 如果必须使用国外服务,考虑:
|
||||
// 1. 使用代理
|
||||
// 2. 使用国内替代方案
|
||||
// 3. 实现缓存机制减少请求
|
||||
```
|
||||
|
||||
#### 3.5 部署脚本中的镜像源配置
|
||||
|
||||
##### 3.5.1 PowerShell 部署脚本
|
||||
```powershell
|
||||
# ✅ 好的做法:部署脚本中配置镜像源
|
||||
# 在 Dockerfile 中已配置,部署脚本无需额外配置
|
||||
# 但如果需要,可以在构建前设置环境变量
|
||||
$env:NPM_CONFIG_REGISTRY = "https://registry.npmmirror.com/"
|
||||
```
|
||||
|
||||
##### 3.5.2 服务器端配置
|
||||
```bash
|
||||
# 如果需要在服务器上直接安装依赖,配置 npm 镜像源
|
||||
ssh user@server "npm config set registry https://registry.npmmirror.com/"
|
||||
```
|
||||
|
||||
#### 3.6 镜像源配置检查清单
|
||||
|
||||
在提交代码前,确保:
|
||||
- [ ] Dockerfile 中配置了 Alpine 镜像源
|
||||
- [ ] Dockerfile 中配置了 npm 镜像源(构建阶段)
|
||||
- [ ] 本地开发环境配置了 npm 镜像源
|
||||
- [ ] 服务器上配置了 Docker 镜像加速器
|
||||
- [ ] docker-compose.yml 中使用特定版本号(不使用 `latest`)
|
||||
- [ ] 所有外部 CDN 资源优先使用国内服务
|
||||
- [ ] 部署文档中说明了镜像源配置要求
|
||||
|
||||
#### 3.7 Docker镜像版本管理
|
||||
|
||||
##### 3.7.1 使用特定版本号(必须)
|
||||
```yaml
|
||||
# ✅ 好的做法:使用特定版本号
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
|
||||
# ❌ 不好的做法:使用latest(可能导致不稳定)
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
```
|
||||
|
||||
##### 3.7.2 版本更新流程
|
||||
1. **测试环境验证**:在测试环境部署新版本,验证兼容性
|
||||
2. **文档记录**:在CHANGELOG中记录版本更新和变更
|
||||
3. **逐步部署**:先在部分环境部署,确认稳定后再全面更新
|
||||
4. **回滚准备**:保留旧版本配置,确保可以快速回滚
|
||||
|
||||
##### 3.7.3 版本号管理建议
|
||||
```yaml
|
||||
# 使用环境变量管理版本(推荐)
|
||||
MINIO_VERSION=${MINIO_VERSION:-RELEASE.2025-04-22T22-12-26Z}
|
||||
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio:${MINIO_VERSION}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能指标
|
||||
|
||||
### 目标指标
|
||||
|
||||
1. **内存使用**
|
||||
- 空闲时:< 200MB
|
||||
- 正常运行时:< 500MB
|
||||
- 峰值:< 1GB
|
||||
|
||||
2. **CPU使用**
|
||||
- 空闲时:< 5%
|
||||
- 正常运行时:< 30%
|
||||
- 峰值:< 80%
|
||||
|
||||
3. **响应时间**
|
||||
- API响应:< 200ms(95分位)
|
||||
- 页面加载:< 2s(首次加载)
|
||||
- 数据库查询:< 100ms(95分位)
|
||||
|
||||
4. **稳定性**
|
||||
- 正常运行时间:> 99.9%
|
||||
- 错误率:< 0.1%
|
||||
- 崩溃恢复时间:< 30s
|
||||
|
||||
---
|
||||
|
||||
## 🔍 代码审查清单
|
||||
|
||||
在提交代码前,检查以下项目:
|
||||
|
||||
### 性能
|
||||
- [ ] 是否避免了内存泄漏?
|
||||
- [ ] 是否使用了流式处理大文件?
|
||||
- [ ] 是否限制了并发操作?
|
||||
- [ ] 是否优化了数据库查询?
|
||||
- [ ] 是否禁用了不必要的优化(如Next.js Image Optimizer)?
|
||||
|
||||
### 稳定性
|
||||
- [ ] 是否处理了所有可能的错误?
|
||||
- [ ] 是否清理了所有资源(文件流、数据库连接等)?
|
||||
- [ ] 是否设置了合理的超时时间?
|
||||
- [ ] 是否实现了重试机制?
|
||||
- [ ] 是否添加了健康检查?
|
||||
|
||||
### 部署友好性
|
||||
- [ ] 是否提供了合理的默认值?
|
||||
- [ ] 是否支持零配置运行?
|
||||
- [ ] 是否添加了必要的日志?
|
||||
- [ ] 是否处理了优雅关闭?
|
||||
- [ ] 是否配置了国内镜像源(npm、Docker、系统包管理器)?
|
||||
- [ ] 是否避免了直接引用国外 CDN 或 API?
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [Next.js性能优化最佳实践](https://nextjs.org/docs/app/building-your-application/optimizing)
|
||||
- [Prisma性能优化指南](https://www.prisma.io/docs/guides/performance-and-optimization)
|
||||
- [Node.js最佳实践](https://github.com/goldbergyoni/nodebestpractices)
|
||||
- [Docker最佳实践](https://docs.docker.com/develop/dev-best-practices/)
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
**维护者**:开发团队
|
||||
|
||||
---
|
||||
|
||||
## 📌 重要提醒
|
||||
|
||||
### 国内镜像源配置(必须遵守)
|
||||
|
||||
本项目主要面向国内用户,**所有外部资源必须使用国内镜像源**:
|
||||
|
||||
1. ✅ **Dockerfile 中已配置**:
|
||||
- Alpine Linux:使用阿里云镜像源
|
||||
- npm:使用 npmmirror.com 镜像源
|
||||
|
||||
2. ✅ **本地开发环境**:
|
||||
- 建议配置 npm 镜像源:`npm config set registry https://registry.npmmirror.com/`
|
||||
|
||||
3. ⚠️ **新增依赖或修改配置时**:
|
||||
- 必须确保使用国内镜像源
|
||||
- 禁止直接使用国外源(npmjs.org、docker.io 等)
|
||||
- 代码审查时必须检查镜像源配置
|
||||
|
||||
4. 📝 **文档要求**:
|
||||
- 所有部署文档必须说明镜像源配置
|
||||
- 新加入的开发者必须了解镜像源要求
|
||||
|
||||
@ -1,111 +0,0 @@
|
||||
# 新功能测试指南
|
||||
|
||||
## 已实现的功能
|
||||
|
||||
### 1. ✅ 断点续传功能
|
||||
- **功能说明**:上传过程中意外关闭网页后,重新打开会提示恢复上传
|
||||
- **测试步骤**:
|
||||
1. 打开上传素材对话框
|
||||
2. 填写表单信息(SPU、分类、场景等)
|
||||
3. 选择文件并开始上传
|
||||
4. 在上传过程中(进度<100%时)关闭浏览器标签页
|
||||
5. 重新打开网站,上传对话框会自动弹出恢复提示
|
||||
6. 点击"恢复上传",表单数据会自动恢复(文件需要重新选择)
|
||||
|
||||
### 2. ✅ 用户激活功能开关
|
||||
- **功能说明**:管理员可在后台控制是否启用用户激活功能
|
||||
- **测试步骤**:
|
||||
1. 使用管理员账号登录
|
||||
2. 进入"系统设置"页面
|
||||
3. 找到"用户激活功能"开关
|
||||
4. 开启/关闭开关并保存
|
||||
5. 测试新用户注册后的激活状态
|
||||
|
||||
### 3. ✅ 邀请功能
|
||||
- **功能说明**:管理员可创建邀请链接,用户可通过邀请码注册
|
||||
- **测试步骤**:
|
||||
1. 使用管理员账号登录
|
||||
2. 进入"系统管理"页面
|
||||
3. 找到"邀请管理"功能
|
||||
4. 创建邀请链接(可设置使用次数和过期时间)
|
||||
5. 复制邀请链接,在新浏览器中打开
|
||||
6. 使用邀请码注册新账号
|
||||
|
||||
### 4. ✅ 视频类素材上传功能
|
||||
- **功能说明**:支持上传视频文件作为素材
|
||||
- **支持格式**:.mp4, .avi, .mov, .wmv, .flv, .mkv, .webm
|
||||
- **测试步骤**:
|
||||
1. 打开上传素材对话框
|
||||
2. 选择素材类型为"视频"
|
||||
3. 上传视频文件(必填)
|
||||
4. 上传视频封面图(必填,作为预览图)
|
||||
5. 填写其他必填信息(SPU、分类、场景等)
|
||||
6. 提交上传
|
||||
7. 上传成功后,在主页点击视频素材
|
||||
8. 应该跳转到详情页,可以看到视频播放器
|
||||
9. 视频可以正常播放(支持流式传输)
|
||||
|
||||
### 5. ✅ 排行榜滚动间隔调整
|
||||
- **功能说明**:排行榜悬浮窗的自动切换间隔从5秒调整为10秒
|
||||
- **测试步骤**:
|
||||
1. 打开网站首页
|
||||
2. 查看右侧的排行榜悬浮窗
|
||||
3. 观察三个标签(贡献、下载、点赞)的自动切换
|
||||
4. 使用秒表计时,确认切换间隔为10秒
|
||||
|
||||
## 测试检查清单
|
||||
|
||||
### 断点续传功能
|
||||
- [ ] 上传过程中关闭页面,重新打开时提示恢复
|
||||
- [ ] 恢复后表单数据正确
|
||||
- [ ] 上传完成后状态被清除
|
||||
- [ ] localStorage中保存的状态正确
|
||||
|
||||
### 视频上传功能
|
||||
- [ ] 可以选择视频类型
|
||||
- [ ] 视频文件上传成功
|
||||
- [ ] 视频封面图上传成功
|
||||
- [ ] 视频在主页正确显示(显示封面图)
|
||||
- [ ] 点击视频跳转到详情页
|
||||
- [ ] 视频播放器正常工作
|
||||
- [ ] 视频可以正常播放
|
||||
- [ ] 视频下载功能正常
|
||||
|
||||
### 用户激活和邀请功能
|
||||
- [ ] 管理员可以开启/关闭用户激活功能
|
||||
- [ ] 管理员可以创建邀请链接
|
||||
- [ ] 邀请链接可以正常使用
|
||||
- [ ] 邀请码验证正确
|
||||
|
||||
### 排行榜功能
|
||||
- [ ] 排行榜正常显示
|
||||
- [ ] 自动切换间隔为10秒
|
||||
- [ ] 三个标签正常切换
|
||||
|
||||
## 已知问题
|
||||
|
||||
1. **视频播放**:视频播放需要登录才能访问下载API,分享页面应该允许未登录用户观看视频
|
||||
- 解决方案:需要修改下载API,对于视频类型且从分享页面访问时,允许未登录用户观看
|
||||
|
||||
2. **视频类型筛选**:主页已添加视频类型筛选选项
|
||||
|
||||
## 测试环境要求
|
||||
|
||||
- Node.js 18+
|
||||
- 数据库已连接
|
||||
- 开发服务器运行中(`npm run dev`)
|
||||
|
||||
## 测试数据准备
|
||||
|
||||
1. 准备一个测试视频文件(.mp4格式,建议小于100MB)
|
||||
2. 准备一张视频封面图(JPG/PNG格式)
|
||||
3. 准备管理员账号用于测试用户激活和邀请功能
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 视频文件较大,上传可能需要较长时间
|
||||
2. 断点续传功能依赖localStorage,清除浏览器数据会影响恢复
|
||||
3. 视频播放需要浏览器支持HTML5 video标签
|
||||
4. 视频流式传输需要服务器支持Range请求
|
||||
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
# 项目根目录清理完成
|
||||
|
||||
## ✅ 清理完成
|
||||
|
||||
项目根目录已成功清理并整理,所有文件已按功能分类存放。
|
||||
|
||||
## 📊 清理结果
|
||||
|
||||
### 清理前
|
||||
- 根目录文件数量:~150+ 个文件
|
||||
- 文件组织:混乱,各种文档、脚本、备份文件混杂
|
||||
|
||||
### 清理后
|
||||
- 根目录文件数量:~20 个核心文件
|
||||
- 文件组织:清晰,按功能分类存放
|
||||
|
||||
## 📁 新的目录结构
|
||||
|
||||
```
|
||||
项目根目录/
|
||||
├── docs/ # 文档目录
|
||||
│ ├── reports/ # 报告文件(30+ 个)
|
||||
│ ├── guides/ # 指南文件(50+ 个)
|
||||
│ └── chats/ # 聊天记录(7 个)
|
||||
├── scripts/ # 脚本目录
|
||||
│ ├── deploy/ # 部署脚本(10 个)
|
||||
│ ├── tools/ # 工具脚本(50+ 个)
|
||||
│ └── backup/ # 备份脚本
|
||||
├── app/ # Next.js 应用代码
|
||||
├── components/ # React 组件
|
||||
├── lib/ # 工具库
|
||||
├── public/ # 静态资源
|
||||
├── prisma/ # 数据库 Schema
|
||||
└── types/ # TypeScript 类型定义
|
||||
```
|
||||
|
||||
## 📋 根目录保留的文件
|
||||
|
||||
### 核心配置文件
|
||||
- `package.json` - 项目依赖配置
|
||||
- `package-lock.json` - 依赖锁定文件
|
||||
- `tsconfig.json` - TypeScript 配置
|
||||
- `next.config.js` - Next.js 配置
|
||||
- `postcss.config.js` - PostCSS 配置
|
||||
- `tailwind.config.ts` - Tailwind CSS 配置
|
||||
- `next-env.d.ts` - Next.js 类型定义
|
||||
- `middleware.ts` - Next.js 中间件
|
||||
- `Dockerfile` - Docker 镜像配置
|
||||
- `docker-compose.yml` - Docker Compose 配置(生产环境)
|
||||
- `.gitignore` - Git 忽略配置
|
||||
|
||||
### 重要文档
|
||||
- `README.md` - 项目主文档
|
||||
|
||||
## 🗑️ 已删除的文件(示例)
|
||||
|
||||
### 备份文件
|
||||
- `*.sql`、`*.zip`、`*.tar.gz` 等
|
||||
|
||||
### 临时文件
|
||||
- 临时/一次性脚本、重复说明文档
|
||||
|
||||
## ✅ 清理效果
|
||||
|
||||
1. 根目录整洁,只保留核心配置文件和重要文档
|
||||
2. 文件组织清晰,文档与脚本分类集中,易于维护
|
||||
|
||||
## 📝 后续建议
|
||||
|
||||
1. 新文档放入 `docs/` 子目录
|
||||
2. 新脚本放入 `scripts/` 子目录
|
||||
3. 备份/临时文件不提交到 Git,使用 `.gitignore` 排除
|
||||
4. 定期清理旧文档和不再使用的脚本
|
||||
|
||||
## 📖 相关文档
|
||||
|
||||
- `docs/清理完成报告.md`
|
||||
- `docs/PORTABLE_DEPLOYMENT_GUIDE.md`
|
||||
|
||||
|
||||
@ -1,205 +0,0 @@
|
||||
# 版本架构设计方案
|
||||
|
||||
## 📋 版本规划
|
||||
|
||||
### 标准版(Standard Edition)
|
||||
- **定位**:内网使用,所有资源存储在服务器本地
|
||||
- **特点**:
|
||||
- 无需外网访问
|
||||
- 资源完全本地化
|
||||
- 部署简单,零配置
|
||||
- 适合企业内部使用
|
||||
|
||||
### Pro版(Professional Edition)
|
||||
- **定位**:支持内网穿透,资源存储在MinIO存储桶
|
||||
- **特点**:
|
||||
- 支持内网穿透功能
|
||||
- 资源存储在MinIO(运行在Docker内)
|
||||
- 存储桶也在服务器上
|
||||
- 适合需要远程访问的场景
|
||||
|
||||
---
|
||||
|
||||
## ✅ 方案符合项目宗旨分析
|
||||
|
||||
### 1. 使用特定版本号 ✅ **完全符合**
|
||||
|
||||
使用 `minio/minio:RELEASE.2025-04-22T22-12-26Z` 而不是 `latest`:
|
||||
|
||||
**优点**:
|
||||
- ✅ **稳定性优先**:避免因MinIO更新导致的不兼容问题
|
||||
- ✅ **可重现性**:确保所有部署环境使用相同版本
|
||||
- ✅ **易于回滚**:出现问题时可以快速定位版本
|
||||
- ✅ **符合项目宗旨**:稳定性优先原则
|
||||
|
||||
**建议**:
|
||||
```yaml
|
||||
# ✅ 好的做法:使用特定版本号
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
|
||||
# ❌ 不好的做法:使用latest(可能因更新导致问题)
|
||||
image: minio/minio:latest
|
||||
```
|
||||
|
||||
### 2. Docker收费问题澄清
|
||||
|
||||
**重要说明**:
|
||||
- ✅ **Docker Engine(服务器版)**:**完全免费**,无任何限制
|
||||
- ⚠️ **Docker Desktop(桌面版)**:个人/小团队免费,企业版收费
|
||||
- ✅ **本项目使用场景**:服务器部署,使用Docker Engine,**无需担心收费问题**
|
||||
|
||||
**结论**:使用Docker部署完全符合项目宗旨,无需担心收费。
|
||||
|
||||
### 3. 国内镜像源配置 ✅ **必须配置**
|
||||
|
||||
虽然使用特定版本号,但仍需配置国内镜像源加速下载:
|
||||
|
||||
#### 3.1 配置Docker镜像加速器
|
||||
|
||||
**在服务器上配置**(`/etc/docker/daemon.json`):
|
||||
```json
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.mirrors.ustc.edu.cn",
|
||||
"https://hub-mirror.c.163.com",
|
||||
"https://mirror.baidubce.com"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**配置后重启Docker**:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
#### 3.2 验证镜像源配置
|
||||
```bash
|
||||
docker info | grep -A 10 "Registry Mirrors"
|
||||
```
|
||||
|
||||
#### 3.3 拉取MinIO镜像(使用国内镜像源)
|
||||
```bash
|
||||
# 配置镜像加速器后,直接拉取即可使用国内源
|
||||
docker pull minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 架构设计建议
|
||||
|
||||
### 标准版架构
|
||||
|
||||
```yaml
|
||||
# docker-compose.standard.yml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
# 数据库存储本地
|
||||
|
||||
app:
|
||||
image: flnxi-material:standard
|
||||
volumes:
|
||||
- ./uploads:/app/uploads # 所有资源存储在本地
|
||||
# 不包含MinIO服务
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- 所有文件存储在服务器本地 `./uploads` 目录
|
||||
- 无需MinIO服务
|
||||
- 资源完全内网访问
|
||||
|
||||
### Pro版架构
|
||||
|
||||
```yaml
|
||||
# docker-compose.prod.yml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z # 使用特定版本
|
||||
volumes:
|
||||
- minio_data:/data # 存储桶数据存储在服务器上
|
||||
# 支持内网穿透配置
|
||||
|
||||
app:
|
||||
image: flnxi-material:pro
|
||||
depends_on:
|
||||
- minio
|
||||
# 资源存储在MinIO存储桶
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- 资源存储在MinIO存储桶(运行在Docker内)
|
||||
- 存储桶数据存储在服务器本地(`minio_data` volume)
|
||||
- 支持内网穿透功能
|
||||
|
||||
---
|
||||
|
||||
## 📝 实施建议
|
||||
|
||||
### 1. 创建两个版本的docker-compose文件
|
||||
|
||||
```
|
||||
项目根目录/
|
||||
├── docker-compose.standard.yml # 标准版配置
|
||||
├── docker-compose.prod.yml # Pro版配置(已存在)
|
||||
└── docker-compose.yml # 默认配置(可链接到标准版或Pro版)
|
||||
```
|
||||
|
||||
### 2. 版本管理策略
|
||||
|
||||
#### 2.1 MinIO版本管理
|
||||
```yaml
|
||||
# 使用环境变量管理版本
|
||||
MINIO_VERSION=${MINIO_VERSION:-RELEASE.2025-04-22T22-12-26Z}
|
||||
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio:${MINIO_VERSION}
|
||||
```
|
||||
|
||||
#### 2.2 版本更新流程
|
||||
1. **测试新版本**:在测试环境验证新版本兼容性
|
||||
2. **更新版本号**:确认稳定后更新版本号
|
||||
3. **文档记录**:在CHANGELOG中记录版本更新
|
||||
4. **部署验证**:部署后验证功能正常
|
||||
|
||||
### 3. 部署文档更新
|
||||
|
||||
在部署文档中明确说明:
|
||||
- ✅ 标准版和Pro版的区别
|
||||
- ✅ MinIO版本号的使用原因
|
||||
- ✅ Docker镜像加速器配置方法
|
||||
- ✅ 版本更新流程
|
||||
|
||||
---
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
### 方案评估
|
||||
|
||||
| 评估项 | 符合度 | 说明 |
|
||||
|--------|--------|------|
|
||||
| **稳定性优先** | ✅ 完全符合 | 使用特定版本号确保稳定性 |
|
||||
| **易于部署** | ✅ 完全符合 | 两个版本都支持一键部署 |
|
||||
| **国内镜像源** | ✅ 需配置 | 配置Docker镜像加速器即可 |
|
||||
| **性能开销** | ✅ 完全符合 | 资源都在服务器上,无额外开销 |
|
||||
| **Docker收费** | ✅ 无影响 | 服务器版Docker完全免费 |
|
||||
|
||||
### 最终结论
|
||||
|
||||
**✅ 该方案完全符合项目宗旨,无需修改**
|
||||
|
||||
**建议补充**:
|
||||
1. ✅ 配置Docker镜像加速器(国内镜像源)
|
||||
2. ✅ 使用特定版本号(已符合要求)
|
||||
3. ✅ 创建两个版本的docker-compose文件
|
||||
4. ✅ 在文档中说明版本管理策略
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
|
||||
|
||||
@ -1,128 +0,0 @@
|
||||
# 用户偏好设置
|
||||
|
||||
## 📋 部署偏好
|
||||
|
||||
### 部署内容原则
|
||||
- ✅ **只上传功能性代码**:应用程序代码、配置文件、依赖文件
|
||||
- ✅ **只上传必要文件**:运行应用所必需的文件
|
||||
- ❌ **不上传文档**:所有 `.md` 文档文件
|
||||
- ❌ **不上传脚本**:所有部署、工具、临时脚本
|
||||
- ❌ **不上传临时文件**:日志、缓存、临时文件
|
||||
- ❌ **不上传开发文件**:IDE 配置、测试文件、开发工具
|
||||
|
||||
### 排除规则
|
||||
以下类型的文件**不会**上传到服务器:
|
||||
|
||||
1. **文档文件**
|
||||
- `docs/` - 所有文档目录
|
||||
- `*.md` - 所有 Markdown 文档
|
||||
- `README.md` - 项目说明文档
|
||||
- `LICENSE` - 许可证文件
|
||||
|
||||
2. **脚本文件**
|
||||
- `scripts/` - 所有脚本目录(部署、工具、临时脚本)
|
||||
- `*.sh` - Shell 脚本
|
||||
- `*.ps1` - PowerShell 脚本
|
||||
- `*.bat` - 批处理脚本
|
||||
- `*.py` - Python 脚本
|
||||
|
||||
3. **开发文件**
|
||||
- `.vscode/` - VS Code 配置
|
||||
- `.idea/` - IntelliJ IDEA 配置
|
||||
- `.cursor/` - Cursor 配置
|
||||
- `.git/` - Git 版本控制
|
||||
- `.gitignore` - Git 忽略文件
|
||||
- `*.swp`, `*.swo` - 编辑器临时文件
|
||||
|
||||
4. **临时文件**
|
||||
- `uploads/` - 上传文件(服务器上已有)
|
||||
- `backups/` - 备份文件
|
||||
- `*.log` - 日志文件
|
||||
- `*.tmp` - 临时文件
|
||||
- `.cache/` - 缓存目录
|
||||
- `coverage/` - 测试覆盖率报告
|
||||
|
||||
5. **构建产物**
|
||||
- `.next/` - Next.js 构建产物(在 Docker 中重新构建)
|
||||
- `node_modules/` - 依赖包(在 Docker 中重新安装)
|
||||
- `out/` - 输出目录
|
||||
- `build/` - 构建目录
|
||||
- `dist/` - 分发目录
|
||||
|
||||
6. **环境配置**
|
||||
- `.env.local` - 本地环境变量
|
||||
- `.env.development.local` - 开发环境变量
|
||||
- `.env.test.local` - 测试环境变量
|
||||
- `.env.example` - 环境变量示例
|
||||
|
||||
7. **其他文件**
|
||||
- `*.tar.gz` - 压缩包
|
||||
- `*.zip` - ZIP 压缩包
|
||||
- `*.sql` - SQL 文件
|
||||
- `*.rpm` - RPM 包
|
||||
- `frp_*` - FRP 相关文件
|
||||
- `.DS_Store` - macOS 系统文件
|
||||
|
||||
### 包含内容
|
||||
以下文件**会**上传到服务器(通过 Docker 镜像):
|
||||
|
||||
1. **应用代码**
|
||||
- `app/` - Next.js 应用代码
|
||||
- `components/` - React 组件
|
||||
- `lib/` - 工具库
|
||||
- `types/` - TypeScript 类型定义
|
||||
- `middleware.ts` - 中间件
|
||||
- `next.config.js` - Next.js 配置
|
||||
- `tailwind.config.ts` - Tailwind CSS 配置
|
||||
- `tsconfig.json` - TypeScript 配置
|
||||
- `postcss.config.js` - PostCSS 配置
|
||||
|
||||
2. **配置文件**
|
||||
- `package.json` - 项目依赖配置
|
||||
- `package-lock.json` - 依赖锁定文件
|
||||
- `Dockerfile` - Docker 镜像构建文件
|
||||
- `.dockerignore` - Docker 忽略文件
|
||||
- `docker-compose.yml` - Docker Compose 配置
|
||||
- `docker-compose.prod.yml` - 生产环境 Docker Compose 配置
|
||||
|
||||
3. **数据库配置**
|
||||
- `prisma/schema.prisma` - Prisma 数据库架构
|
||||
- `prisma/migrations/` - 数据库迁移文件
|
||||
|
||||
4. **公共资源**
|
||||
- `public/` - 公共静态资源
|
||||
|
||||
## 🚀 部署方式
|
||||
|
||||
### 当前部署方式
|
||||
使用 **直接上传源代码**方式部署:
|
||||
1. 本地打包源代码(只包含功能性代码,排除非功能性文件)
|
||||
2. 上传源代码压缩包到服务器
|
||||
3. 在服务器上解压并**由服务器 Docker 重新构建**
|
||||
4. **不需要在本地测试和构建**
|
||||
|
||||
### 部署脚本
|
||||
- `scripts/deploy/安全部署.ps1` - Windows PowerShell 部署脚本
|
||||
- `scripts/deploy/安全部署.sh` - Linux/Mac 部署脚本
|
||||
|
||||
### 部署流程
|
||||
1. **部署前检查**:运行 `部署前检查清单.bat`(可选)
|
||||
2. **执行部署**:运行 `scripts/deploy/安全部署.ps1`
|
||||
- 自动打包源代码(排除文档、脚本、临时文件等)
|
||||
- 上传压缩包到服务器
|
||||
- 服务器自动解压、备份、构建 Docker 镜像、启动容器
|
||||
3. **验证部署**:检查服务器上的 Docker 容器状态
|
||||
|
||||
### 部署特点
|
||||
- ✅ **不需要本地 Docker**:不在本地构建 Docker 镜像
|
||||
- ✅ **不需要本地测试**:直接上传源代码到服务器
|
||||
- ✅ **服务器构建**:由服务器上的 Docker 重新构建镜像
|
||||
- ✅ **自动备份**:自动备份服务器上的上传文件
|
||||
- ✅ **自动迁移**:自动执行数据库迁移
|
||||
|
||||
## 📝 注意事项
|
||||
- 源代码打包排除规则与 `.dockerignore` 保持一致
|
||||
- 服务器构建时会使用 `.dockerignore`
|
||||
- 服务器 `.env` 文件独立维护,本地 `.env.local` 不上传
|
||||
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
# 用户偏好设置总结
|
||||
|
||||
## 📋 部署偏好
|
||||
|
||||
### 核心原则
|
||||
**只上传功能性代码和必要文件,排除所有非功能性文件**
|
||||
|
||||
### ✅ 会包含的文件
|
||||
1. **应用代码**
|
||||
- `app/`、`components/`、`lib/`、`types/`、`middleware.ts`
|
||||
2. **配置文件**
|
||||
- `package.json`、`Dockerfile`、`.dockerignore`、`docker-compose.yml`、`next.config.js`、`tailwind.config.ts`、`tsconfig.json`、`postcss.config.js`
|
||||
3. **数据库配置**
|
||||
- `prisma/schema.prisma`、`prisma/migrations/`
|
||||
4. **公共资源**
|
||||
- `public/`
|
||||
|
||||
### ❌ 会排除的文件
|
||||
- 文档(docs/、*.md)、脚本(scripts/、*.sh、*.ps1、*.bat)、开发文件(.vscode/、.idea/、.cursor/)、临时文件(uploads/、*.log)、构建产物(.next/、node_modules/)、环境变量(.env.local 等)
|
||||
|
||||
## 🚀 部署方式
|
||||
|
||||
### 当前部署方式
|
||||
使用 **直接上传源代码**方式部署:
|
||||
1. 本地打包源代码(排除非功能性文件)
|
||||
2. 上传至服务器
|
||||
3. 服务器 Docker 重新构建镜像并自动部署
|
||||
|
||||
### 脚本与流程
|
||||
- `scripts/deploy/安全部署.ps1` / `scripts/deploy/安全部署.sh`
|
||||
- 流程:检查 → 打包 → 上传 → 服务器备份/解压/构建/启动/迁移 → 验证
|
||||
|
||||
## 📝 配置文件
|
||||
- `.dockerignore` 控制 Docker 构建排除项(已与偏好一致)
|
||||
|
||||
## ✅ 偏好设置确认
|
||||
- 只上传功能性代码与必要文件
|
||||
- 排除文档/脚本/临时与构建产物
|
||||
- 服务器侧构建镜像并自动执行迁移
|
||||
|
||||
|
||||
@ -1,395 +0,0 @@
|
||||
# 私有镜像仓库部署指南
|
||||
|
||||
## 📋 概述
|
||||
|
||||
本系统支持**私有镜像仓库部署**,确保:
|
||||
- ✅ 其他公司使用系统时**看不到源码**
|
||||
- ✅ 直接从构建好的镜像服务器下载镜像
|
||||
- ✅ 所有依赖资源从国内镜像源下载
|
||||
- ✅ 支持一键从标准版升级到Pro版
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 架构设计
|
||||
|
||||
### 镜像仓库结构
|
||||
|
||||
```
|
||||
your-registry.com/
|
||||
├── flnxi-material:standard-latest # 标准版镜像
|
||||
└── flnxi-material:pro-latest # Pro版镜像
|
||||
```
|
||||
|
||||
### 部署流程
|
||||
|
||||
```
|
||||
1. 开发环境构建镜像
|
||||
↓
|
||||
2. 推送到私有镜像仓库
|
||||
↓
|
||||
3. 客户服务器配置镜像仓库地址
|
||||
↓
|
||||
4. 客户直接拉取镜像部署(无需源码)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 配置步骤
|
||||
|
||||
### 0. 快速部署(推荐)
|
||||
|
||||
**在 git.1080p.hk 服务器上执行**:
|
||||
```bash
|
||||
# 上传部署脚本
|
||||
scp scripts/server/setup-registry.sh root@git.1080p.hk:/root/
|
||||
|
||||
# SSH登录服务器
|
||||
ssh root@git.1080p.hk
|
||||
|
||||
# 执行部署(带认证)
|
||||
chmod +x /root/setup-registry.sh
|
||||
REGISTRY_USER=registry-user REGISTRY_PASSWORD=your-secure-password \
|
||||
/root/setup-registry.sh
|
||||
|
||||
# 或执行部署(无认证,仅内网使用)
|
||||
/root/setup-registry.sh
|
||||
```
|
||||
|
||||
### 1. 设置私有镜像仓库
|
||||
|
||||
#### 1.1 使用Docker Registry(推荐)
|
||||
|
||||
**方式1:使用部署脚本(推荐)**
|
||||
```bash
|
||||
# 在 git.1080p.hk 服务器上执行
|
||||
bash scripts/server/setup-registry.sh
|
||||
```
|
||||
|
||||
**方式2:手动部署**
|
||||
```bash
|
||||
# 在 git.1080p.hk 服务器上执行
|
||||
# 拉取Registry镜像
|
||||
docker pull registry:2
|
||||
|
||||
# 运行Registry(HTTP版本,内网使用)
|
||||
docker run -d \
|
||||
--name docker-registry \
|
||||
-p 5000:5000 \
|
||||
-v /var/lib/docker-registry:/var/lib/registry \
|
||||
--restart=always \
|
||||
registry:2
|
||||
```
|
||||
|
||||
**方式3:使用Docker Compose(推荐用于生产环境)**
|
||||
```bash
|
||||
# 上传 docker-compose-registry.yml 到服务器
|
||||
scp docker-compose-registry.yml root@git.1080p.hk:/root/
|
||||
|
||||
# 启动
|
||||
ssh root@git.1080p.hk
|
||||
cd /root
|
||||
docker compose -f docker-compose-registry.yml up -d
|
||||
```
|
||||
|
||||
#### 1.2 配置HTTPS(生产环境必须)
|
||||
|
||||
```bash
|
||||
# 生成证书
|
||||
mkdir -p /etc/docker/registry
|
||||
openssl req -x509 -newkey rsa:4096 -nodes \
|
||||
-keyout /etc/docker/registry/domain.key \
|
||||
-out /etc/docker/registry/domain.crt \
|
||||
-days 365 \
|
||||
-subj "/CN=your-registry.com"
|
||||
|
||||
# 使用HTTPS运行
|
||||
docker run -d \
|
||||
--name docker-registry \
|
||||
-p 5000:5000 \
|
||||
-v /etc/docker/registry:/certs \
|
||||
-v /var/lib/registry:/var/lib/registry \
|
||||
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
|
||||
-e REGISTRY_HTTP_TLS_PRIVATE_KEY=/certs/domain.key \
|
||||
--restart=always \
|
||||
registry:2
|
||||
```
|
||||
|
||||
### 2. 构建和推送镜像
|
||||
|
||||
#### 2.1 构建标准版镜像
|
||||
|
||||
```bash
|
||||
# 构建镜像
|
||||
docker build -t your-registry.com:5000/flnxi-material:standard-latest .
|
||||
|
||||
# 标记镜像
|
||||
docker tag your-registry.com:5000/flnxi-material:standard-latest \
|
||||
your-registry.com:5000/flnxi-material:standard-$(date +%Y%m%d)
|
||||
|
||||
# 推送到仓库
|
||||
docker push your-registry.com:5000/flnxi-material:standard-latest
|
||||
docker push your-registry.com:5000/flnxi-material:standard-$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
#### 2.2 构建Pro版镜像
|
||||
|
||||
```bash
|
||||
# Pro版使用相同的Dockerfile,但通过环境变量区分
|
||||
docker build -t your-registry.com:5000/flnxi-material:pro-latest .
|
||||
|
||||
# 标记镜像
|
||||
docker tag your-registry.com:5000/flnxi-material:pro-latest \
|
||||
your-registry.com:5000/flnxi-material:pro-$(date +%Y%m%d)
|
||||
|
||||
# 推送到仓库
|
||||
docker push your-registry.com:5000/flnxi-material:pro-latest
|
||||
docker push your-registry.com:5000/flnxi-material:pro-$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
### 3. 客户服务器配置
|
||||
|
||||
#### 3.1 配置镜像仓库地址
|
||||
|
||||
**在客户服务器上配置**(`/etc/docker/daemon.json`):
|
||||
```json
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.mirrors.ustc.edu.cn",
|
||||
"https://hub-mirror.c.163.com",
|
||||
"https://mirror.baidubce.com"
|
||||
],
|
||||
"insecure-registries": [
|
||||
"your-registry.com:5000"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**如果使用HTTPS**,移除 `insecure-registries`,配置证书:
|
||||
```bash
|
||||
# 复制证书到客户服务器
|
||||
scp your-registry.com:/etc/docker/registry/domain.crt /etc/docker/certs.d/your-registry.com:5000/ca.crt
|
||||
|
||||
# 重启Docker
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
#### 3.2 配置环境变量
|
||||
|
||||
**创建 `.env` 文件**:
|
||||
```bash
|
||||
# Docker镜像仓库地址
|
||||
DOCKER_REGISTRY=your-registry.com:5000
|
||||
|
||||
# 镜像标签
|
||||
IMAGE_TAG=latest
|
||||
|
||||
# 服务器IP
|
||||
SERVER_IP=客户服务器IP
|
||||
|
||||
# 数据库密码(建议修改)
|
||||
POSTGRES_PASSWORD=your-secure-password
|
||||
|
||||
# MinIO密码(Pro版需要)
|
||||
MINIO_ROOT_PASSWORD=your-secure-password
|
||||
```
|
||||
|
||||
### 4. 部署标准版
|
||||
|
||||
```bash
|
||||
# 1. 上传配置文件
|
||||
scp docker-compose.standard.yml root@客户服务器:/root/deploy/
|
||||
scp .env root@客户服务器:/root/deploy/
|
||||
|
||||
# 2. SSH登录客户服务器
|
||||
ssh root@客户服务器
|
||||
|
||||
# 3. 配置Docker镜像加速器
|
||||
bash scripts/server/setup-docker-mirror.sh
|
||||
|
||||
# 4. 部署
|
||||
cd /root/deploy
|
||||
docker compose -f docker-compose.standard.yml --env-file .env up -d
|
||||
```
|
||||
|
||||
### 5. 升级到Pro版
|
||||
|
||||
#### 方式1:使用升级脚本(推荐)
|
||||
|
||||
**PowerShell(Windows)**:
|
||||
```powershell
|
||||
.\scripts\deploy\upgrade-to-pro.ps1 `
|
||||
-Server 客户服务器IP `
|
||||
-DockerRegistry your-registry.com:5000 `
|
||||
-ImageTag latest
|
||||
```
|
||||
|
||||
**Bash(Linux)**:
|
||||
```bash
|
||||
export DOCKER_REGISTRY=your-registry.com:5000
|
||||
export IMAGE_TAG=latest
|
||||
bash scripts/deploy/upgrade-to-pro.sh
|
||||
```
|
||||
|
||||
#### 方式2:手动升级
|
||||
|
||||
```bash
|
||||
# 1. 备份
|
||||
cd /root/deploy
|
||||
mkdir -p backup
|
||||
cp docker-compose.yml backup/
|
||||
docker compose exec -T postgres pg_dump -U postgres flnxi > backup/db.sql
|
||||
|
||||
# 2. 停止服务
|
||||
docker compose down
|
||||
|
||||
# 3. 切换配置
|
||||
ln -sf docker-compose.prod.yml docker-compose.yml
|
||||
|
||||
# 4. 更新环境变量
|
||||
echo "DOCKER_REGISTRY=your-registry.com:5000" >> .env
|
||||
echo "IMAGE_TAG=latest" >> .env
|
||||
|
||||
# 5. 拉取镜像
|
||||
docker pull your-registry.com:5000/flnxi-material:pro-latest
|
||||
docker pull minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
docker pull minio/mc:RELEASE.2025-04-22T22-12-26Z
|
||||
|
||||
# 6. 启动服务
|
||||
docker compose --env-file .env up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 安全配置
|
||||
|
||||
### 1. 镜像仓库认证
|
||||
|
||||
**启用认证**:
|
||||
```bash
|
||||
# 创建认证文件
|
||||
mkdir -p /auth
|
||||
docker run --entrypoint htpasswd httpd:2 \
|
||||
-Bbn username password > /auth/htpasswd
|
||||
|
||||
# 使用认证运行Registry
|
||||
docker run -d \
|
||||
--name docker-registry \
|
||||
-p 5000:5000 \
|
||||
-v /auth:/auth \
|
||||
-e "REGISTRY_AUTH=htpasswd" \
|
||||
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
|
||||
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
|
||||
registry:2
|
||||
```
|
||||
|
||||
**客户服务器登录**:
|
||||
```bash
|
||||
docker login your-registry.com:5000
|
||||
# 输入用户名和密码
|
||||
```
|
||||
|
||||
### 2. 网络隔离
|
||||
|
||||
- 使用内网部署Registry
|
||||
- 配置防火墙规则
|
||||
- 使用VPN访问(如需要)
|
||||
|
||||
---
|
||||
|
||||
## 📝 版本管理
|
||||
|
||||
### 镜像标签策略
|
||||
|
||||
```
|
||||
标准版:
|
||||
- standard-latest # 最新版本
|
||||
- standard-20251115 # 日期版本
|
||||
- standard-v1.0.0 # 语义版本
|
||||
|
||||
Pro版:
|
||||
- pro-latest # 最新版本
|
||||
- pro-20251115 # 日期版本
|
||||
- pro-v1.0.0 # 语义版本
|
||||
```
|
||||
|
||||
### 更新流程
|
||||
|
||||
1. **构建新镜像**
|
||||
2. **推送到仓库**(保留旧版本)
|
||||
3. **在测试环境验证**
|
||||
4. **通知客户更新**
|
||||
5. **客户执行升级脚本**
|
||||
|
||||
---
|
||||
|
||||
## ✅ 检查清单
|
||||
|
||||
### 镜像仓库配置
|
||||
- [ ] Registry服务已部署
|
||||
- [ ] HTTPS证书已配置(生产环境)
|
||||
- [ ] 认证已启用(如需要)
|
||||
- [ ] 镜像已构建并推送
|
||||
|
||||
### 客户服务器配置
|
||||
- [ ] Docker镜像加速器已配置
|
||||
- [ ] 镜像仓库地址已配置
|
||||
- [ ] 证书已安装(HTTPS)
|
||||
- [ ] 环境变量已设置
|
||||
|
||||
### 部署验证
|
||||
- [ ] 标准版部署成功
|
||||
- [ ] 应用可以正常访问
|
||||
- [ ] 数据存储正常
|
||||
- [ ] 升级到Pro版成功
|
||||
|
||||
---
|
||||
|
||||
## 🐛 故障排查
|
||||
|
||||
### 问题1:无法拉取镜像
|
||||
**原因**:镜像仓库地址配置错误或网络问题
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查配置
|
||||
docker info | grep -A 10 "Insecure Registries"
|
||||
|
||||
# 测试连接
|
||||
curl -k https://your-registry.com:5000/v2/
|
||||
|
||||
# 检查DNS
|
||||
nslookup your-registry.com
|
||||
```
|
||||
|
||||
### 问题2:认证失败
|
||||
**原因**:未登录或凭证错误
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 重新登录
|
||||
docker login your-registry.com:5000
|
||||
|
||||
# 检查凭证
|
||||
cat ~/.docker/config.json
|
||||
```
|
||||
|
||||
### 问题3:升级失败
|
||||
**原因**:配置错误或镜像不存在
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 检查镜像是否存在
|
||||
docker pull your-registry.com:5000/flnxi-material:pro-latest
|
||||
|
||||
# 检查配置文件
|
||||
cat docker-compose.prod.yml | grep image
|
||||
|
||||
# 查看日志
|
||||
docker compose logs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-15
|
||||
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
# 素材模板迁移指南(新版本)
|
||||
|
||||
> 面向 AI 编程平台(如 Cursor)编写,帮助快速理解“素材模板 = 素材类型”的全新架构,并指导从老版本迁移。
|
||||
|
||||
## 1. 架构变化速览
|
||||
|
||||
- **模板即素材类型**:预制模板只保留 6 类(3DMAX、PSD、AI 工作流、视频、模特、其他),`materialType` 作为模板元数据存储在 `upload_templates.materialType` 字段中。
|
||||
- **字段更精简**:表单中不再渲染 `materialType` 字段,通用字段统一来自 `STANDARD_FIELD_TEMPLATES`,必填逻辑由模板控制。
|
||||
- **前台上传**:`components/UploadModal.tsx` 依据模板元数据决定素材类型,不再依赖隐藏字段。
|
||||
- **同步机制**:`BroadcastChannel + localStorage` 继续用于前后台同步启用状态,无需重新部署即可生效。
|
||||
|
||||
## 2. 迁移步骤(按顺序执行)
|
||||
|
||||
### Step 1:数据库
|
||||
|
||||
1. 执行 Prisma 迁移 `20251124001000_add_material_type_to_templates`:
|
||||
```bash
|
||||
npx prisma migrate deploy
|
||||
```
|
||||
2. 如需要手动执行:
|
||||
```sql
|
||||
ALTER TABLE "upload_templates"
|
||||
ADD COLUMN "materialType" TEXT NOT NULL DEFAULT '其他';
|
||||
UPDATE "upload_templates"
|
||||
SET "materialType" = COALESCE(NULLIF("materialType", ''), '其他');
|
||||
```
|
||||
|
||||
### Step 2:后端
|
||||
|
||||
- 确保以下 API 已更新并部署:
|
||||
- `app/api/upload-templates/route.ts`
|
||||
- `app/api/admin/upload-templates/*`
|
||||
- `app/api/templates/route.ts`
|
||||
- `app/api/material-types/route.ts`
|
||||
- `app/api/template-categories/*`
|
||||
- `app/api/materials/route.ts`
|
||||
- 关键检查点:
|
||||
- API 返回的模板对象需包含 `materialType`。
|
||||
- 预制模板列表只包含 6 个 key。
|
||||
- `template-categories` 不再引用 `materialType` 字段。
|
||||
|
||||
### Step 3:前端
|
||||
|
||||
- 主要文件:
|
||||
- `app/admin/_components/TemplateManager.tsx`
|
||||
- `app/admin/_components/template-manager/*`
|
||||
- `components/UploadModal.tsx`
|
||||
- 必须确认:
|
||||
1. 新建/编辑模板时可选择素材类型(下拉框来自 `MATERIAL_TYPE_OPTIONS`)。
|
||||
2. 保存模板时写入 `materialType` 字段。
|
||||
3. 上传弹窗根据模板的 `materialType` 控制字段、校验与文件要求。
|
||||
4. 预制模板列表与文档保持一致(3DMAX、PSD、AI 工作流、视频、模特、其他)。
|
||||
|
||||
### Step 4:文档与校验
|
||||
|
||||
- 更新 `docs/素材类型模板配置记录.md`(已完成)。
|
||||
- 确认根目录的本指南同步提交,供 AI 工具拉取。
|
||||
- 回归测试:
|
||||
- 切换预制模板触发前台刷新。
|
||||
- 上传不同类型素材,验证必填校验、文件校验与标签显示。
|
||||
- 首页筛选(模板 + 素材类型)可用。
|
||||
|
||||
## 3. 常见问题(FAQ)
|
||||
|
||||
| 问题 | 说明 | 排查建议 |
|
||||
|------|------|----------|
|
||||
| 模板保存后 `materialType` 为空 | 旧模板未手动选择类型 | 在后台编辑模板,补全素材类型后再次保存 |
|
||||
| 前台上传仍出现“素材类型”字段 | 构建未更新或缓存未失效 | 清理 `.next`、重新构建并刷新浏览器缓存 |
|
||||
| `/api/material-types` 返回旧数据 | 未部署最新 API 或存在历史模板 | 确认路由已更新,并检查 `upload_templates.materialType` |
|
||||
| 预制模板仍包含 CAD/C4D | `enabledBuiltinTemplates` 仍记录旧 key | 在数据库 `system_settings.enabledBuiltinTemplates` 中清理旧 key |
|
||||
|
||||
## 4. 建议的提交流程
|
||||
|
||||
1. **开发**:在本地完成所有代码改造 + 文档更新。
|
||||
2. **检查**:`npm run lint` / `npm run build`,确保无类型/构建错误。
|
||||
3. **部署**:执行 `deploy-new-fixed.ps1`(或 CI/CD 流程),留意镜像推送日志。
|
||||
4. **验证**:访问 `/admin` 与 `/`,测试 6 种模板上传路径。
|
||||
|
||||
---
|
||||
|
||||
> 如需进一步扩展素材类型,请在 `BUILTIN_TEMPLATES` 与 `BUILTIN_TEMPLATE_TYPES` 中新增配置,并同步更新本文档与 `docs/素材类型模板配置记录.md`。 AI 平台可直接搜索“素材模板迁移指南”获取最新步骤。*** End Patch
|
||||
|
||||
@ -1,202 +0,0 @@
|
||||
# 设计系统规范
|
||||
|
||||
## 概述
|
||||
|
||||
本文档定义了项目的统一视觉设计系统,确保所有界面元素保持一致的外观和交互体验。
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **一致性**:所有组件使用统一的圆角、阴影、间距
|
||||
2. **层次感**:通过阴影和颜色建立清晰的视觉层次
|
||||
3. **响应式**:适配各种屏幕尺寸
|
||||
4. **可访问性**:确保足够的对比度和可读性
|
||||
|
||||
## 视觉规范
|
||||
|
||||
### 1. 圆角 (Border Radius)
|
||||
|
||||
| 用途 | 类名 | 值 | 示例 |
|
||||
|------|------|-----|------|
|
||||
| 小元素(按钮、标签) | `rounded-lg` | 8px | 小按钮、徽章 |
|
||||
| 标准卡片 | `rounded-xl` | 12px | 标准卡片、输入框 |
|
||||
| 大卡片 | `rounded-2xl` | 16px | 主要内容卡片 |
|
||||
| 超大容器 | `rounded-3xl` | 24px | 特殊展示区域 |
|
||||
|
||||
**统一规则:**
|
||||
- 按钮、输入框:`rounded-xl` (12px)
|
||||
- 卡片:`rounded-2xl` (16px)
|
||||
- 导航项:`rounded-xl` (12px)
|
||||
- 特殊容器:`rounded-3xl` (24px)
|
||||
|
||||
### 2. 阴影 (Box Shadow)
|
||||
|
||||
| 层级 | 类名 | 值 | 用途 |
|
||||
|------|------|-----|------|
|
||||
| 无阴影 | `shadow-none` | - | 扁平元素 |
|
||||
| 轻微阴影 | `shadow-sm` | 0 1px 2px rgba(0,0,0,0.05) | 输入框、小卡片 |
|
||||
| 标准阴影 | `shadow-md` | 0 4px 6px rgba(0,0,0,0.07) | 悬停状态 |
|
||||
| 明显阴影 | `shadow-lg` | 0 10px 15px rgba(0,0,0,0.1) | 主要卡片 |
|
||||
| 强烈阴影 | `shadow-xl` | 0 20px 25px rgba(0,0,0,0.1) | 模态框、重要卡片 |
|
||||
| 极强阴影 | `shadow-2xl` | 0 25px 50px rgba(0,0,0,0.15) | 浮动面板 |
|
||||
|
||||
**统一规则:**
|
||||
- 默认卡片:`shadow-lg`
|
||||
- 悬停状态:`hover:shadow-xl`
|
||||
- 模态框/弹窗:`shadow-2xl`
|
||||
- 输入框:`shadow-sm` 或 `shadow-none`
|
||||
|
||||
### 3. 间距 (Spacing)
|
||||
|
||||
使用 Tailwind 标准间距系统:
|
||||
|
||||
| 用途 | 类名 | 值 | 示例 |
|
||||
|------|------|-----|------|
|
||||
| 紧密间距 | `gap-2` / `space-y-2` | 8px | 相关元素组 |
|
||||
| 标准间距 | `gap-4` / `space-y-4` | 16px | 卡片内元素 |
|
||||
| 宽松间距 | `gap-6` / `space-y-6` | 24px | 卡片之间 |
|
||||
| 大间距 | `gap-8` / `space-y-8` | 32px | 区块之间 |
|
||||
|
||||
**统一规则:**
|
||||
- 卡片内边距:`p-6` (24px)
|
||||
- 卡片间距:`gap-6` (24px)
|
||||
- 区块间距:`space-y-8` (32px)
|
||||
- 表单元素间距:`space-y-4` (16px)
|
||||
|
||||
### 4. 边框 (Border)
|
||||
|
||||
| 用途 | 类名 | 值 | 示例 |
|
||||
|------|------|-----|------|
|
||||
| 标准边框 | `border` | 1px | 卡片、输入框 |
|
||||
| 强调边框 | `border-2` | 2px | 选中状态 |
|
||||
| 左侧强调 | `border-l-4` | 4px | 导航项、列表项 |
|
||||
|
||||
**统一规则:**
|
||||
- 默认边框:`border border-slate-200 dark:border-slate-700`
|
||||
- 输入框边框:`border border-slate-300 dark:border-slate-600`
|
||||
- 选中状态:`border-2 border-indigo-500`
|
||||
|
||||
### 5. 颜色系统
|
||||
|
||||
#### 主色调
|
||||
- **主色**:Indigo (`indigo-500` = #6366f1)
|
||||
- **辅助色**:Purple (`purple-500` = #a855f7)
|
||||
- **成功色**:Green (`green-500` = #22c55e)
|
||||
- **警告色**:Yellow (`yellow-500` = #eab308)
|
||||
- **错误色**:Red (`red-500` = #ef4444)
|
||||
|
||||
#### 中性色
|
||||
- **背景色**:
|
||||
- 浅色模式:`bg-white` / `bg-gray-50`
|
||||
- 深色模式:`bg-slate-900` / `bg-slate-800`
|
||||
- **文本色**:
|
||||
- 浅色模式:`text-gray-900` / `text-gray-600`
|
||||
- 深色模式:`text-white` / `text-slate-300`
|
||||
|
||||
### 6. 过渡动画
|
||||
|
||||
| 属性 | 类名 | 值 | 用途 |
|
||||
|------|------|-----|------|
|
||||
| 标准过渡 | `transition-all` | all 200ms ease | 通用交互 |
|
||||
| 快速过渡 | `transition-all duration-150` | all 150ms | 悬停反馈 |
|
||||
| 慢速过渡 | `transition-all duration-300` | all 300ms | 状态变化 |
|
||||
|
||||
**统一规则:**
|
||||
- 所有交互元素:`transition-all duration-200`
|
||||
- 悬停效果:`hover:scale-[1.02]` 或 `hover:shadow-xl`
|
||||
- 点击效果:`active:scale-[0.98]`
|
||||
|
||||
## 组件样式规范
|
||||
|
||||
### 卡片 (Card)
|
||||
|
||||
```tsx
|
||||
// 标准卡片
|
||||
className="rounded-2xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-lg p-6"
|
||||
|
||||
// 悬停卡片
|
||||
className="rounded-2xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-lg hover:shadow-xl transition-all duration-200 p-6"
|
||||
```
|
||||
|
||||
### 按钮 (Button)
|
||||
|
||||
```tsx
|
||||
// 主要按钮
|
||||
className="px-6 py-3 rounded-xl bg-indigo-600 text-white font-medium shadow-lg shadow-indigo-500/30 hover:bg-indigo-700 hover:shadow-xl transition-all duration-200"
|
||||
|
||||
// 次要按钮
|
||||
className="px-6 py-3 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 font-medium hover:bg-slate-50 dark:hover:bg-slate-700 transition-all duration-200"
|
||||
```
|
||||
|
||||
### 输入框 (Input)
|
||||
|
||||
```tsx
|
||||
className="w-full px-4 py-3 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 text-slate-900 dark:text-white focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 dark:focus:ring-indigo-800 outline-none transition-all"
|
||||
```
|
||||
|
||||
### 导航项 (Nav Item)
|
||||
|
||||
```tsx
|
||||
className="px-4 py-3 rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 hover:border-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 transition-all duration-200"
|
||||
```
|
||||
|
||||
## 实施指南
|
||||
|
||||
### 1. 使用 Tailwind 工具类
|
||||
|
||||
优先使用 Tailwind 预设的工具类,避免自定义样式:
|
||||
|
||||
```tsx
|
||||
// ✅ 正确
|
||||
<div className="rounded-2xl shadow-lg p-6">
|
||||
|
||||
// ❌ 错误
|
||||
<div className="rounded-[16px] shadow-[0_10px_15px_rgba(0,0,0,0.1)] p-[24px]">
|
||||
```
|
||||
|
||||
### 2. 创建可复用组件
|
||||
|
||||
将常用样式封装为组件:
|
||||
|
||||
```tsx
|
||||
// components/ui/Card.tsx
|
||||
export function Card({ children, className = '' }) {
|
||||
return (
|
||||
<div className={`rounded-2xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-lg p-6 ${className}`}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 使用 CSS 变量(可选)
|
||||
|
||||
对于需要动态调整的值,使用 CSS 变量:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 24px;
|
||||
}
|
||||
```
|
||||
|
||||
## 检查清单
|
||||
|
||||
在开发新组件时,请确保:
|
||||
|
||||
- [ ] 使用统一的圆角值(`rounded-xl` 或 `rounded-2xl`)
|
||||
- [ ] 使用统一的阴影层级(`shadow-lg` 或 `shadow-xl`)
|
||||
- [ ] 使用统一的间距系统(`p-6`, `gap-6`, `space-y-6`)
|
||||
- [ ] 使用统一的颜色系统(Indigo 主色)
|
||||
- [ ] 添加过渡动画(`transition-all duration-200`)
|
||||
- [ ] 支持深色模式(`dark:` 前缀)
|
||||
- [ ] 确保足够的对比度
|
||||
|
||||
## 迁移计划
|
||||
|
||||
1. **阶段一**:更新 Tailwind 配置,添加统一的设计令牌
|
||||
2. **阶段二**:创建可复用的 UI 组件库
|
||||
3. **阶段三**:逐步迁移现有组件到新规范
|
||||
4. **阶段四**:建立代码审查流程,确保新代码遵循规范
|
||||
|
||||
@ -1,253 +0,0 @@
|
||||
# 设计系统使用指南
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 导入样式工具
|
||||
|
||||
```tsx
|
||||
import { cardStyles, buttonStyles, inputStyles, navItemStyles, cn } from '@/lib/ui-styles'
|
||||
```
|
||||
|
||||
### 2. 使用统一样式
|
||||
|
||||
#### 卡片 (Card)
|
||||
|
||||
```tsx
|
||||
// 标准卡片
|
||||
<div className={cardStyles.base}>
|
||||
<h2>标题</h2>
|
||||
<p>内容</p>
|
||||
</div>
|
||||
|
||||
// 带悬停效果的卡片
|
||||
<div className={cardStyles.hover}>
|
||||
<h2>标题</h2>
|
||||
<p>内容</p>
|
||||
</div>
|
||||
|
||||
// 大卡片
|
||||
<div className={cardStyles.large}>
|
||||
<h2>标题</h2>
|
||||
<p>内容</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 按钮 (Button)
|
||||
|
||||
```tsx
|
||||
// 主要按钮
|
||||
<button className={buttonStyles.primary}>
|
||||
保存
|
||||
</button>
|
||||
|
||||
// 次要按钮
|
||||
<button className={buttonStyles.secondary}>
|
||||
取消
|
||||
</button>
|
||||
|
||||
// 危险操作按钮
|
||||
<button className={buttonStyles.danger}>
|
||||
删除
|
||||
</button>
|
||||
|
||||
// 幽灵按钮(无背景)
|
||||
<button className={buttonStyles.ghost}>
|
||||
更多
|
||||
</button>
|
||||
```
|
||||
|
||||
#### 输入框 (Input)
|
||||
|
||||
```tsx
|
||||
// 标准输入框
|
||||
<input
|
||||
type="text"
|
||||
className={inputStyles.base}
|
||||
placeholder="请输入..."
|
||||
/>
|
||||
|
||||
// 错误状态输入框
|
||||
<input
|
||||
type="text"
|
||||
className={inputStyles.error}
|
||||
placeholder="请输入..."
|
||||
/>
|
||||
```
|
||||
|
||||
#### 导航项 (Nav Item)
|
||||
|
||||
```tsx
|
||||
// 基础导航项
|
||||
<button className={navItemStyles.base}>
|
||||
导航项
|
||||
</button>
|
||||
|
||||
// 带悬停效果
|
||||
<button className={navItemStyles.hover}>
|
||||
导航项
|
||||
</button>
|
||||
|
||||
// 激活状态
|
||||
<button className={navItemStyles.active}>
|
||||
导航项
|
||||
</button>
|
||||
```
|
||||
|
||||
#### 组合样式
|
||||
|
||||
```tsx
|
||||
import { cn } from '@/lib/ui-styles'
|
||||
|
||||
// 组合多个样式类
|
||||
<div className={cn(
|
||||
cardStyles.base,
|
||||
'mt-4', // 额外的 Tailwind 类
|
||||
isActive && 'ring-2 ring-indigo-500' // 条件样式
|
||||
)}>
|
||||
内容
|
||||
</div>
|
||||
```
|
||||
|
||||
## 迁移现有代码
|
||||
|
||||
### 迁移前
|
||||
|
||||
```tsx
|
||||
// ❌ 不统一的样式
|
||||
<div className="rounded-lg border border-gray-200 bg-white shadow-md p-4">
|
||||
<button className="px-4 py-2 rounded-md bg-blue-500 text-white">
|
||||
按钮
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 迁移后
|
||||
|
||||
```tsx
|
||||
// ✅ 使用统一样式
|
||||
<div className={cardStyles.base}>
|
||||
<button className={buttonStyles.primary}>
|
||||
按钮
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 优先使用样式工具
|
||||
|
||||
```tsx
|
||||
// ✅ 正确
|
||||
import { cardStyles } from '@/lib/ui-styles'
|
||||
<div className={cardStyles.base}>
|
||||
|
||||
// ❌ 错误(除非有特殊需求)
|
||||
<div className="rounded-2xl border border-slate-200 bg-white shadow-lg p-6">
|
||||
```
|
||||
|
||||
### 2. 使用 cn() 函数组合样式
|
||||
|
||||
```tsx
|
||||
// ✅ 正确
|
||||
import { cn, cardStyles } from '@/lib/ui-styles'
|
||||
<div className={cn(cardStyles.base, 'mt-4', isActive && 'ring-2')}>
|
||||
|
||||
// ❌ 错误
|
||||
<div className={`${cardStyles.base} mt-4 ${isActive ? 'ring-2' : ''}`}>
|
||||
```
|
||||
|
||||
### 3. 保持一致性
|
||||
|
||||
```tsx
|
||||
// ✅ 正确:所有卡片使用相同的样式
|
||||
<div className={cardStyles.base}>卡片 1</div>
|
||||
<div className={cardStyles.base}>卡片 2</div>
|
||||
<div className={cardStyles.base}>卡片 3</div>
|
||||
|
||||
// ❌ 错误:每个卡片样式都不同
|
||||
<div className="rounded-lg shadow-md p-4">卡片 1</div>
|
||||
<div className="rounded-xl shadow-lg p-6">卡片 2</div>
|
||||
<div className="rounded-2xl shadow-xl p-8">卡片 3</div>
|
||||
```
|
||||
|
||||
## 常见场景
|
||||
|
||||
### 表单
|
||||
|
||||
```tsx
|
||||
import { cardStyles, inputStyles, buttonStyles, spacingStyles } from '@/lib/ui-styles'
|
||||
|
||||
<form className={cn(cardStyles.base, spacingStyles.form)}>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">用户名</label>
|
||||
<input type="text" className={inputStyles.base} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">密码</label>
|
||||
<input type="password" className={inputStyles.base} />
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<button type="submit" className={buttonStyles.primary}>
|
||||
提交
|
||||
</button>
|
||||
<button type="button" className={buttonStyles.secondary}>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
```
|
||||
|
||||
### 导航菜单
|
||||
|
||||
```tsx
|
||||
import { navItemStyles } from '@/lib/ui-styles'
|
||||
|
||||
<nav className="space-y-2">
|
||||
{items.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
className={item.active ? navItemStyles.active : navItemStyles.hover}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
```
|
||||
|
||||
### 卡片列表
|
||||
|
||||
```tsx
|
||||
import { cardStyles, spacingStyles } from '@/lib/ui-styles'
|
||||
|
||||
<div className={cn('grid gap-6', spacingStyles.section)}>
|
||||
{items.map((item) => (
|
||||
<div key={item.id} className={cardStyles.hover}>
|
||||
<h3>{item.title}</h3>
|
||||
<p>{item.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
## 扩展样式
|
||||
|
||||
如果需要添加新的样式,请在 `lib/ui-styles.ts` 中添加:
|
||||
|
||||
```tsx
|
||||
// lib/ui-styles.ts
|
||||
export const customStyles = {
|
||||
// 你的自定义样式
|
||||
special: 'rounded-2xl bg-gradient-to-r from-indigo-500 to-purple-500 ...',
|
||||
} as const
|
||||
```
|
||||
|
||||
## 检查清单
|
||||
|
||||
在提交代码前,请检查:
|
||||
|
||||
- [ ] 使用了统一的样式工具类
|
||||
- [ ] 圆角、阴影、间距符合规范
|
||||
- [ ] 支持深色模式
|
||||
- [ ] 添加了过渡动画
|
||||
- [ ] 响应式设计正确
|
||||
|
||||
@ -1,146 +0,0 @@
|
||||
# 设计系统迁移示例
|
||||
|
||||
## 概述
|
||||
|
||||
本文档展示了如何将现有组件迁移到统一的设计系统。以 `AppearanceSettingsPanel.tsx` 为例。
|
||||
|
||||
## 迁移步骤
|
||||
|
||||
### 1. 导入样式工具
|
||||
|
||||
```tsx
|
||||
// 迁移前
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
// 迁移后
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { cardStyles, buttonStyles, inputStyles, navItemStyles, cn } from '@/lib/ui-styles'
|
||||
```
|
||||
|
||||
### 2. 替换卡片样式
|
||||
|
||||
```tsx
|
||||
// 迁移前
|
||||
<div className="rounded-2xl border border-slate-200/60 dark:border-slate-700/60 bg-white/80 dark:bg-slate-900/60 p-8">
|
||||
|
||||
// 迁移后
|
||||
<div className={cn(cardStyles.base, 'flex flex-col items-center gap-4')}>
|
||||
```
|
||||
|
||||
### 3. 替换按钮样式
|
||||
|
||||
```tsx
|
||||
// 迁移前
|
||||
<button className="px-6 py-3 rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 hover:bg-slate-50 dark:hover:bg-slate-700 transition-all">
|
||||
|
||||
// 迁移后
|
||||
<button className={buttonStyles.secondary}>
|
||||
```
|
||||
|
||||
### 4. 替换输入框样式
|
||||
|
||||
```tsx
|
||||
// 迁移前
|
||||
<input className="w-full px-4 py-3 rounded-2xl border border-slate-200 dark:border-slate-700 bg-slate-50/80 dark:bg-slate-900 text-slate-900 dark:text-white focus:border-indigo-400 focus:ring-2 focus:ring-indigo-200 dark:focus:ring-indigo-600 outline-none transition-all" />
|
||||
|
||||
// 迁移后
|
||||
<input className={inputStyles.base} />
|
||||
```
|
||||
|
||||
### 5. 替换导航项样式
|
||||
|
||||
```tsx
|
||||
// 迁移前
|
||||
const navButtonClass = (key: SectionKey) =>
|
||||
`w-full px-4 py-3 rounded-2xl border text-left group transition-all duration-500 ${
|
||||
activeNav === key
|
||||
? 'border-indigo-400/70 bg-white dark:bg-slate-900 shadow-lg shadow-indigo-500/30'
|
||||
: 'border-slate-200/70 dark:border-slate-700/70 hover:border-indigo-400/50 hover:bg-indigo-50/70 dark:hover:bg-indigo-900/20'
|
||||
}`
|
||||
|
||||
// 迁移后
|
||||
const navButtonClass = (key: SectionKey) =>
|
||||
cn(
|
||||
'w-full text-left group',
|
||||
activeNav === key ? navItemStyles.active : navItemStyles.hover
|
||||
)
|
||||
```
|
||||
|
||||
### 6. 使用 cn() 组合样式
|
||||
|
||||
```tsx
|
||||
// 迁移前
|
||||
<div className={`rounded-3xl border border-slate-200/70 dark:border-slate-700/40 bg-white dark:bg-[#141e32] shadow-xl p-6 space-y-6 ${sectionRevealClass('brand')} ${staggerDelays.brand}`}>
|
||||
|
||||
// 迁移后
|
||||
<div className={cn(
|
||||
cardStyles.large,
|
||||
'space-y-6',
|
||||
sectionRevealClass('brand'),
|
||||
staggerDelays.brand
|
||||
)}>
|
||||
```
|
||||
|
||||
## 迁移统计
|
||||
|
||||
在 `AppearanceSettingsPanel.tsx` 中:
|
||||
|
||||
- ✅ 替换了 6 个卡片样式
|
||||
- ✅ 替换了 8 个按钮样式
|
||||
- ✅ 替换了 8 个输入框样式
|
||||
- ✅ 替换了导航项样式函数
|
||||
- ✅ 使用 `cn()` 函数统一组合样式
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 保留特殊样式
|
||||
|
||||
对于有特殊需求的组件(如渐变背景),可以保留部分自定义样式:
|
||||
|
||||
```tsx
|
||||
// 保留渐变背景的特殊按钮
|
||||
<button className={cn(
|
||||
'px-5 py-2.5 rounded-xl bg-gradient-to-r from-indigo-500 to-purple-500 text-white text-sm font-semibold shadow-lg shadow-indigo-500/40 hover:shadow-xl transition-all duration-200',
|
||||
'disabled:opacity-60 disabled:cursor-not-allowed'
|
||||
)}>
|
||||
```
|
||||
|
||||
### 2. 条件样式
|
||||
|
||||
使用 `cn()` 函数处理条件样式:
|
||||
|
||||
```tsx
|
||||
<div className={cn(
|
||||
cardStyles.base,
|
||||
isActive && 'ring-2 ring-indigo-500',
|
||||
isDisabled && 'opacity-50'
|
||||
)}>
|
||||
```
|
||||
|
||||
### 3. 额外类名
|
||||
|
||||
在统一样式基础上添加额外的 Tailwind 类:
|
||||
|
||||
```tsx
|
||||
<input className={cn(inputStyles.base, 'text-sm font-mono')} />
|
||||
```
|
||||
|
||||
## 验证清单
|
||||
|
||||
迁移完成后,请检查:
|
||||
|
||||
- [ ] 所有卡片使用统一的圆角(`rounded-2xl` 或 `rounded-3xl`)
|
||||
- [ ] 所有按钮使用统一的样式工具类
|
||||
- [ ] 所有输入框使用统一的样式工具类
|
||||
- [ ] 导航项使用统一的样式工具类
|
||||
- [ ] 深色模式正常工作
|
||||
- [ ] 悬停和交互效果正常
|
||||
- [ ] 响应式布局正常
|
||||
- [ ] 没有 lint 错误
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 继续迁移其他组件
|
||||
2. 在代码审查中检查是否遵循设计系统
|
||||
3. 更新组件文档,说明使用的样式工具类
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
# 部署偏好更新完成总结
|
||||
|
||||
## ✅ 已完成的工作
|
||||
|
||||
### 1. 更新部署脚本
|
||||
- ✅ `scripts/deploy/安全部署.ps1`、`scripts/deploy/安全部署.sh` 改为直接上传源代码方式
|
||||
|
||||
### 2. 更新用户偏好设置文档
|
||||
- ✅ `用户偏好设置.md`、`用户偏好设置总结.md`、`部署排除配置.md`、`查看偏好设置.bat`
|
||||
|
||||
### 3. 创建新文档
|
||||
- ✅ `部署方式更新说明.md`(详细说明新的部署方式)
|
||||
|
||||
## 🚀 新的部署方式
|
||||
- 打包源代码 → 上传 → 服务器 Docker 重新构建 → 自动部署与迁移
|
||||
|
||||
## 📝 注意事项
|
||||
- `VERSION.txt` 可按需保留(记录版本),不影响部署与镜像最小化
|
||||
|
||||
|
||||
@ -1,110 +0,0 @@
|
||||
# 部署成功报告
|
||||
|
||||
## ✅ 部署状态
|
||||
|
||||
**部署时间:** 2025-11-11 10:26
|
||||
**服务器:** 45.249.208.118:4320
|
||||
**容器名称:** flnxi-material
|
||||
**容器状态:** ✅ 正在运行
|
||||
**应用状态:** ✅ Next.js 已就绪
|
||||
|
||||
---
|
||||
|
||||
## 📋 本次更新内容
|
||||
|
||||
### 1. **断点续传功能** ✅
|
||||
- 上传过程中关闭页面后,重新打开会提示恢复上传
|
||||
- 上传状态自动保存到 `localStorage`
|
||||
- 支持恢复表单数据
|
||||
|
||||
### 2. **视频上传功能** ✅
|
||||
- 支持多种视频格式(.mp4, .avi, .mov, .wmv, .flv, .mkv, .webm)
|
||||
- 视频上传需要封面图(预览图)
|
||||
- 视频流式传输支持(Range 请求)
|
||||
- 新增视频播放 API:`/api/materials/stream`
|
||||
|
||||
### 3. **排行榜滚动间隔调整** ✅
|
||||
- 从 5 秒调整为 10 秒
|
||||
- 三个标签(贡献、下载、点赞)自动切换间隔更慢
|
||||
|
||||
### 4. **数据库 Schema 更新** ✅
|
||||
- 添加"视频"类型到 `materialType` 字段
|
||||
- 数据库迁移已执行
|
||||
|
||||
---
|
||||
|
||||
## 🐳 Docker 镜像说明
|
||||
|
||||
### 当前使用的镜像
|
||||
- **镜像名称:** `youyidian-3d-app:latest`
|
||||
- **创建时间:** 2025-11-08 17:35:29
|
||||
- **镜像大小:** 303 MB
|
||||
|
||||
### 关于镜像更新
|
||||
|
||||
**重要说明:**
|
||||
- ✅ 本次部署使用了**现有的 Docker 镜像**
|
||||
- ❌ 无法从 Docker Hub 拉取新的基础镜像(网络限制)
|
||||
- ✅ 但是**应用代码已更新**(源代码已上传并部署)
|
||||
|
||||
**为什么可以使用旧镜像?**
|
||||
1. Docker 镜像只包含运行环境和依赖
|
||||
2. 应用代码是通过 Volume 挂载或重新构建时复制的
|
||||
3. 本次更新主要是代码变更,不涉及依赖更新
|
||||
4. 旧镜像仍然可以运行新代码
|
||||
|
||||
**何时需要更新镜像?**
|
||||
- 当 `package.json` 中的依赖发生变化时
|
||||
- 当 `Dockerfile` 中的系统依赖发生变化时
|
||||
- 当需要更新 Node.js 版本时
|
||||
|
||||
---
|
||||
|
||||
## 🌐 访问地址
|
||||
|
||||
### 内部访问
|
||||
```
|
||||
http://localhost:4320
|
||||
```
|
||||
|
||||
### 外部访问
|
||||
```
|
||||
http://45.249.208.118:4320
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 部署详情
|
||||
|
||||
### 容器信息
|
||||
```
|
||||
容器名称: flnxi-material
|
||||
镜像: youyidian-3d-app:latest
|
||||
端口映射: 0.0.0.0:4320->3000/tcp
|
||||
状态: Up (运行中)
|
||||
```
|
||||
|
||||
### 应用日志
|
||||
```
|
||||
Next.js 14.2.33
|
||||
- Local: http://localhost:3000
|
||||
- Network: http://0.0.0.0:3000
|
||||
✓ Starting...
|
||||
✓ Ready in 249ms
|
||||
```
|
||||
|
||||
### 磁盘使用
|
||||
```
|
||||
总容量: 50GB
|
||||
已使用: 18GB (35%)
|
||||
可用: 33GB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 功能验证清单
|
||||
|
||||
…(内容同原文件,保持不变)
|
||||
|
||||
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
# 部署排除配置说明
|
||||
|
||||
## 📋 概述
|
||||
|
||||
此文档说明部署时哪些文件会被排除,哪些文件会被包含。
|
||||
|
||||
## ❌ 排除的文件类型
|
||||
|
||||
…(内容同原文件,保持不变)
|
||||
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
# 部署方式更新说明
|
||||
|
||||
## 📋 更新内容
|
||||
|
||||
…(内容同原文件,保持不变)
|
||||
|
||||
|
||||
@ -1,398 +0,0 @@
|
||||
# 项目优化分析与建议
|
||||
|
||||
## 📋 项目概述
|
||||
|
||||
### 项目目标
|
||||
**最终目的**:让其他公司能够**一键部署**或**一条命令**从Docker部署到自己的NAS或服务器上。
|
||||
|
||||
**核心原则**:
|
||||
1. **性能开销优先** - 最小化资源消耗,确保在低配置设备上也能稳定运行
|
||||
2. **稳定性优先** - 确保长期稳定运行,减少崩溃和错误
|
||||
3. **易于部署** - 零配置或最小配置即可运行
|
||||
4. **可维护性** - 代码清晰,易于理解和维护
|
||||
5. **国内镜像源优先** - 所有依赖、库等外部资源必须使用国内镜像源
|
||||
|
||||
### 技术栈
|
||||
- **前端**:Next.js 14 + React 18 + TypeScript + TailwindCSS
|
||||
- **后端**:Next.js API Routes
|
||||
- **数据库**:PostgreSQL + Prisma ORM
|
||||
- **存储**:MinIO (S3兼容)
|
||||
- **部署**:Docker + Docker Compose
|
||||
|
||||
## 🔍 优化点分析
|
||||
|
||||
### 1. 性能优化(优先级:高)
|
||||
|
||||
#### 1.1 前端性能
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **图片优化** | 已禁用Next.js Image Optimizer,使用unoptimized属性 | 1. 实现图片懒加载,仅加载可视区域内图片<br>2. 为不同设备尺寸提供不同分辨率图片<br>3. 优化图片格式(WebP/AVIF优先) | 减少初始加载时间,降低带宽消耗 |
|
||||
| **代码分割** | 基本实现,但仍有优化空间 | 1. 进一步拆分大型组件<br>2. 实现路由级别的代码分割<br>3. 优化第三方库的按需加载 | 减少初始JS bundle大小,提升页面加载速度 |
|
||||
| **静态资源缓存** | 基础配置 | 1. 优化缓存策略,设置合理的Cache-Control头<br>2. 实现静态资源版本控制<br>3. 考虑使用CDN加速静态资源 | 减少重复请求,提升资源加载速度 |
|
||||
| **渲染优化** | 部分组件使用了React.memo | 1. 为更多组件添加React.memo优化<br>2. 优化useMemo和useCallback的使用<br>3. 实现虚拟滚动,处理大量数据列表 | 减少不必要的重渲染,提升页面响应速度 |
|
||||
|
||||
#### 1.2 后端性能
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **数据库查询优化** | 部分查询使用了select和分页 | 1. 为所有查询添加合理的索引<br>2. 进一步优化查询,仅选择必要字段<br>3. 实现数据库连接池的合理配置 | 减少数据库查询时间,降低数据库负载 |
|
||||
| **API响应优化** | 基础实现 | 1. 实现API响应的压缩<br>2. 优化JSON序列化<br>3. 实现API请求的缓存机制 | 减少API响应时间,降低带宽消耗 |
|
||||
| **文件处理优化** | 部分使用了流式处理 | 1. 确保所有大文件处理都使用流式API<br>2. 优化文件上传/下载的并发控制<br>3. 实现断点续传功能 | 减少内存占用,提升文件处理效率 |
|
||||
| **缓存策略** | 基础实现 | 1. 实现更完善的缓存机制,包括内存缓存和Redis缓存<br>2. 设置合理的缓存过期时间<br>3. 实现缓存的失效策略 | 减少重复计算和数据库查询,提升响应速度 |
|
||||
|
||||
### 2. 稳定性优化(优先级:高)
|
||||
|
||||
#### 2.1 错误处理
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **全局错误处理** | 部分实现 | 1. 实现全局的错误捕获机制<br>2. 为所有API路由添加统一的错误处理<br>3. 实现前端错误边界,防止页面崩溃 | 提高系统的容错能力,减少崩溃 |
|
||||
| **日志系统** | 基础实现,使用console.log | 1. 实现结构化日志系统<br>2. 添加日志级别控制<br>3. 实现日志的持久化存储<br>4. 添加日志分析和监控 | 便于问题定位和系统监控 |
|
||||
| **异常恢复机制** | 部分实现 | 1. 实现更完善的异常恢复机制<br>2. 添加自动重启功能<br>3. 实现服务的健康检查 | 提高系统的自愈能力,减少 downtime |
|
||||
|
||||
#### 2.2 资源管理
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **内存管理** | 基础实现 | 1. 实现更严格的内存泄漏检测<br>2. 优化大对象的处理<br>3. 实现资源的自动释放机制 | 减少内存泄漏,降低内存占用 |
|
||||
| **文件描述符管理** | 部分实现 | 1. 确保所有文件流都正确关闭<br>2. 优化文件描述符的使用<br>3. 实现文件描述符的监控 | 防止文件描述符泄漏,提高系统稳定性 |
|
||||
| **数据库连接管理** | 基础实现,使用Prisma连接池 | 1. 优化数据库连接池的配置<br>2. 实现数据库连接的监控<br>3. 确保所有数据库连接都正确关闭 | 防止数据库连接泄漏,提高数据库稳定性 |
|
||||
|
||||
### 3. 部署友好性优化(优先级:中)
|
||||
|
||||
#### 3.1 Docker优化
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **镜像大小优化** | 基础实现,使用多阶段构建 | 1. 进一步优化Dockerfile,减少镜像大小<br>2. 清理不必要的依赖和文件<br>3. 使用更小的基础镜像 | 减少镜像下载时间,降低存储占用 |
|
||||
| **镜像构建优化** | 基础实现 | 1. 优化Docker构建缓存<br>2. 实现并行构建<br>3. 优化构建脚本 | 减少镜像构建时间,提高构建效率 |
|
||||
| **部署脚本优化** | 已实现PowerShell和Bash脚本 | 1. 进一步优化部署脚本,提高可靠性<br>2. 添加更详细的部署日志<br>3. 实现部署的回滚机制<br>4. 添加部署的健康检查 | 提高部署的可靠性和易用性 |
|
||||
|
||||
#### 3.2 配置管理
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **环境变量管理** | 已实现,支持多种环境配置 | 1. 进一步优化环境变量的管理<br>2. 添加环境变量的验证<br>3. 实现环境变量的文档化 | 提高配置的可靠性和易用性 |
|
||||
| **配置文件优化** | 基础实现 | 1. 优化配置文件的结构<br>2. 添加配置文件的验证<br>3. 实现配置文件的版本控制 | 提高配置的可靠性和可维护性 |
|
||||
|
||||
### 4. 可维护性优化(优先级:中)
|
||||
|
||||
#### 4.1 代码质量
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **代码规范** | 已实现基本规范 | 1. 进一步完善代码规范<br>2. 实现自动化的代码检查<br>3. 实现自动化的代码格式化 | 提高代码的一致性和可读性 |
|
||||
| **测试覆盖率** | 基础实现,缺少单元测试和集成测试 | 1. 提高测试覆盖率,包括单元测试和集成测试<br>2. 实现自动化测试<br>3. 实现测试报告 | 提高代码的可靠性,减少bug |
|
||||
| **文档完善** | 已实现部分文档 | 1. 完善API文档<br>2. 完善组件文档<br>3. 完善部署和使用文档 | 提高代码的可理解性和可维护性 |
|
||||
|
||||
#### 4.2 架构优化
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **模块化设计** | 部分实现,已拆分部分组件 | 1. 进一步优化模块化设计<br>2. 实现更清晰的模块边界<br>3. 优化模块间的依赖关系 | 提高代码的可复用性和可维护性 |
|
||||
| **状态管理** | 基础实现,使用React Context | 1. 优化状态管理,考虑使用更适合的状态管理方案<br>2. 实现更清晰的状态流转<br>3. 优化状态的更新机制 | 提高状态管理的可靠性和可维护性 |
|
||||
| **API设计** | 基础实现,RESTful风格 | 1. 进一步优化API设计<br>2. 实现更清晰的API版本控制<br>3. 优化API的命名和结构 | 提高API的易用性和可维护性 |
|
||||
|
||||
### 5. 国内镜像源优化(优先级:高)
|
||||
|
||||
| 优化项 | 当前状态 | 建议 | 预期收益 |
|
||||
|--------|----------|------|----------|
|
||||
| **npm镜像源** | 已配置,使用npmmirror.com | 1. 确保所有环境都使用国内npm镜像源<br>2. 优化npm安装配置,提高安装速度 | 提高依赖安装速度,确保国内用户的正常使用 |
|
||||
| **Docker镜像源** | 已配置,使用国内镜像加速器 | 1. 确保所有Docker镜像都从国内镜像源拉取<br>2. 优化Docker镜像的拉取配置 | 提高镜像拉取速度,确保国内用户的正常使用 |
|
||||
| **系统包镜像源** | 已配置,Alpine使用阿里云镜像源 | 1. 确保所有系统包都从国内镜像源安装<br>2. 优化系统包的安装配置 | 提高系统包安装速度,确保国内用户的正常使用 |
|
||||
| **CDN资源** | 基础配置,使用jsDelivr | 1. 进一步优化CDN资源的使用<br>2. 考虑使用国内CDN服务<br>3. 实现CDN资源的回退机制 | 提高静态资源加载速度,确保国内用户的正常使用 |
|
||||
|
||||
## 📊 优化优先级排序
|
||||
|
||||
### 优先级 1:必须立即优化
|
||||
|
||||
1. **数据库索引优化** - 为所有查询添加合理的索引,提高数据库查询性能
|
||||
2. **全局错误处理机制** - 实现完善的错误捕获和处理机制,提高系统稳定性
|
||||
3. **结构化日志系统** - 实现完善的日志系统,便于问题定位和系统监控
|
||||
4. **图片懒加载** - 实现图片懒加载,减少初始加载时间
|
||||
5. **国内镜像源全面检查** - 确保所有外部资源都使用国内镜像源
|
||||
|
||||
### 优先级 2:短期内优化
|
||||
|
||||
1. **代码分割优化** - 进一步拆分大型组件,减少初始JS bundle大小
|
||||
2. **Docker镜像优化** - 进一步优化Docker镜像,减少镜像大小和构建时间
|
||||
3. **缓存策略优化** - 实现完善的缓存机制,提高系统响应速度
|
||||
4. **测试覆盖率提升** - 提高测试覆盖率,减少bug
|
||||
5. **API文档完善** - 完善API文档,提高API的易用性
|
||||
|
||||
### 优先级 3:长期优化
|
||||
|
||||
1. **模块化设计优化** - 进一步优化模块化设计,提高代码的可复用性
|
||||
2. **状态管理优化** - 优化状态管理,提高状态管理的可靠性
|
||||
3. **虚拟滚动实现** - 实现虚拟滚动,处理大量数据列表
|
||||
4. **CDN加速** - 考虑使用CDN加速静态资源
|
||||
5. **自动化测试实现** - 实现自动化测试,提高代码质量
|
||||
|
||||
## 🚀 具体优化建议
|
||||
|
||||
### 1. 数据库索引优化
|
||||
|
||||
**实施步骤**:
|
||||
1. 分析所有数据库查询,识别频繁查询的字段
|
||||
2. 为这些字段添加合理的索引
|
||||
3. 测试索引效果,优化索引配置
|
||||
4. 监控索引使用情况,定期优化
|
||||
|
||||
**关键表和字段**:
|
||||
- `Material`表:`userId`、`categoryId`、`sceneId`、`platformId`、`uploadedAt`
|
||||
- `User`表:`email`、`username`、`role`
|
||||
- `Department`表:`parentId`
|
||||
|
||||
### 2. 全局错误处理机制
|
||||
|
||||
**实施步骤**:
|
||||
1. 在前端实现全局错误边界
|
||||
2. 在后端实现统一的API错误处理中间件
|
||||
3. 实现错误的分类和分级
|
||||
4. 实现错误的通知机制
|
||||
|
||||
**关键实现**:
|
||||
```typescript
|
||||
// 前端错误边界示例
|
||||
'use client'
|
||||
|
||||
import { ErrorBoundary } from 'react-error-boundary'
|
||||
|
||||
function ErrorFallback({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }) {
|
||||
return (
|
||||
<div className="error-container">
|
||||
<h2>出现错误</h2>
|
||||
<p>{error.message}</p>
|
||||
<button onClick={resetErrorBoundary}>重试</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 后端统一错误处理示例
|
||||
export function errorHandler(err: any, req: Request, res: Response, next: NextFunction) {
|
||||
console.error('Error:', err)
|
||||
|
||||
const status = err.status || 500
|
||||
const message = err.message || 'Internal Server Error'
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: message,
|
||||
timestamp: new Date().toISOString()
|
||||
}, { status })
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 结构化日志系统
|
||||
|
||||
**实施步骤**:
|
||||
1. 选择合适的日志库(如winston、pino)
|
||||
2. 实现结构化日志格式
|
||||
3. 配置日志级别和输出方式
|
||||
4. 实现日志的持久化存储
|
||||
5. 添加日志分析和监控
|
||||
|
||||
**关键实现**:
|
||||
```typescript
|
||||
import winston from 'winston'
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.json()
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
|
||||
new winston.transports.File({ filename: 'logs/combined.log' })
|
||||
]
|
||||
})
|
||||
|
||||
export default logger
|
||||
```
|
||||
|
||||
### 4. 图片懒加载
|
||||
|
||||
**实施步骤**:
|
||||
1. 为所有图片组件添加懒加载功能
|
||||
2. 实现图片的渐进式加载
|
||||
3. 添加图片加载状态的处理
|
||||
4. 优化图片的加载顺序
|
||||
|
||||
**关键实现**:
|
||||
```typescript
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function LazyImage({ src, alt, width, height }: { src: string; alt: string; width: number; height: number }) {
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true)
|
||||
observer.disconnect()
|
||||
}
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
)
|
||||
|
||||
if (ref.current) {
|
||||
observer.observe(ref.current)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (ref.current) {
|
||||
observer.unobserve(ref.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div ref={ref} className="relative">
|
||||
{isVisible ? (
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
unoptimized
|
||||
className="transition-opacity duration-300"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full bg-slate-200 animate-pulse" style={{ width, height }} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Docker镜像优化
|
||||
|
||||
**实施步骤**:
|
||||
1. 优化Dockerfile,使用更小的基础镜像
|
||||
2. 进一步清理不必要的依赖和文件
|
||||
3. 优化Docker构建缓存
|
||||
4. 实现并行构建
|
||||
|
||||
**优化后的Dockerfile示例**:
|
||||
```dockerfile
|
||||
# 构建阶段
|
||||
FROM node:18-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# 配置国内镜像源
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
RUN npm config set registry https://registry.npmmirror.com/
|
||||
|
||||
# 安装依赖
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --production --no-audit --prefer-offline
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建应用
|
||||
RUN npm run build
|
||||
|
||||
# 清理不必要的文件
|
||||
RUN rm -rf node_modules/.cache
|
||||
RUN rm -rf src
|
||||
RUN rm -rf .git
|
||||
|
||||
# 运行阶段
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# 配置国内镜像源
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
|
||||
# 复制构建产物
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 3000
|
||||
|
||||
# 启动应用
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
## 📈 预期优化效果
|
||||
|
||||
| 指标 | 当前状态 | 预期优化后 |
|
||||
|------|----------|------------|
|
||||
| **页面加载时间** | 2-3秒 | < 1秒 |
|
||||
| **API响应时间** | 200-500ms | < 100ms |
|
||||
| **数据库查询时间** | 100-300ms | < 50ms |
|
||||
| **Docker镜像大小** | ~500MB | < 200MB |
|
||||
| **错误率** | < 0.1% | < 0.01% |
|
||||
| **正常运行时间** | > 99.9% | > 99.99% |
|
||||
|
||||
## 📋 优化实施计划
|
||||
|
||||
### 第1周:优先级1优化
|
||||
- 完成数据库索引优化
|
||||
- 实现全局错误处理机制
|
||||
- 实现结构化日志系统
|
||||
- 实现图片懒加载
|
||||
- 完成国内镜像源全面检查
|
||||
|
||||
### 第2-3周:优先级2优化
|
||||
- 完成代码分割优化
|
||||
- 完成Docker镜像优化
|
||||
- 实现缓存策略优化
|
||||
- 提高测试覆盖率
|
||||
- 完善API文档
|
||||
|
||||
### 第4-6周:优先级3优化
|
||||
- 优化模块化设计
|
||||
- 优化状态管理
|
||||
- 实现虚拟滚动
|
||||
- 考虑CDN加速方案
|
||||
- 实现自动化测试
|
||||
|
||||
## 🔍 监控与评估
|
||||
|
||||
### 监控指标
|
||||
|
||||
1. **性能指标**:
|
||||
- 页面加载时间
|
||||
- API响应时间
|
||||
- 数据库查询时间
|
||||
- 内存使用率
|
||||
- CPU使用率
|
||||
|
||||
2. **稳定性指标**:
|
||||
- 错误率
|
||||
- 崩溃次数
|
||||
- 正常运行时间
|
||||
- 恢复时间
|
||||
|
||||
3. **部署指标**:
|
||||
- 镜像构建时间
|
||||
- 镜像大小
|
||||
- 部署时间
|
||||
- 部署成功率
|
||||
|
||||
### 评估方法
|
||||
|
||||
1. **基准测试**:在优化前进行全面的基准测试,记录各项指标
|
||||
2. **阶段性评估**:在每个优化阶段完成后,进行评估,对比优化效果
|
||||
3. **长期监控**:在优化完成后,进行长期监控,确保优化效果的持续性
|
||||
4. **用户反馈**:收集用户反馈,评估优化对用户体验的影响
|
||||
|
||||
## 🎯 结论
|
||||
|
||||
通过以上优化措施,我们可以显著提高项目的性能、稳定性、部署友好性和可维护性,更好地实现项目的核心目标:让其他公司能够一键部署或一条命令从Docker部署到自己的NAS或服务器上。
|
||||
|
||||
优化是一个持续的过程,我们需要定期评估和调整优化策略,确保项目始终保持最佳状态。同时,我们需要关注用户反馈,根据用户需求不断优化和改进项目。
|
||||
|
||||
让我们共同努力,将FLNXI素材库平台打造成一个高性能、高稳定性、易于部署和维护的优秀项目!
|
||||
@ -1,137 +0,0 @@
|
||||
# FLNXI素材库项目说明
|
||||
|
||||
本说明整理了当前项目的关键架构、模块划分、术语约定及日常操作规范,便于在需求沟通或交接时快速对齐背景。可直接复制内容给其他 AI 翻译或润色。
|
||||
|
||||
---
|
||||
|
||||
## 一、整体概览
|
||||
- **框架**:Next.js 14(App Router)+ Prisma + PostgreSQL + S3/MinIO 对象存储
|
||||
- **核心能力**:素材上传、模板管理、分类管理、筛选、权限控制、下载/流媒体播放
|
||||
- **部署**:推荐脚本 `scripts/deploy/deploy-new-fixed.ps1`(本地构建 → 镜像推送 Registry → 远程 docker compose 滚动更新)
|
||||
- **存储策略**:所有素材文件只走 S3/MinIO(`lib/s3.ts`),本地磁盘仅保留兼容读取
|
||||
|
||||
---
|
||||
|
||||
## 二、架构拆解
|
||||
1. **前端层(Next.js App Router)**
|
||||
- UI 组件集中在 `app/admin/_components`、`components` 目录,采用 Client + Server 组件混合模式
|
||||
- 状态管理以 React hooks 为主,配合 `useThemeStyles`、`DialogProvider` 等自定义 hooks
|
||||
2. **服务层(API Routes)**
|
||||
- 位于 `app/api/*`,每个目录一个 Route Handler,依赖 `@/lib/prisma`、`@/lib/s3`、`@/lib/field-pool`
|
||||
- 所有需要权限的接口都通过 `getServerSession(authOptions)` 校验
|
||||
3. **数据层(Prisma + PostgreSQL)**
|
||||
- Prisma schema 管理所有表结构;通过 `prisma/migrations/*` 记录历史
|
||||
- 字段池相关表:`FieldDefinition`、`FieldOption`
|
||||
4. **存储层(S3/MinIO)**
|
||||
- `lib/s3.ts` 封装上传、删除、预签名下载
|
||||
- Docker Compose 中默认启动 MinIO + 初始化服务,确保环境一致
|
||||
5. **部署 & 运维**
|
||||
- PowerShell 脚本封装构建、推送、远程部署、健康检查
|
||||
- Registry + 服务器 Compose 目录 `/root/deploy` 统一管理容器
|
||||
|
||||
---
|
||||
|
||||
## 二、角色与权限
|
||||
- 用户划分为普通用户、管理员、超级管理员
|
||||
- 管理端入口 `/admin`,包含:仪表盘、素材库、模板管理、分类管理、系统设置等模块
|
||||
- 所有管理 API 通过 `next-auth` session 鉴权,管理员及以上才可操作分类管理
|
||||
|
||||
---
|
||||
|
||||
## 三、字段池与分类管理
|
||||
### 核心概念
|
||||
- **字段池**:`FieldDefinition` 与 `FieldOption` 两张表,集中维护各模板字段及其选项
|
||||
- **字段同步**:模板字段勾选“添加到分类”后写入字段池;字段定义保持不删,只清理字段对应的数据记录
|
||||
- **启用/禁用**:`isFilterEnabled` 控制字段是否出现在分类列表及首页筛选
|
||||
|
||||
### 关键逻辑
|
||||
- `syncFieldPoolFromTemplates()`(`lib/field-pool.ts`)在 `GET /api/template-categories` 前执行,确保字段池与模板同步
|
||||
- 选项清洗:`stripRedundantBrackets`、`normalizeFieldOptionValue`、`expandOptionValue` 消除 `[[[xxx]]]`、`[a,b]` 类脏数据;`formatFieldOptionValues` 去重
|
||||
- 前端 `CategoriesTab.tsx` 用 `sanitizeOptionLabel` 再次兜底,保证展示干净
|
||||
|
||||
---
|
||||
|
||||
## 四、分类管理前端交互(`app/admin/_components/CategoriesTab.tsx`)
|
||||
### 左侧“分类列表”卡片
|
||||
- 编辑模式启用 `@dnd-kit/core` + `SortableContext`,允许拖拽调整顺序;顺序写入 `filterOrder`,同步首页筛选
|
||||
- 卡片包含:拖拽手柄(方形 8x8)、标题、字段信息、重命名按钮(方形图标按钮,调用全局 Prompt)、禁用按钮
|
||||
- 重命名只修改显示名(`filterDisplayName`),字段 `key` 不变
|
||||
|
||||
### 右侧“选项卡片”区域
|
||||
- 展示字段选项,并支持新增/重命名/删除
|
||||
- 选项重命名同样使用全局 Prompt 弹窗
|
||||
- “添加新选项”卡片输入后写回字段池数据
|
||||
|
||||
### 顶部操作
|
||||
- “编辑/保存/取消/刷新”按钮:
|
||||
- 保存 → `PUT /api/category-fields` 批量写入 `filterOrder`、`filterDisplayName`
|
||||
- 取消 → 退出编辑并重新拉取数据
|
||||
- 刷新 → 在非编辑状态下重新获取分类与字段
|
||||
|
||||
---
|
||||
|
||||
## 五、API 速览
|
||||
| 路径 | 方法 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `/api/template-categories` | GET | 拉取已启用分类字段,返回 `fieldKey` / 显示名 / 默认名 / 顺序 / 选项 |
|
||||
| `/api/category-fields` | PUT | 批量更新分类字段顺序与显示名(管理员权限) |
|
||||
| `/api/materials/upload` | POST | 上传素材;文件直接写入 S3,记录 URL |
|
||||
| `/api/materials/download` | GET | 下载素材;S3 文件走 `createPresignedDownloadUrl` 302 跳转 |
|
||||
| `/api/materials/stream` | GET | 视频流式播放,同样用预签名 URL |
|
||||
| `/api/admin/cleanup-database` | POST | 清理数据库记录(不再删除本地文件) |
|
||||
|
||||
---
|
||||
|
||||
## 六、上传/下载链路要点
|
||||
- 上传接口接收表单(素材文件/缩略图/字段 JSON),生成唯一文件名后上传 S3
|
||||
- 下载与流媒体接口检测 `zipFilePath` 是否为 S3 URL:
|
||||
- 是 → 创建预签名链接并 302
|
||||
- 否 → 走兼容逻辑读取本地旧文件
|
||||
- `cleanup-database` 操作只处理数据库,已移除本地文件删除代码
|
||||
|
||||
---
|
||||
|
||||
## 七、部署与运维
|
||||
- **首选脚本**:`deploy-new-fixed.ps1`(Windows PowerShell),使用私有 Registry
|
||||
- **Compose**:`docker-compose.standard.yml` 包含 `postgres`、`minio`、`minio-init`、`app`;MinIO 初始化自动建桶并开放下载
|
||||
- **健康检查**:部署脚本最终轮询 `/api/health`,确保上线成功
|
||||
- **辅助脚本**:`scripts/deploy` 目录下 `check-*`、`fix-*`、`setup-*`、`manual-import-image.ps1` 等,用于诊断、离线包、镜像导入
|
||||
|
||||
---
|
||||
|
||||
## 八、统一术语
|
||||
- “分类列表”:左侧字段列表(可排序、重命名、禁用/启用)
|
||||
- “小卡片”:右侧具体选项卡片
|
||||
- “字段池”:`FieldDefinition`/`FieldOption` 数据体系
|
||||
- “模板”:上传模板(定义字段的来源)
|
||||
- “启用/禁用”:是否参与分类列表与首页筛选
|
||||
- “Prompt 弹窗”:`DialogProvider` 提供的统一输入弹窗
|
||||
|
||||
---
|
||||
|
||||
## 九、统一 UI 与风格规范
|
||||
- **主题系统**:依赖 `useThemeStyles`、`useTheme` 输出 `styles` 对象,集中管理按钮、输入框、卡片、文本颜色等
|
||||
- **布局**
|
||||
- 管理端页面通用:左侧固定 Sidebar(240px+),右侧内容区滚动
|
||||
- 分类管理左侧列表为滚动容器,右侧内容随页面滚动
|
||||
- **按钮 & 动画**
|
||||
- 使用 `cardAnimations`、`buttonAnimations` 定义 hover/active/入场效果
|
||||
- 可交互部件默认有最小点击区域(>=32px),方形图标按钮保持 8x8(tailwind 单位)
|
||||
- **弹窗**
|
||||
- 全部走 `DialogProvider`(confirm/alert/prompt),禁止单独实现模态框
|
||||
- 提示文案统一使用简体中文,按钮文案保持“确定/取消/保存/禁用”等常用词
|
||||
- **拖拽**
|
||||
- 基于 `@dnd-kit`,拖动元素配“九宫格”图标作为手柄,拖拽过程加 `ring-indigo` 高亮
|
||||
- **表单输入**
|
||||
- 优先使用 `styles.inputStyles.base`,禁用手动 inline 样式,保持焦点态一致
|
||||
- Placeholder 以描述性文案为主,例如“请输入显示名称”
|
||||
- **空状态**
|
||||
- 使用 `text-xs text-slate-500` 描述,无数据时文案统一采用“暂无 XXX”格式
|
||||
- **命名**
|
||||
- 组件/变量遵循英文语义命名;对用户展示的字段使用中文
|
||||
|
||||
---
|
||||
|
||||
如需追加更细的 API 参数、数据库字段说明或交互流程,请在此文档基础上补充。***
|
||||
|
||||
|
||||
@ -1,438 +0,0 @@
|
||||
# 模板系统优化 - 部署说明
|
||||
|
||||
## 概述
|
||||
|
||||
本文档说明如何将模板系统优化部署到生产环境。部署使用Harbor Registry和现有的 `deploy-new-fixed.ps1` 脚本。
|
||||
|
||||
## 部署架构
|
||||
|
||||
```
|
||||
本地开发环境
|
||||
↓ (构建镜像)
|
||||
↓
|
||||
Harbor Registry (harbor.flnxi.com)
|
||||
↓ (拉取镜像)
|
||||
↓
|
||||
生产服务器 (45.249.208.118)
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 本地部署(一键)
|
||||
|
||||
```powershell
|
||||
# 在项目根目录执行
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
```
|
||||
|
||||
这个命令会自动:
|
||||
- ✓ 运行代码检查
|
||||
- ✓ 构建Docker镜像
|
||||
- ✓ 推送到Harbor
|
||||
- ✓ SSH到服务器部署
|
||||
|
||||
### 2. 服务器数据迁移
|
||||
|
||||
```bash
|
||||
# SSH连接
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 执行迁移
|
||||
cd /root/deploy
|
||||
docker exec flnxi-material npx prisma migrate deploy
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
```
|
||||
|
||||
### 3. 验证
|
||||
|
||||
访问: http://45.249.208.118:4320/admin/templates
|
||||
|
||||
## 详细步骤
|
||||
|
||||
### 步骤 1: 部署前准备
|
||||
|
||||
#### 1.1 检查代码状态
|
||||
|
||||
```powershell
|
||||
# 运行测试
|
||||
npm test
|
||||
|
||||
# 代码检查
|
||||
npm run check
|
||||
|
||||
# 查看Git状态
|
||||
git status
|
||||
```
|
||||
|
||||
确认:
|
||||
- [ ] 所有测试通过(118个)
|
||||
- [ ] 代码已提交
|
||||
- [ ] 无未跟踪的重要文件
|
||||
|
||||
#### 1.2 备份数据库
|
||||
|
||||
```bash
|
||||
# SSH到服务器
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 创建备份目录
|
||||
mkdir -p /backup
|
||||
|
||||
# 备份数据库
|
||||
docker exec flnxi-postgres pg_dump -U postgres -d flnxi_material -F c -b -v -f /backup/backup_$(date +%Y%m%d_%H%M%S).dump
|
||||
|
||||
# 验证备份文件
|
||||
ls -lh /backup/
|
||||
```
|
||||
|
||||
### 步骤 2: 构建和推送
|
||||
|
||||
#### 2.1 使用部署脚本
|
||||
|
||||
```powershell
|
||||
# 基本部署
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
|
||||
# 使用SSH密钥
|
||||
.\deploy-new-fixed.ps1 `
|
||||
-Registry harbor `
|
||||
-KeyPath "C:\Users\YourUsername\.ssh\youyidian_prod_ed25519"
|
||||
|
||||
# 跳过代码检查(快速部署)
|
||||
.\deploy-new-fixed.ps1 -Registry harbor -SkipCheck
|
||||
|
||||
# 清理旧镜像
|
||||
.\deploy-new-fixed.ps1 -Registry harbor -CleanImages -KeepImageCount 5
|
||||
```
|
||||
|
||||
#### 2.2 脚本执行流程
|
||||
|
||||
1. **检查阶段** (可选)
|
||||
- 运行 `npm run check`
|
||||
- 验证代码质量
|
||||
|
||||
2. **构建阶段**
|
||||
- 准备 `.env.production`
|
||||
- 执行 `docker build`
|
||||
- 验证镜像
|
||||
|
||||
3. **推送阶段**
|
||||
- 登录Harbor
|
||||
- 推送镜像到 `harbor.flnxi.com/flnxi/flnxi-material:local-deploy`
|
||||
|
||||
4. **部署阶段**
|
||||
- SSH连接到服务器
|
||||
- 拉取镜像
|
||||
- 重启应用容器
|
||||
|
||||
### 步骤 3: 数据库迁移
|
||||
|
||||
#### 3.1 应用Prisma迁移
|
||||
|
||||
```bash
|
||||
# 在服务器上执行
|
||||
docker exec flnxi-material npx prisma migrate deploy
|
||||
```
|
||||
|
||||
这会应用以下迁移:
|
||||
- `20251201133500_add_material_kind_to_templates`
|
||||
- `20251202000000_add_material_kind_to_materials`
|
||||
- `20251202091500_add_material_ownership_fields`
|
||||
- `20251213073100_add_template_system_indexes`
|
||||
|
||||
#### 3.2 运行数据迁移脚本
|
||||
|
||||
```bash
|
||||
# 迁移旧数据
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
```
|
||||
|
||||
脚本会:
|
||||
1. 检查模板version字段
|
||||
2. 为没有templateId的素材分配默认模板
|
||||
3. 验证数据完整性
|
||||
4. 生成迁移报告
|
||||
|
||||
#### 3.3 验证数据
|
||||
|
||||
```bash
|
||||
# 验证数据完整性
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
```
|
||||
|
||||
验证内容:
|
||||
- 模板数据完整性
|
||||
- 素材templateId引用
|
||||
- 字段数据完整性
|
||||
- 字段池数据
|
||||
- 孤立数据检查
|
||||
|
||||
### 步骤 4: 验证部署
|
||||
|
||||
#### 4.1 健康检查
|
||||
|
||||
```bash
|
||||
# 检查应用状态
|
||||
curl http://45.249.208.118:4320/api/health
|
||||
|
||||
# 查看容器状态
|
||||
docker ps | grep flnxi-material
|
||||
|
||||
# 查看日志
|
||||
docker logs -f flnxi-material --tail 100
|
||||
```
|
||||
|
||||
#### 4.2 功能测试
|
||||
|
||||
访问以下页面进行测试:
|
||||
|
||||
1. **模板管理** (http://45.249.208.118:4320/admin/templates)
|
||||
- [ ] 页面正常加载
|
||||
- [ ] 可以查看模板列表
|
||||
- [ ] 可以创建新模板
|
||||
- [ ] 可以编辑模板字段
|
||||
- [ ] 字段拖拽排序正常
|
||||
|
||||
2. **素材上传** (http://45.249.208.118:4320/upload)
|
||||
- [ ] 可以选择模板
|
||||
- [ ] 自定义字段显示正确
|
||||
- [ ] 可以上传素材
|
||||
- [ ] 素材详情页正常
|
||||
|
||||
3. **首页筛选** (http://45.249.208.118:4320)
|
||||
- [ ] Tag按钮显示正常
|
||||
- [ ] 筛选功能正常
|
||||
- [ ] 筛选结果正确
|
||||
|
||||
#### 4.3 性能检查
|
||||
|
||||
```bash
|
||||
# 查看资源使用
|
||||
docker stats flnxi-material --no-stream
|
||||
|
||||
# 检查响应时间
|
||||
time curl -s http://45.249.208.118:4320/api/admin/upload-templates > /dev/null
|
||||
```
|
||||
|
||||
预期指标:
|
||||
- 模板列表加载 < 500ms
|
||||
- 素材上传验证 < 100ms
|
||||
- 内存使用 < 1GB
|
||||
- CPU使用 < 50%
|
||||
|
||||
### 步骤 5: 监控
|
||||
|
||||
#### 5.1 查看日志
|
||||
|
||||
```bash
|
||||
# 实时日志
|
||||
docker logs -f flnxi-material
|
||||
|
||||
# 最近100行
|
||||
docker logs flnxi-material --tail 100
|
||||
|
||||
# 错误日志
|
||||
docker logs flnxi-material 2>&1 | grep -i error
|
||||
|
||||
# 特定时间段
|
||||
docker logs flnxi-material --since 30m
|
||||
```
|
||||
|
||||
#### 5.2 监控指标
|
||||
|
||||
持续监控30分钟,关注:
|
||||
- 错误日志数量
|
||||
- 响应时间
|
||||
- 内存使用趋势
|
||||
- CPU使用率
|
||||
- 用户反馈
|
||||
|
||||
## 回滚方案
|
||||
|
||||
如果部署出现严重问题,按以下步骤回滚:
|
||||
|
||||
### 1. 立即回滚
|
||||
|
||||
```bash
|
||||
# SSH到服务器
|
||||
ssh root@45.249.208.118
|
||||
cd /root/deploy
|
||||
|
||||
# 停止当前版本
|
||||
docker-compose stop app
|
||||
|
||||
# 恢复数据库
|
||||
docker exec flnxi-postgres pg_restore -U postgres -d flnxi_material -c /backup/backup_YYYYMMDD_HHMMSS.dump
|
||||
|
||||
# 拉取旧版本镜像(假设之前的tag是 previous-tag)
|
||||
docker pull harbor.flnxi.com/flnxi/flnxi-material:previous-tag
|
||||
docker tag harbor.flnxi.com/flnxi/flnxi-material:previous-tag flnxi-material:local-deploy
|
||||
|
||||
# 重启应用
|
||||
docker-compose up -d app
|
||||
```
|
||||
|
||||
### 2. 验证回滚
|
||||
|
||||
```bash
|
||||
# 检查应用状态
|
||||
curl http://45.249.208.118:4320/api/health
|
||||
|
||||
# 查看日志
|
||||
docker logs flnxi-material --tail 50
|
||||
|
||||
# 验证功能
|
||||
# 访问关键页面确认正常
|
||||
```
|
||||
|
||||
### 3. 通知相关人员
|
||||
|
||||
- 通知技术团队回滚完成
|
||||
- 通知产品团队暂停新功能使用
|
||||
- 记录回滚原因和问题
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 部署脚本执行失败
|
||||
|
||||
**症状**: `deploy-new-fixed.ps1` 执行出错
|
||||
|
||||
**排查步骤**:
|
||||
1. 检查Docker Desktop是否运行
|
||||
2. 检查网络连接
|
||||
3. 查看错误信息
|
||||
4. 使用 `-Debug` 参数重新运行
|
||||
|
||||
**解决方案**:
|
||||
```powershell
|
||||
# 启动Docker Desktop
|
||||
# 然后重新运行
|
||||
.\deploy-new-fixed.ps1 -Registry harbor -Debug
|
||||
```
|
||||
|
||||
### Q2: Harbor推送失败
|
||||
|
||||
**症状**: 推送镜像到Harbor时失败
|
||||
|
||||
**排查步骤**:
|
||||
1. 检查Harbor服务是否正常
|
||||
2. 验证Harbor凭据
|
||||
3. 检查网络连接
|
||||
|
||||
**解决方案**:
|
||||
```powershell
|
||||
# 手动登录测试
|
||||
docker login harbor.flnxi.com -u admin -p Harbor12345
|
||||
|
||||
# 手动推送
|
||||
docker tag flnxi-material:local-deploy harbor.flnxi.com/flnxi/flnxi-material:local-deploy
|
||||
docker push harbor.flnxi.com/flnxi/flnxi-material:local-deploy
|
||||
```
|
||||
|
||||
### Q3: SSH连接失败
|
||||
|
||||
**症状**: 无法SSH到服务器
|
||||
|
||||
**排查步骤**:
|
||||
1. 检查SSH密钥路径
|
||||
2. 验证服务器地址
|
||||
3. 检查防火墙设置
|
||||
|
||||
**解决方案**:
|
||||
```powershell
|
||||
# 使用SSH密钥
|
||||
.\deploy-new-fixed.ps1 `
|
||||
-Registry harbor `
|
||||
-KeyPath "C:\Users\YourUsername\.ssh\youyidian_prod_ed25519"
|
||||
|
||||
# 或手动SSH测试
|
||||
ssh -i "C:\Users\YourUsername\.ssh\youyidian_prod_ed25519" root@45.249.208.118
|
||||
```
|
||||
|
||||
### Q4: 数据迁移失败
|
||||
|
||||
**症状**: `migrate-template-data.ts` 执行失败
|
||||
|
||||
**排查步骤**:
|
||||
1. 查看错误日志
|
||||
2. 检查数据库连接
|
||||
3. 验证数据完整性
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查数据库连接
|
||||
docker exec flnxi-material npx prisma db pull
|
||||
|
||||
# 查看详细错误
|
||||
docker exec flnxi-material npm run migrate:template-data 2>&1 | tee migration.log
|
||||
|
||||
# 手动修复数据后重新运行
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
```
|
||||
|
||||
### Q5: 应用启动失败
|
||||
|
||||
**症状**: 容器启动后立即退出
|
||||
|
||||
**排查步骤**:
|
||||
1. 查看容器日志
|
||||
2. 检查环境变量
|
||||
3. 验证数据库连接
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 查看详细日志
|
||||
docker logs flnxi-material
|
||||
|
||||
# 检查环境变量
|
||||
docker exec flnxi-material env | grep DATABASE_URL
|
||||
|
||||
# 重启容器
|
||||
docker-compose restart app
|
||||
```
|
||||
|
||||
## 部署检查清单
|
||||
|
||||
### 部署前
|
||||
|
||||
- [ ] 代码已提交到Git
|
||||
- [ ] 所有测试通过(118个)
|
||||
- [ ] 代码检查通过
|
||||
- [ ] 已创建数据库备份
|
||||
- [ ] 已通知相关人员
|
||||
- [ ] 已准备回滚方案
|
||||
|
||||
### 部署中
|
||||
|
||||
- [ ] 部署脚本执行成功
|
||||
- [ ] 镜像推送到Harbor成功
|
||||
- [ ] 服务器拉取镜像成功
|
||||
- [ ] 应用容器启动成功
|
||||
- [ ] 数据库迁移成功
|
||||
- [ ] 数据验证通过
|
||||
|
||||
### 部署后
|
||||
|
||||
- [ ] 健康检查通过
|
||||
- [ ] 功能测试通过
|
||||
- [ ] 性能指标正常
|
||||
- [ ] 无错误日志
|
||||
- [ ] 用户反馈正常
|
||||
- [ ] 监控30分钟无异常
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [快速部署指南](./quick-deployment-guide.md) - 简化的部署步骤
|
||||
- [部署前检查清单](./pre-deployment-checklist.md) - 详细的检查清单
|
||||
- [项目总结](../template-system-optimization-summary.md) - 完整的项目总结
|
||||
- [API文档](../api/template-system-api.md) - API接口文档
|
||||
- [用户手册](../user-guide/template-system-guide.md) - 用户使用指南
|
||||
- [脚本说明](../../scripts/README.md) - 数据迁移脚本说明
|
||||
|
||||
## 联系方式
|
||||
|
||||
如有问题,请联系:
|
||||
- **技术负责人**: [姓名] - [联系方式]
|
||||
- **运维负责人**: [姓名] - [联系方式]
|
||||
- **产品负责人**: [姓名] - [联系方式]
|
||||
@ -1,345 +0,0 @@
|
||||
# 模板系统优化 - 部署前检查清单
|
||||
|
||||
## 概述
|
||||
|
||||
本文档提供了部署模板系统优化到测试和生产环境的完整检查清单。
|
||||
|
||||
## 部署前准备
|
||||
|
||||
### 1. 代码检查
|
||||
|
||||
- [ ] 所有代码已提交到Git仓库
|
||||
- [ ] 所有单元测试通过(118个测试用例)
|
||||
- [ ] 代码已通过TypeScript类型检查
|
||||
- [ ] 代码已通过ESLint检查
|
||||
- [ ] 核心功能代码覆盖率达标(>80%)
|
||||
|
||||
```bash
|
||||
# 运行检查
|
||||
npm run check
|
||||
npm test
|
||||
```
|
||||
|
||||
### 2. 数据库准备
|
||||
|
||||
- [ ] 已创建数据库备份
|
||||
- [ ] 已检查数据库迁移文件
|
||||
- [ ] 已在本地测试数据库迁移
|
||||
- [ ] 已准备回滚脚本
|
||||
|
||||
```bash
|
||||
# 检查迁移文件
|
||||
ls prisma/migrations
|
||||
|
||||
# 验证schema
|
||||
npm run prisma:validate
|
||||
```
|
||||
|
||||
### 3. 数据迁移准备
|
||||
|
||||
- [ ] 已准备数据迁移脚本(migrate-template-data.ts)
|
||||
- [ ] 已准备数据验证脚本(verify-template-data.ts)
|
||||
- [ ] 已在测试数据上验证迁移脚本
|
||||
- [ ] 已准备迁移失败的回滚方案
|
||||
|
||||
```bash
|
||||
# 测试迁移脚本(在测试环境)
|
||||
npm run migrate:template-data
|
||||
|
||||
# 验证数据完整性
|
||||
npm run verify:template-data
|
||||
```
|
||||
|
||||
### 4. 文档检查
|
||||
|
||||
- [ ] API文档已更新(docs/api/template-system-api.md)
|
||||
- [ ] 用户手册已更新(docs/user-guide/template-system-guide.md)
|
||||
- [ ] 部署文档已准备
|
||||
- [ ] 回滚文档已准备
|
||||
|
||||
## 测试环境部署
|
||||
|
||||
### 步骤 1: 备份数据库
|
||||
|
||||
```bash
|
||||
# PostgreSQL备份示例
|
||||
pg_dump -h <host> -U <user> -d <database> -F c -b -v -f backup_$(date +%Y%m%d_%H%M%S).dump
|
||||
|
||||
# 或使用Prisma Studio导出关键数据
|
||||
npm run prisma:studio
|
||||
```
|
||||
|
||||
### 步骤 2: 运行数据库迁移
|
||||
|
||||
```bash
|
||||
# 生成Prisma客户端
|
||||
npm run prisma:generate
|
||||
|
||||
# 应用数据库迁移
|
||||
npx prisma migrate deploy
|
||||
|
||||
# 验证迁移结果
|
||||
npm run verify:template-data
|
||||
```
|
||||
|
||||
### 步骤 3: 运行数据迁移脚本
|
||||
|
||||
```bash
|
||||
# 迁移旧数据
|
||||
npm run migrate:template-data
|
||||
|
||||
# 再次验证数据完整性
|
||||
npm run verify:template-data
|
||||
```
|
||||
|
||||
### 步骤 4: 部署新代码
|
||||
|
||||
```bash
|
||||
# 构建应用
|
||||
npm run build
|
||||
|
||||
# 启动应用
|
||||
npm start
|
||||
|
||||
# 或使用Docker
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 步骤 5: 执行冒烟测试
|
||||
|
||||
#### 5.1 模板管理测试
|
||||
|
||||
- [ ] 访问模板管理页面(/admin/templates)
|
||||
- [ ] 创建新模板
|
||||
- [ ] 编辑模板字段
|
||||
- [ ] 测试字段拖拽排序
|
||||
- [ ] 测试select字段选项管理
|
||||
- [ ] 删除测试模板
|
||||
|
||||
#### 5.2 素材上传测试
|
||||
|
||||
- [ ] 访问素材上传页面
|
||||
- [ ] 选择模板
|
||||
- [ ] 填写自定义字段
|
||||
- [ ] 上传素材文件
|
||||
- [ ] 验证素材详情页显示
|
||||
|
||||
#### 5.3 字段池同步测试
|
||||
|
||||
- [ ] 创建带有select字段的模板
|
||||
- [ ] 验证字段池自动同步
|
||||
- [ ] 访问分类管理页面
|
||||
- [ ] 验证字段和选项显示正确
|
||||
|
||||
#### 5.4 首页筛选测试
|
||||
|
||||
- [ ] 访问首页
|
||||
- [ ] 验证Tag按钮显示
|
||||
- [ ] 测试单选筛选
|
||||
- [ ] 测试多选筛选
|
||||
- [ ] 验证筛选结果正确
|
||||
|
||||
#### 5.5 性能测试
|
||||
|
||||
- [ ] 模板列表加载时间 < 500ms
|
||||
- [ ] 素材上传验证时间 < 100ms
|
||||
- [ ] 字段池同步时间 < 1s
|
||||
- [ ] 首页Tag按钮响应时间 < 100ms
|
||||
|
||||
### 步骤 6: 监控和日志
|
||||
|
||||
```bash
|
||||
# 查看应用日志
|
||||
docker logs -f <container_name>
|
||||
|
||||
# 或使用PM2
|
||||
pm2 logs
|
||||
|
||||
# 监控数据库性能
|
||||
# 检查慢查询日志
|
||||
```
|
||||
|
||||
## 生产环境部署
|
||||
|
||||
### 部署前最终检查
|
||||
|
||||
- [ ] 测试环境已稳定运行至少24小时
|
||||
- [ ] 所有冒烟测试通过
|
||||
- [ ] 没有发现严重bug
|
||||
- [ ] 性能指标达标
|
||||
- [ ] 已通知相关人员部署计划
|
||||
- [ ] 已准备回滚方案
|
||||
|
||||
### 部署窗口
|
||||
|
||||
- **推荐时间**: 非业务高峰期(如凌晨2-6点)
|
||||
- **预计时长**: 30-60分钟
|
||||
- **回滚时间**: 15分钟
|
||||
|
||||
### 部署步骤
|
||||
|
||||
与测试环境部署步骤相同,但需要额外注意:
|
||||
|
||||
1. **数据库备份**: 必须创建完整备份
|
||||
2. **灰度发布**: 如果可能,先部署到部分服务器
|
||||
3. **监控**: 密切监控系统指标和错误日志
|
||||
4. **用户通知**: 如需停机,提前通知用户
|
||||
|
||||
### 部署后验证
|
||||
|
||||
- [ ] 所有冒烟测试通过
|
||||
- [ ] 数据完整性验证通过
|
||||
- [ ] 性能指标正常
|
||||
- [ ] 错误日志无异常
|
||||
- [ ] 用户反馈正常
|
||||
|
||||
## 回滚方案
|
||||
|
||||
### 何时回滚
|
||||
|
||||
- 发现严重bug影响核心功能
|
||||
- 性能严重下降
|
||||
- 数据完整性问题
|
||||
- 用户无法正常使用
|
||||
|
||||
### 回滚步骤
|
||||
|
||||
1. **停止应用**
|
||||
```bash
|
||||
docker-compose down
|
||||
# 或
|
||||
pm2 stop all
|
||||
```
|
||||
|
||||
2. **恢复数据库**
|
||||
```bash
|
||||
# PostgreSQL恢复示例
|
||||
pg_restore -h <host> -U <user> -d <database> -v backup_YYYYMMDD_HHMMSS.dump
|
||||
```
|
||||
|
||||
3. **部署旧版本代码**
|
||||
```bash
|
||||
git checkout <previous_version>
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
4. **验证回滚**
|
||||
- 检查应用是否正常运行
|
||||
- 验证数据完整性
|
||||
- 执行基本功能测试
|
||||
|
||||
## 监控指标
|
||||
|
||||
### 应用指标
|
||||
|
||||
- 响应时间
|
||||
- 错误率
|
||||
- 请求量
|
||||
- CPU使用率
|
||||
- 内存使用率
|
||||
|
||||
### 数据库指标
|
||||
|
||||
- 查询响应时间
|
||||
- 连接数
|
||||
- 慢查询数量
|
||||
- 锁等待时间
|
||||
|
||||
### 业务指标
|
||||
|
||||
- 模板创建数量
|
||||
- 素材上传数量
|
||||
- 字段池同步次数
|
||||
- 用户活跃度
|
||||
|
||||
## 常见问题处理
|
||||
|
||||
### 问题 1: 数据迁移失败
|
||||
|
||||
**症状**: migrate-template-data.ts 执行失败
|
||||
|
||||
**解决方案**:
|
||||
1. 检查错误日志
|
||||
2. 验证数据库连接
|
||||
3. 检查数据完整性
|
||||
4. 手动修复问题数据
|
||||
5. 重新运行迁移脚本
|
||||
|
||||
### 问题 2: 字段池同步失败
|
||||
|
||||
**症状**: 分类管理页面显示不正确
|
||||
|
||||
**解决方案**:
|
||||
1. 检查字段池同步日志
|
||||
2. 手动触发同步:调用 `/api/admin/sync-field-pool`
|
||||
3. 验证字段定义和选项
|
||||
|
||||
### 问题 3: 性能下降
|
||||
|
||||
**症状**: 页面加载缓慢
|
||||
|
||||
**解决方案**:
|
||||
1. 检查数据库索引是否正确创建
|
||||
2. 检查慢查询日志
|
||||
3. 优化查询语句
|
||||
4. 增加缓存
|
||||
|
||||
### 问题 4: 版本冲突
|
||||
|
||||
**症状**: 模板更新时出现409错误
|
||||
|
||||
**解决方案**:
|
||||
1. 这是正常的并发控制机制
|
||||
2. 提示用户刷新页面重新编辑
|
||||
3. 检查是否有多人同时编辑
|
||||
|
||||
## 联系人
|
||||
|
||||
- **技术负责人**: [姓名] - [联系方式]
|
||||
- **运维负责人**: [姓名] - [联系方式]
|
||||
- **产品负责人**: [姓名] - [联系方式]
|
||||
|
||||
## 附录
|
||||
|
||||
### A. 数据库迁移文件列表
|
||||
|
||||
```
|
||||
prisma/migrations/
|
||||
├── 20251201133500_add_material_kind_to_templates/
|
||||
├── 20251202000000_add_material_kind_to_materials/
|
||||
├── 20251202091500_add_material_ownership_fields/
|
||||
└── 20251213073100_add_template_system_indexes/
|
||||
```
|
||||
|
||||
### B. 关键API端点
|
||||
|
||||
- `GET /api/admin/upload-templates` - 获取模板列表
|
||||
- `POST /api/admin/upload-templates` - 创建模板
|
||||
- `GET /api/admin/upload-templates/[id]` - 获取模板详情
|
||||
- `PUT /api/admin/upload-templates/[id]` - 更新模板
|
||||
- `DELETE /api/admin/upload-templates/[id]` - 删除模板
|
||||
- `POST /api/admin/sync-field-pool` - 同步字段池
|
||||
|
||||
### C. 环境变量检查
|
||||
|
||||
```bash
|
||||
# 必需的环境变量
|
||||
DATABASE_URL=postgresql://...
|
||||
NEXTAUTH_SECRET=...
|
||||
NEXTAUTH_URL=...
|
||||
S3_ENDPOINT=...
|
||||
S3_ACCESS_KEY_ID=...
|
||||
S3_SECRET_ACCESS_KEY=...
|
||||
S3_BUCKET_NAME=...
|
||||
```
|
||||
|
||||
### D. 性能基准
|
||||
|
||||
| 操作 | 目标时间 | 可接受时间 |
|
||||
|------|---------|-----------|
|
||||
| 模板列表加载 | < 300ms | < 500ms |
|
||||
| 模板详情加载 | < 200ms | < 400ms |
|
||||
| 素材上传验证 | < 50ms | < 100ms |
|
||||
| 字段池同步 | < 500ms | < 1s |
|
||||
| 首页Tag响应 | < 50ms | < 100ms |
|
||||
@ -1,228 +0,0 @@
|
||||
# 模板系统优化 - 快速部署指南
|
||||
|
||||
## 使用Harbor部署(推荐)
|
||||
|
||||
本项目使用 `deploy-new-fixed.ps1` 脚本通过Harbor进行部署。
|
||||
|
||||
### 部署步骤
|
||||
|
||||
#### 1. 本地构建并推送到Harbor
|
||||
|
||||
```powershell
|
||||
# 使用Harbor部署(默认)
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
|
||||
# 或指定Harbor配置
|
||||
.\deploy-new-fixed.ps1 `
|
||||
-Registry harbor `
|
||||
-HarborUrl "harbor.flnxi.com" `
|
||||
-HarborProjectName "flnxi" `
|
||||
-HarborUsername "admin" `
|
||||
-HarborPassword "Harbor12345"
|
||||
```
|
||||
|
||||
**脚本会自动完成**:
|
||||
- ✓ 代码检查(npm run check)
|
||||
- ✓ Docker镜像构建
|
||||
- ✓ 推送到Harbor Registry
|
||||
- ✓ SSH连接到服务器
|
||||
- ✓ 拉取镜像并部署
|
||||
|
||||
#### 2. 服务器上执行数据库迁移
|
||||
|
||||
部署完成后,SSH连接到服务器执行数据库迁移:
|
||||
|
||||
```bash
|
||||
# SSH连接到服务器
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 进入部署目录
|
||||
cd /root/deploy
|
||||
|
||||
# 应用数据库迁移(Prisma)
|
||||
docker exec flnxi-material npx prisma migrate deploy
|
||||
|
||||
# 运行数据迁移脚本(迁移旧数据)
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
|
||||
# 验证数据完整性
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
```
|
||||
|
||||
#### 3. 验证部署
|
||||
|
||||
访问以下地址验证:
|
||||
- 应用首页: http://45.249.208.118:4320
|
||||
- 管理后台: http://45.249.208.118:4320/admin
|
||||
- 模板管理: http://45.249.208.118:4320/admin/templates
|
||||
|
||||
快速测试:
|
||||
- [ ] 模板管理页面正常
|
||||
- [ ] 创建新模板
|
||||
- [ ] 上传素材测试
|
||||
- [ ] 首页筛选功能正常
|
||||
|
||||
## 部署参数说明
|
||||
|
||||
### 常用参数
|
||||
|
||||
```powershell
|
||||
# 跳过代码检查(快速部署)
|
||||
.\deploy-new-fixed.ps1 -SkipCheck
|
||||
|
||||
# 只构建不部署(测试构建)
|
||||
.\deploy-new-fixed.ps1 -SkipUpload
|
||||
|
||||
# 清理旧镜像(节省空间)
|
||||
.\deploy-new-fixed.ps1 -CleanImages -KeepImageCount 5
|
||||
|
||||
# 使用SSH密钥
|
||||
.\deploy-new-fixed.ps1 -KeyPath "C:\Users\YourUsername\.ssh\youyidian_prod_ed25519"
|
||||
```
|
||||
|
||||
### 完整参数列表
|
||||
|
||||
```powershell
|
||||
-Server # 服务器地址(默认: 45.249.208.118)
|
||||
-Username # SSH用户名(默认: root)
|
||||
-KeyPath # SSH密钥路径
|
||||
-Registry # Registry类型: harbor 或 default(默认: harbor)
|
||||
-HarborUrl # Harbor地址(默认: harbor.flnxi.com)
|
||||
-HarborProjectName # Harbor项目名称(默认: flnxi)
|
||||
-HarborUsername # Harbor用户名(默认: admin)
|
||||
-HarborPassword # Harbor密码
|
||||
-SkipCheck # 跳过代码检查
|
||||
-SkipBuild # 跳过Docker构建
|
||||
-SkipUpload # 跳过推送和部署
|
||||
-CleanImages # 清理旧镜像
|
||||
-KeepImageCount # 保留镜像数量(默认: 5)
|
||||
-Debug # 调试模式
|
||||
-Help # 显示帮助
|
||||
```
|
||||
|
||||
## 生产环境部署注意事项
|
||||
|
||||
**推荐时间**: 非业务高峰期(凌晨2-6点)
|
||||
**预计时长**: 30-60分钟
|
||||
|
||||
### 部署前检查
|
||||
|
||||
- [ ] 代码已提交并推送到Git
|
||||
- [ ] 所有测试通过(118个单元测试)
|
||||
- [ ] 已在测试环境验证
|
||||
- [ ] 已创建数据库备份
|
||||
- [ ] 已通知相关人员
|
||||
|
||||
### 数据库备份
|
||||
|
||||
```bash
|
||||
# SSH到服务器
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 备份数据库
|
||||
docker exec flnxi-postgres pg_dump -U postgres -d flnxi_material -F c -b -v -f /backup/backup_$(date +%Y%m%d_%H%M%S).dump
|
||||
|
||||
# 或使用外部备份
|
||||
pg_dump -h 45.249.208.118 -U postgres -d flnxi_material -F c -f backup_$(date +%Y%m%d_%H%M%S).dump
|
||||
```
|
||||
|
||||
### 部署流程
|
||||
|
||||
1. **本地构建推送**
|
||||
```powershell
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
```
|
||||
|
||||
2. **服务器数据迁移**
|
||||
```bash
|
||||
ssh root@45.249.208.118
|
||||
cd /root/deploy
|
||||
docker exec flnxi-material npx prisma migrate deploy
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
```
|
||||
|
||||
3. **验证部署**
|
||||
- 检查应用是否正常运行
|
||||
- 执行冒烟测试
|
||||
- 查看日志确认无错误
|
||||
|
||||
4. **监控观察**
|
||||
- 持续监控30分钟
|
||||
- 关注错误日志
|
||||
- 收集用户反馈
|
||||
|
||||
## 回滚方案
|
||||
|
||||
如果部署出现问题,按以下步骤回滚:
|
||||
|
||||
### 1. 停止当前版本
|
||||
|
||||
```bash
|
||||
ssh root@45.249.208.118
|
||||
cd /root/deploy
|
||||
docker-compose stop app
|
||||
```
|
||||
|
||||
### 2. 恢复数据库
|
||||
|
||||
```bash
|
||||
# 恢复数据库备份
|
||||
docker exec flnxi-postgres pg_restore -U postgres -d flnxi_material -c /backup/backup_YYYYMMDD_HHMMSS.dump
|
||||
```
|
||||
|
||||
### 3. 部署旧版本
|
||||
|
||||
```bash
|
||||
# 拉取旧版本镜像
|
||||
docker pull harbor.flnxi.com/flnxi/flnxi-material:previous-tag
|
||||
|
||||
# 标记为当前版本
|
||||
docker tag harbor.flnxi.com/flnxi/flnxi-material:previous-tag flnxi-material:local-deploy
|
||||
|
||||
# 重启应用
|
||||
docker-compose up -d app
|
||||
```
|
||||
|
||||
### 4. 验证回滚
|
||||
|
||||
- 检查应用是否正常
|
||||
- 验证数据完整性
|
||||
- 确认功能正常
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: Docker构建失败?
|
||||
**A**: 检查 `.env.production` 文件是否存在,或使用 `-EnvFile` 参数指定环境变量文件。
|
||||
|
||||
### Q: SSH连接失败?
|
||||
**A**: 使用 `-KeyPath` 参数指定SSH密钥路径,或检查SSH密钥权限。
|
||||
|
||||
### Q: Harbor推送失败?
|
||||
**A**: 检查Harbor凭据是否正确,确认Harbor服务是否正常运行。
|
||||
|
||||
### Q: 数据迁移失败?
|
||||
**A**: 查看错误日志,检查数据库连接,必要时手动修复数据后重新运行。
|
||||
|
||||
### Q: 健康检查超时?
|
||||
**A**: 应用可能需要更长时间启动,手动访问 http://45.249.208.118:4320/api/health 确认。
|
||||
|
||||
## 查看日志
|
||||
|
||||
```bash
|
||||
# 应用日志
|
||||
docker logs -f flnxi-material
|
||||
|
||||
# 数据库日志
|
||||
docker logs -f flnxi-postgres
|
||||
|
||||
# 实时监控
|
||||
docker stats flnxi-material
|
||||
```
|
||||
|
||||
## 需要帮助?
|
||||
|
||||
- 完整检查清单: `docs/deployment/pre-deployment-checklist.md`
|
||||
- 项目总结: `docs/template-system-optimization-summary.md`
|
||||
- API文档: `docs/api/template-system-api.md`
|
||||
- 用户手册: `docs/user-guide/template-system-guide.md`
|
||||
154
docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md
Normal file
154
docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md
Normal file
@ -0,0 +1,154 @@
|
||||
# 多品牌 NAS 与 Linux 交付差异清单
|
||||
|
||||
## 1. 这份清单解决什么问题
|
||||
|
||||
这套系统的目标环境不是单一机器,也不是单一品牌的 `NAS(网络存储)`。
|
||||
|
||||
后续真实交付时,可能遇到:
|
||||
|
||||
- `fnOS(飞牛系统)`
|
||||
- `DSM(群晖系统)`
|
||||
- 其他品牌 `NAS(网络存储)`
|
||||
- 通用 `Linux(服务器系统)`
|
||||
|
||||
这些环境虽然都可能支持 `Docker(容器)`,但不代表它们的:
|
||||
|
||||
- 路径规则一样
|
||||
- 权限模型一样
|
||||
- 端口管理一样
|
||||
- 反向代理方式一样
|
||||
- 日志和自启动方式一样
|
||||
- `CPU(处理器)` 架构一样
|
||||
|
||||
所以,任何“在某一台测试机上跑通”的做法,都不能直接当成客户交付标准。
|
||||
|
||||
一句话结论:
|
||||
|
||||
`FLNXI 素材库` 的交付基线应该是“可参数化的容器化方案”,而不是“绑定某一台机器习惯”的方案。
|
||||
|
||||
## 2. 当前项目的固定基线
|
||||
|
||||
不管客户机器是什么品牌,后续都应尽量坚持这些不变项:
|
||||
|
||||
- 最终使用者只通过浏览器访问系统,不承担部署动作。
|
||||
- 主交付单元优先是 `docker compose(容器编排) + .env(环境变量文件)`。
|
||||
- 环境差异优先收敛到 `.env`、挂载目录、端口、反向代理,而不是改业务代码。
|
||||
- 许可证链路、初始化链路、备份恢复链路必须可重复执行,不能依赖一次性手工操作。
|
||||
- 共享机器部署时,容器名、卷名、网络名必须项目级隔离。
|
||||
|
||||
## 3. 不同机器最容易出问题的地方
|
||||
|
||||
| 差异项 | 常见差异 | 对本项目的含义 | 交付前必须确认 |
|
||||
| --- | --- | --- | --- |
|
||||
| `CPU(处理器)` 架构 | `x86_64`、`arm64`、旧型号低性能 CPU | 镜像、依赖、转码能力可能不兼容 | 目标机器是 `x86_64` 还是 `arm64`;主镜像和依赖镜像是否支持 |
|
||||
| 容器入口 | 有的机器支持 `docker compose(容器编排)`,有的只给图形界面 | 交付步骤不能默认客户会命令行 | 目标机器是否支持 `docker compose v2`;若不支持,是否已有可接受的替代入口 |
|
||||
| 宿主机目录 | `Linux(服务器系统)` 常见 `/opt/...`、`/srv/...`,`NAS(网络存储)` 常见品牌自定义挂载点 | 数据卷路径不能写死 | 客户最终要把数据放到哪个宿主机目录 |
|
||||
| 权限模型 | 有的机器是 `root(管理员)` 直登,有的只有图形界面管理员 | 挂载目录、上传目录、备份目录权限会不同 | 是否可 SSH;是否可创建目录;是否允许容器写入挂载目录 |
|
||||
| 端口管理 | 有的机器已有 80/443/9000/9001 等服务占用 | 默认端口不一定能直接用 | 客户可用端口范围;是否已有反向代理占用 |
|
||||
| 反向代理 / TLS(证书) | 有的机器有内置反代,有的要自己配 `Nginx(反向代理)` | 域名、HTTPS、回调地址不能写死 | 是否使用域名;证书由谁托管;最终外部访问地址是什么 |
|
||||
| 网络出站 | 有的内网机器不能直接访问镜像仓库或授权服务 | 镜像拉取、许可证验证可能失败 | 是否能访问 `Registry(镜像仓库)`、`license-server(许可证服务)`、必要的外部 DNS |
|
||||
| 文件系统性能 | `NAS(网络存储)` 上机械盘、阵列、快照策略差异大 | 上传、预览、转码体验差异明显 | 数据盘类型;是否是 SSD;是否允许视频转码落盘 |
|
||||
| 日志与自启动 | 有的机器靠 `systemd(系统服务)`,有的靠品牌图形界面 | 运维说明不能只写一种方式 | 客户如何看日志、重启服务、设置开机自启 |
|
||||
| 共享宿主机 | 测试机 / 客户机可能不只跑这一个项目 | 容器名、卷名、网络名、端口必须隔离 | 是否独占机器;若不是,现有项目占用了什么 |
|
||||
|
||||
## 4. 商业化交付时的机器分级
|
||||
|
||||
建议交付前先把客户机器分成 3 类,不要混着处理。
|
||||
|
||||
### A 类:标准 Linux 服务器
|
||||
|
||||
特征:
|
||||
|
||||
- 可 SSH
|
||||
- 可执行 `docker compose(容器编排)`
|
||||
- 可自由创建宿主机目录
|
||||
- 端口和反向代理由实施方控制
|
||||
|
||||
处理建议:
|
||||
|
||||
- 这是最适合当前项目标准交付链路的环境。
|
||||
- 优先走通用 `compose + .env` 方案。
|
||||
|
||||
### B 类:可 SSH 的 NAS
|
||||
|
||||
特征:
|
||||
|
||||
- 品牌系统上有图形化容器管理
|
||||
- 同时保留 SSH 或命令行能力
|
||||
- 路径、权限、反向代理存在品牌差异
|
||||
|
||||
处理建议:
|
||||
|
||||
- 这是当前项目最需要兼容的主场景。
|
||||
- 仍优先使用通用容器方案,但要提前适配:
|
||||
- 挂载路径
|
||||
- 端口
|
||||
- 反代入口
|
||||
- 目录权限
|
||||
|
||||
### C 类:强品牌绑定、命令行能力弱的环境
|
||||
|
||||
特征:
|
||||
|
||||
- 只能主要依赖图形界面
|
||||
- `compose(容器编排)` 支持弱或不稳定
|
||||
- 权限、网络、目录能力受限较多
|
||||
|
||||
处理建议:
|
||||
|
||||
- 不要先承诺“按标准脚本一键部署”。
|
||||
- 先做品牌专项验证,再决定是否提供该品牌专用安装说明。
|
||||
|
||||
## 5. 客户机器交付前必须采集的信息
|
||||
|
||||
每次客户部署前,至少要收集下面这份信息,不要跳过。
|
||||
|
||||
| 项目 | 要确认的内容 |
|
||||
| --- | --- |
|
||||
| 品牌与型号 | 机器品牌、型号、系统名称、系统版本 |
|
||||
| `CPU(处理器)` | 架构、核心数、是否支持当前镜像 |
|
||||
| 内存 | 总内存、可用内存 |
|
||||
| 磁盘 | 可用空间、是否 SSD、数据盘路径 |
|
||||
| 容器能力 | `Docker(容器)` 版本、`docker compose(容器编排)` 是否可用 |
|
||||
| SSH 能力 | 是否可 SSH;是否有管理员权限 |
|
||||
| 端口 | 可用端口、已占用端口、是否已有反向代理 |
|
||||
| 域名与证书 | 是否有正式域名;HTTPS 是否由客户现有网关处理 |
|
||||
| 网络出站 | 能否访问镜像仓库、许可证服务、DNS |
|
||||
| 共享情况 | 是否独占机器;若共享,已有项目名单和端口名单 |
|
||||
| 备份 | 备份目录、备份责任人、恢复演练方式 |
|
||||
| 视频能力 | 是否启用转码;是否具备 `ffmpeg(视频处理工具)` 和足够磁盘 |
|
||||
|
||||
## 6. 这套项目目前必须坚持的交付约束
|
||||
|
||||
后续如果继续做商业化交付,下面这些约束不能松:
|
||||
|
||||
- 不能把某一台测试机的固定 IP、固定目录、固定端口当成客户默认值。
|
||||
- 不能把某个品牌 `NAS(网络存储)` 的宿主机路径写死到通用文档。
|
||||
- 不能把共享测试机的容器名、卷名、网络名直接搬到客户环境。
|
||||
- 不能默认所有客户机器都能直接访问 `Registry(镜像仓库)` 或许可证服务。
|
||||
- 不能默认所有客户机器都有高性能 CPU 或 SSD。
|
||||
- 不能把“内部测试机能跑通”当成“客户环境已可交付”。
|
||||
|
||||
## 7. 当前项目的实际落地建议
|
||||
|
||||
结合当前仓库现状,后续建议这样执行:
|
||||
|
||||
1. 通用交付主线只保留一套:`docker compose(容器编排) + .env(环境变量文件)`。
|
||||
2. 品牌差异只落在部署前检查、目录映射、端口、反代和权限上。
|
||||
3. 如果某品牌确实需要特殊步骤,再单独补“品牌补充说明”,不要污染主部署文档。
|
||||
4. 共享测试机只用于内部验证,不代表客户标准机型。
|
||||
5. 每次客户交付前,先填完本清单第 5 节,再开始部署。
|
||||
|
||||
## 8. 目前哪些内容不能直接当客户模板
|
||||
|
||||
当前仓库中仍有一些“固定服务器 / 固定环境”的历史部署写法。
|
||||
|
||||
这些内容可以作为内部参考,但不能直接视为客户默认模板:
|
||||
|
||||
- 固定公网服务器地址
|
||||
- 固定测试机地址
|
||||
- 固定镜像仓库凭据
|
||||
- 固定端口直接复用
|
||||
- 固定容器名直接复用
|
||||
|
||||
后续若发现这类信息仍在主文档里,应继续收口成“示例”或“内部历史记录”,不要继续当标准交付口径。
|
||||
@ -1,105 +0,0 @@
|
||||
# 当前部署状态
|
||||
|
||||
## 📊 部署概览
|
||||
|
||||
**部署时间**: 2024-12-13
|
||||
**服务器**: 45.249.208.118:4320
|
||||
**容器名称**: youyidian-3d-material
|
||||
**镜像**: harbor.flnxi.com/flnxi/flnxi-material:local-deploy
|
||||
|
||||
## ✅ 已完成
|
||||
|
||||
- [x] Docker 镜像构建成功
|
||||
- [x] 推送到 Harbor Registry
|
||||
- [x] 部署到服务器
|
||||
- [x] 容器启动成功
|
||||
- [x] 健康检查通过
|
||||
- [x] 迁移脚本 TypeScript 错误修复
|
||||
|
||||
## ⏳ 待完成
|
||||
|
||||
- [ ] 执行数据库迁移
|
||||
- [ ] 运行数据迁移脚本
|
||||
- [ ] 验证数据完整性
|
||||
- [ ] 执行冒烟测试
|
||||
|
||||
## 🚀 下一步操作
|
||||
|
||||
### 立即执行
|
||||
|
||||
请按照以下文档执行数据库迁移:
|
||||
|
||||
1. **快速命令清单**: `docs/deployment/数据库迁移命令.md`
|
||||
2. **详细操作步骤**: `docs/deployment/部署后操作步骤.md`
|
||||
3. **快速部署指南**: `docs/deployment/立即部署指南.md`
|
||||
|
||||
### 命令摘要
|
||||
|
||||
```bash
|
||||
# SSH 连接
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 执行迁移
|
||||
cd /root/deploy
|
||||
docker exec youyidian-3d-material npx prisma migrate deploy
|
||||
docker exec youyidian-3d-material npm run migrate:template-data
|
||||
docker exec youyidian-3d-material npm run verify:template-data
|
||||
```
|
||||
|
||||
## 🌐 访问地址
|
||||
|
||||
- **应用首页**: http://45.249.208.118:4320
|
||||
- **健康检查**: http://45.249.208.118:4320/api/health
|
||||
- **管理后台**: http://45.249.208.118:4320/admin
|
||||
- **模板管理**: http://45.249.208.118:4320/admin/templates
|
||||
|
||||
## 📋 相关文档
|
||||
|
||||
### 部署文档
|
||||
- `docs/deployment/立即部署指南.md` - 快速部署命令
|
||||
- `docs/deployment/部署后操作步骤.md` - 详细操作步骤
|
||||
- `docs/deployment/数据库迁移命令.md` - 迁移命令清单
|
||||
- `docs/deployment/pre-deployment-checklist.md` - 部署前检查清单
|
||||
|
||||
### 技术文档
|
||||
- `docs/api/template-system-api.md` - API 文档
|
||||
- `docs/user-guide/template-system-guide.md` - 用户手册
|
||||
- `docs/template-system-optimization-summary.md` - 项目总结
|
||||
|
||||
### 项目状态
|
||||
- `docs/PROJECT_STATUS.md` - 项目状态
|
||||
- `.kiro/specs/template-system-optimization/tasks.md` - 任务列表
|
||||
|
||||
## 🔧 技术细节
|
||||
|
||||
### 修复的问题
|
||||
|
||||
1. **Dockerfile 依赖冲突**
|
||||
- 添加 `--legacy-peer-deps` 到 npm ci 命令
|
||||
- 解决 peer dependencies 冲突
|
||||
|
||||
2. **Next.js 构建错误**
|
||||
- 添加 `eslint: { ignoreDuringBuilds: true }` 到 next.config.js
|
||||
- 跳过构建时的 ESLint 检查
|
||||
|
||||
3. **迁移脚本 TypeScript 错误**
|
||||
- 修复 Prisma 查询语法
|
||||
- 使用正确的 null 值处理方式
|
||||
|
||||
### 部署配置
|
||||
|
||||
- **Registry**: Harbor (harbor.flnxi.com/flnxi/)
|
||||
- **部署脚本**: deploy-new-fixed.ps1
|
||||
- **环境变量**: /root/deploy/.env.production
|
||||
- **端口映射**: 4320:3000
|
||||
|
||||
## 📞 需要帮助?
|
||||
|
||||
如果遇到问题,请查看:
|
||||
1. 容器日志: `docker logs youyidian-3d-material --tail 100`
|
||||
2. 故障排查: `docs/deployment/部署后操作步骤.md` 中的"故障排查"章节
|
||||
3. 项目状态: `docs/PROJECT_STATUS.md`
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2024-12-13
|
||||
@ -1,55 +0,0 @@
|
||||
# 数据库迁移命令清单
|
||||
|
||||
## 🎯 快速执行(复制粘贴)
|
||||
|
||||
```bash
|
||||
# 1. SSH连接
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 2. 进入目录
|
||||
cd /root/deploy
|
||||
|
||||
# 3. 检查容器
|
||||
docker ps | grep youyidian-3d-material
|
||||
|
||||
# 4. 应用数据库迁移
|
||||
docker exec youyidian-3d-material npx prisma migrate deploy
|
||||
|
||||
# 5. 运行数据迁移
|
||||
docker exec youyidian-3d-material npm run migrate:template-data
|
||||
|
||||
# 6. 验证数据
|
||||
docker exec youyidian-3d-material npm run verify:template-data
|
||||
|
||||
# 7. 查看日志
|
||||
docker logs youyidian-3d-material --tail 50
|
||||
```
|
||||
|
||||
## ✅ 成功标志
|
||||
|
||||
### Prisma迁移成功
|
||||
```
|
||||
✓ Prisma Migrate applied the following migration(s):
|
||||
migrations/
|
||||
└─ 20251213073100_add_template_system_indexes/
|
||||
```
|
||||
|
||||
### 数据迁移成功
|
||||
```
|
||||
✓ 数据迁移成功完成!
|
||||
```
|
||||
|
||||
### 数据验证成功
|
||||
```
|
||||
✓ 所有验证通过!
|
||||
```
|
||||
|
||||
## 🌐 验证访问
|
||||
|
||||
- 首页: http://45.249.208.118:4320
|
||||
- 健康检查: http://45.249.208.118:4320/api/health
|
||||
- 模板管理: http://45.249.208.118:4320/admin/templates
|
||||
|
||||
## 📋 完整文档
|
||||
|
||||
详细步骤和故障排查:`docs/deployment/POST_DEPLOYMENT_STEPS.md`
|
||||
@ -1,121 +0,0 @@
|
||||
# 立即部署指南
|
||||
|
||||
## 🚀 快速部署命令
|
||||
|
||||
### 方式1: 使用默认配置(推荐)
|
||||
|
||||
```powershell
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
```
|
||||
|
||||
这个命令会:
|
||||
- ✅ 运行代码检查
|
||||
- ✅ 构建Docker镜像
|
||||
- ✅ 推送到Harbor (harbor.flnxi.com/flnxi/)
|
||||
- ✅ SSH到服务器 (45.249.208.118)
|
||||
- ✅ 自动部署应用
|
||||
|
||||
### 方式2: 使用SSH密钥(更安全)
|
||||
|
||||
```powershell
|
||||
.\deploy-new-fixed.ps1 `
|
||||
-Registry harbor `
|
||||
-KeyPath "C:\Users\YourUsername\.ssh\youyidian_prod_ed25519"
|
||||
```
|
||||
|
||||
### 方式3: 跳过代码检查(快速部署)
|
||||
|
||||
```powershell
|
||||
.\deploy-new-fixed.ps1 -Registry harbor -SkipCheck
|
||||
```
|
||||
|
||||
## 📋 部署后操作
|
||||
|
||||
部署完成后,需要SSH到服务器执行数据迁移:
|
||||
|
||||
```bash
|
||||
# 1. SSH连接到服务器
|
||||
ssh root@45.249.208.118
|
||||
|
||||
# 2. 进入部署目录
|
||||
cd /root/deploy
|
||||
|
||||
# 3. 应用数据库迁移
|
||||
docker exec flnxi-material npx prisma migrate deploy
|
||||
|
||||
# 4. 运行数据迁移脚本
|
||||
docker exec flnxi-material npm run migrate:template-data
|
||||
|
||||
# 5. 验证数据完整性
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
|
||||
# 6. 查看应用日志
|
||||
docker logs -f flnxi-material --tail 50
|
||||
```
|
||||
|
||||
## ✅ 验证部署
|
||||
|
||||
访问以下地址验证部署:
|
||||
|
||||
- 应用首页: http://45.249.208.118:4320
|
||||
- 健康检查: http://45.249.208.118:4320/api/health
|
||||
- 管理后台: http://45.249.208.118:4320/admin
|
||||
- 模板管理: http://45.249.208.118:4320/admin/templates
|
||||
|
||||
## 🔍 快速测试
|
||||
|
||||
1. **模板管理**
|
||||
- 访问 `/admin/templates`
|
||||
- 创建一个新模板
|
||||
- 添加几个字段
|
||||
- 测试拖拽排序
|
||||
|
||||
2. **素材上传**
|
||||
- 访问首页
|
||||
- 点击上传
|
||||
- 选择刚创建的模板
|
||||
- 填写字段并上传
|
||||
|
||||
3. **首页筛选**
|
||||
- 返回首页
|
||||
- 测试Tag筛选功能
|
||||
- 验证筛选结果
|
||||
|
||||
## ⚠️ 常见问题
|
||||
|
||||
### Q: Docker Desktop未运行
|
||||
```powershell
|
||||
# 启动Docker Desktop后重试
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
```
|
||||
|
||||
### Q: SSH连接失败
|
||||
```powershell
|
||||
# 使用SSH密钥
|
||||
.\deploy-new-fixed.ps1 `
|
||||
-Registry harbor `
|
||||
-KeyPath "C:\Users\YourUsername\.ssh\youyidian_prod_ed25519"
|
||||
```
|
||||
|
||||
### Q: Harbor推送失败
|
||||
```powershell
|
||||
# 检查Harbor凭据
|
||||
docker login harbor.flnxi.com -u admin -p Harbor12345
|
||||
|
||||
# 重新部署
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
```
|
||||
|
||||
## 📞 需要帮助?
|
||||
|
||||
- 完整部署文档: `docs/deployment/DEPLOYMENT.md`
|
||||
- 快速部署指南: `docs/deployment/quick-deployment-guide.md`
|
||||
- 部署检查清单: `docs/deployment/pre-deployment-checklist.md`
|
||||
|
||||
---
|
||||
|
||||
**准备好了吗?运行命令开始部署!** 🚀
|
||||
|
||||
```powershell
|
||||
.\deploy-new-fixed.ps1 -Registry harbor
|
||||
```
|
||||
@ -1,323 +0,0 @@
|
||||
# 部署后操作步骤
|
||||
|
||||
## 📋 当前状态
|
||||
|
||||
✅ **应用已成功部署**
|
||||
- 服务器: 45.249.208.118:4320
|
||||
- 容器名称: `youyidian-3d-material`
|
||||
- 健康检查: 通过
|
||||
|
||||
⏳ **待完成**: 数据库迁移
|
||||
|
||||
---
|
||||
|
||||
## 🚀 立即执行的步骤
|
||||
|
||||
### 步骤 1: SSH连接到服务器
|
||||
|
||||
```bash
|
||||
ssh root@45.249.208.118
|
||||
```
|
||||
|
||||
### 步骤 2: 进入部署目录
|
||||
|
||||
```bash
|
||||
cd /root/deploy
|
||||
```
|
||||
|
||||
### 步骤 3: 检查容器状态
|
||||
|
||||
```bash
|
||||
# 查看容器是否运行
|
||||
docker ps | grep youyidian-3d-material
|
||||
|
||||
# 查看容器日志
|
||||
docker logs youyidian-3d-material --tail 50
|
||||
```
|
||||
|
||||
### 步骤 4: 应用数据库迁移
|
||||
|
||||
```bash
|
||||
# 应用Prisma迁移
|
||||
docker exec youyidian-3d-material npx prisma migrate deploy
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
```
|
||||
✓ Prisma Migrate applied the following migration(s):
|
||||
migrations/
|
||||
└─ 20251213073100_add_template_system_indexes/
|
||||
```
|
||||
|
||||
### 步骤 5: 运行数据迁移脚本
|
||||
|
||||
```bash
|
||||
# 迁移旧数据
|
||||
docker exec youyidian-3d-material npm run migrate:template-data
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
```
|
||||
开始数据迁移...
|
||||
|
||||
步骤 1: 检查模板version字段...
|
||||
✓ 所有模板都有version字段
|
||||
|
||||
步骤 2: 检查没有templateId的素材...
|
||||
✓ 所有素材都有templateId
|
||||
|
||||
步骤 3: 验证数据完整性...
|
||||
模板总数: X
|
||||
有version的模板: X
|
||||
素材总数(未删除): X
|
||||
有templateId的素材: X
|
||||
字段总数: X
|
||||
有templateId的字段: X
|
||||
字段池定义总数: X
|
||||
|
||||
✓ 数据完整性验证完成
|
||||
|
||||
============================================================
|
||||
迁移报告
|
||||
============================================================
|
||||
模板: X/X 有version字段
|
||||
素材: X/X 有templateId
|
||||
字段: X/X 有templateId
|
||||
字段池: X 个定义
|
||||
============================================================
|
||||
|
||||
✓ 数据迁移成功完成!
|
||||
```
|
||||
|
||||
### 步骤 6: 验证数据完整性
|
||||
|
||||
```bash
|
||||
# 验证数据
|
||||
docker exec youyidian-3d-material npm run verify:template-data
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
```
|
||||
开始数据验证...
|
||||
|
||||
验证 1: 检查模板数据...
|
||||
验证 2: 检查素材数据...
|
||||
验证 3: 检查字段数据...
|
||||
验证 4: 检查字段池数据...
|
||||
验证 5: 检查孤立数据...
|
||||
|
||||
================================================================================
|
||||
验证报告
|
||||
================================================================================
|
||||
|
||||
模板:
|
||||
✓ 模板名称完整性
|
||||
✓ 所有模板都有名称
|
||||
✓ 模板version字段
|
||||
✓ 所有模板都有version
|
||||
✓ 模板materialKind有效性
|
||||
✓ 所有模板的materialKind都有效
|
||||
|
||||
素材:
|
||||
✓ 素材templateId完整性
|
||||
✓ 所有素材都有templateId
|
||||
✓ 素材templateId引用有效性
|
||||
✓ 所有素材的templateId引用都有效
|
||||
|
||||
字段:
|
||||
✓ 字段templateId完整性
|
||||
✓ 所有字段都有templateId
|
||||
✓ 字段key唯一性
|
||||
✓ 所有字段key在模板内唯一
|
||||
|
||||
字段池:
|
||||
✓ 字段池key唯一性
|
||||
✓ 所有字段池key唯一
|
||||
|
||||
孤立数据:
|
||||
✓ 没有字段的模板
|
||||
⚠ X 个模板没有字段(可能是草稿)
|
||||
✓ 没有素材的模板
|
||||
⚠ X 个模板没有素材(可能是新建的)
|
||||
|
||||
================================================================================
|
||||
统计信息
|
||||
================================================================================
|
||||
模板总数: X (活跃: X)
|
||||
字段总数: X
|
||||
素材总数: X
|
||||
字段池定义: X (启用筛选: X)
|
||||
================================================================================
|
||||
|
||||
✓ 所有验证通过!
|
||||
```
|
||||
|
||||
### 步骤 7: 重启容器(可选)
|
||||
|
||||
```bash
|
||||
# 如果需要重启容器
|
||||
docker restart youyidian-3d-material
|
||||
|
||||
# 等待几秒后检查状态
|
||||
sleep 5
|
||||
docker ps | grep youyidian-3d-material
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证部署
|
||||
|
||||
### 1. 访问应用
|
||||
|
||||
在浏览器中访问以下地址:
|
||||
|
||||
- **应用首页**: http://45.249.208.118:4320
|
||||
- **健康检查**: http://45.249.208.118:4320/api/health
|
||||
- **管理后台**: http://45.249.208.118:4320/admin
|
||||
- **模板管理**: http://45.249.208.118:4320/admin/templates
|
||||
|
||||
### 2. 功能测试
|
||||
|
||||
#### 测试 1: 模板管理
|
||||
1. 访问 `/admin/templates`
|
||||
2. 查看现有模板列表
|
||||
3. 点击"创建模板"
|
||||
4. 填写模板信息并添加字段
|
||||
5. 保存模板
|
||||
|
||||
#### 测试 2: 素材上传
|
||||
1. 返回首页
|
||||
2. 点击"上传素材"
|
||||
3. 选择刚创建的模板
|
||||
4. 填写字段信息
|
||||
5. 上传文件
|
||||
|
||||
#### 测试 3: 首页筛选
|
||||
1. 返回首页
|
||||
2. 使用Tag筛选功能
|
||||
3. 验证筛选结果正确
|
||||
|
||||
### 3. 查看日志
|
||||
|
||||
```bash
|
||||
# 实时查看应用日志
|
||||
docker logs -f youyidian-3d-material --tail 100
|
||||
|
||||
# 查看最近的错误日志
|
||||
docker logs youyidian-3d-material 2>&1 | grep -i error | tail -20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 故障排查
|
||||
|
||||
### 问题 1: 容器未运行
|
||||
|
||||
```bash
|
||||
# 检查容器状态
|
||||
docker ps -a | grep youyidian-3d-material
|
||||
|
||||
# 查看容器日志
|
||||
docker logs youyidian-3d-material --tail 100
|
||||
|
||||
# 重启容器
|
||||
docker restart youyidian-3d-material
|
||||
```
|
||||
|
||||
### 问题 2: 数据库连接失败
|
||||
|
||||
```bash
|
||||
# 检查数据库连接
|
||||
docker exec youyidian-3d-material npx prisma db pull
|
||||
|
||||
# 查看环境变量
|
||||
docker exec youyidian-3d-material env | grep DATABASE_URL
|
||||
```
|
||||
|
||||
### 问题 3: 迁移脚本失败
|
||||
|
||||
```bash
|
||||
# 查看详细错误
|
||||
docker exec youyidian-3d-material npm run migrate:template-data 2>&1 | tee migration-error.log
|
||||
|
||||
# 手动连接数据库检查
|
||||
docker exec -it youyidian-3d-material npx prisma studio
|
||||
```
|
||||
|
||||
### 问题 4: 应用无法访问
|
||||
|
||||
```bash
|
||||
# 检查端口映射
|
||||
docker port youyidian-3d-material
|
||||
|
||||
# 检查防火墙
|
||||
sudo ufw status
|
||||
|
||||
# 检查Nginx配置(如果使用)
|
||||
sudo nginx -t
|
||||
sudo systemctl status nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 监控指标
|
||||
|
||||
### 关键指标
|
||||
|
||||
```bash
|
||||
# CPU和内存使用
|
||||
docker stats youyidian-3d-material --no-stream
|
||||
|
||||
# 磁盘使用
|
||||
df -h
|
||||
|
||||
# 数据库大小
|
||||
docker exec youyidian-3d-material npx prisma db execute --stdin <<< "SELECT pg_size_pretty(pg_database_size('your_database_name'));"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 回滚方案
|
||||
|
||||
如果部署出现问题,可以回滚到之前的版本:
|
||||
|
||||
```bash
|
||||
# 1. 停止当前容器
|
||||
docker stop youyidian-3d-material
|
||||
|
||||
# 2. 使用之前的镜像重新部署
|
||||
docker run -d \
|
||||
--name youyidian-3d-material \
|
||||
-p 4320:3000 \
|
||||
--env-file /root/deploy/.env.production \
|
||||
harbor.flnxi.com/flnxi/flnxi-material:previous-tag
|
||||
|
||||
# 3. 如果需要回滚数据库
|
||||
docker exec youyidian-3d-material npx prisma migrate resolve --rolled-back "20251213073100_add_template_system_indexes"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 完成后更新任务
|
||||
|
||||
完成所有步骤后,更新任务文件:
|
||||
|
||||
```markdown
|
||||
- [x] 29. 部署到测试环境
|
||||
- ✅ 备份数据库
|
||||
- ✅ 部署新代码
|
||||
- ✅ 容器运行正常
|
||||
- ✅ 数据库迁移完成
|
||||
- ✅ 数据验证通过
|
||||
- ✅ 冒烟测试完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 需要帮助?
|
||||
|
||||
- 完整部署文档: `docs/deployment/DEPLOYMENT.md`
|
||||
- 快速部署指南: `docs/deployment/quick-deployment-guide.md`
|
||||
- 部署检查清单: `docs/deployment/pre-deployment-checklist.md`
|
||||
- API文档: `docs/api/template-system-api.md`
|
||||
- 用户手册: `docs/user-guide/template-system-guide.md`
|
||||
@ -1,109 +0,0 @@
|
||||
# 部署完成总结
|
||||
|
||||
## ✅ 部署状态
|
||||
|
||||
**部署时间**: 2024-12-13
|
||||
**部署状态**: 成功 ✅
|
||||
**服务器**: 45.249.208.118:4320
|
||||
**容器**: youyidian-3d-material
|
||||
|
||||
## 📊 部署详情
|
||||
|
||||
### 1. 代码部署
|
||||
- ✅ Docker 镜像构建成功
|
||||
- ✅ 推送到 Harbor Registry
|
||||
- ✅ 部署到生产服务器
|
||||
- ✅ 容器启动成功
|
||||
|
||||
### 2. 数据库迁移
|
||||
- ✅ Prisma 迁移已应用
|
||||
- ✅ 迁移标记为已完成(20251213073100_add_template_system_indexes)
|
||||
- ✅ 数据验证通过
|
||||
|
||||
### 3. 数据统计
|
||||
- **模板数量**: 3 个
|
||||
- **素材数量**: 4 个(未删除)
|
||||
- **数据完整性**: 正常
|
||||
|
||||
### 4. 修复的问题
|
||||
- ✅ Dockerfile 依赖冲突(添加 --legacy-peer-deps)
|
||||
- ✅ Next.js ESLint 构建错误(ignoreDuringBuilds)
|
||||
- ✅ 迁移脚本 TypeScript 错误
|
||||
- ✅ package.json 脚本命令(改用 npx tsx)
|
||||
|
||||
## 🌐 访问地址
|
||||
|
||||
- **应用首页**: http://45.249.208.118:4320
|
||||
- **健康检查**: http://45.249.208.118:4320/api/health
|
||||
- **管理后台**: http://45.249.208.118:4320/admin
|
||||
- **模板管理**: http://45.249.208.118:4320/admin/templates
|
||||
|
||||
## 🧪 待完成:功能测试
|
||||
|
||||
### 测试清单
|
||||
|
||||
1. **模板管理测试**
|
||||
- [ ] 访问模板管理页面
|
||||
- [ ] 查看现有模板列表
|
||||
- [ ] 创建新模板
|
||||
- [ ] 编辑模板字段
|
||||
- [ ] 测试拖拽排序
|
||||
|
||||
2. **素材上传测试**
|
||||
- [ ] 访问首页
|
||||
- [ ] 点击上传素材
|
||||
- [ ] 选择模板
|
||||
- [ ] 填写字段
|
||||
- [ ] 上传文件
|
||||
|
||||
3. **首页筛选测试**
|
||||
- [ ] 测试 Tag 筛选功能
|
||||
- [ ] 验证筛选结果
|
||||
- [ ] 测试多选筛选
|
||||
|
||||
## 📝 技术细节
|
||||
|
||||
### 部署配置
|
||||
```yaml
|
||||
Registry: Harbor (harbor.flnxi.com/flnxi/)
|
||||
镜像: flnxi-material:local-deploy
|
||||
端口: 4320:3000
|
||||
环境变量: /root/deploy/.env.production
|
||||
```
|
||||
|
||||
### 数据库迁移
|
||||
```sql
|
||||
-- 已应用的迁移
|
||||
20251213073100_add_template_system_indexes
|
||||
|
||||
-- 迁移内容
|
||||
- 添加模板系统索引
|
||||
- 添加用户字段(钉钉集成)
|
||||
- 添加素材收藏、下载日志
|
||||
- 添加平台、部门、弹幕表
|
||||
```
|
||||
|
||||
### 修改的文件
|
||||
1. `Dockerfile` - 添加 --legacy-peer-deps
|
||||
2. `next.config.js` - 添加 eslint.ignoreDuringBuilds
|
||||
3. `package.json` - 修改迁移脚本命令
|
||||
4. `scripts/migrate-template-data.ts` - 修复 TypeScript 错误
|
||||
5. `scripts/verify-template-data.ts` - 修复 TypeScript 错误
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- **部署指南**: `docs/deployment/立即部署指南.md`
|
||||
- **操作步骤**: `docs/deployment/部署后操作步骤.md`
|
||||
- **迁移命令**: `docs/deployment/数据库迁移命令.md`
|
||||
- **当前状态**: `docs/deployment/当前部署状态.md`
|
||||
- **任务列表**: `.kiro/specs/template-system-optimization/tasks.md`
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
部署已成功完成!应用正在 http://45.249.208.118:4320 运行。
|
||||
|
||||
**下一步**: 执行功能冒烟测试,验证所有功能正常工作。
|
||||
|
||||
---
|
||||
**部署完成时间**: 2024-12-13
|
||||
**部署人员**: AI Assistant (Kiro)
|
||||
312
docs/deployment/部署说明.md
Normal file
312
docs/deployment/部署说明.md
Normal file
@ -0,0 +1,312 @@
|
||||
# FLNXI 素材库部署说明
|
||||
|
||||
本文档是当前主应用部署入口,合并了原来的快速部署、部署前检查清单和完整部署说明。
|
||||
|
||||
先说结论:
|
||||
|
||||
- 客户或私有化交付默认按 `NAS(网络存储)` / `Linux(服务器系统)` 现场参数部署。
|
||||
- 固定服务器、固定端口、固定 Registry 地址只属于内部维护示例,不是客户默认交付口径。
|
||||
- 客户交付必须显式固定完整 `APP_IMAGE`,例如 `registry.example.com/flnxi/material/flnxi-material:1.0.1`;不要把 `latest` 当成客户部署依据。
|
||||
- `deploy-registry.ps1` 不再默认指向任何测试服务器,发布和远程部署必须显式传入 `-RegistryHost`,远程部署还必须显式传入 `-Server`。
|
||||
- 不同品牌 NAS 或客户自有 Linux 服务器交付前,先看 `docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md`。
|
||||
|
||||
## 1. 当前部署形态
|
||||
|
||||
主应用生产编排文件:
|
||||
|
||||
```bash
|
||||
docker-compose.prod.yml
|
||||
```
|
||||
|
||||
当前会拉起:
|
||||
|
||||
- `postgres`:PostgreSQL 数据库
|
||||
- `minio`:MinIO 对象存储
|
||||
- `minio-init`:初始化对象存储桶
|
||||
- `app`:FLNXI 素材库主应用
|
||||
|
||||
当前主应用镜像以 Registry 发布链路为准,实际部署时可通过 `.env` 中的 `APP_IMAGE`、`POSTGRES_IMAGE`、`MINIO_IMAGE`、`MINIO_MC_IMAGE` 覆盖。容器名、卷名、网络名和端口也支持用 `.env` 覆盖,下面命令中的 `flnxi-material` 和 `4320` 都只是默认值。
|
||||
|
||||
`APP_IMAGE` 是生产启动必填项。这样做是为了让客户现场明确运行哪个版本,避免安装或升级时误拉旧仓库、旧测试镜像或 `latest`。
|
||||
|
||||
当前不会由该 Compose 文件直接拉起:
|
||||
|
||||
- Nginx / Traefik 等反向代理
|
||||
- 独立 `license-server`
|
||||
- Node 版或 Go 版安装 Agent
|
||||
|
||||
这些组件根据交付场景单独部署或由安装 Agent 编排。
|
||||
|
||||
## 2. 部署前准备
|
||||
|
||||
### 2.1 机器条件
|
||||
|
||||
建议最低条件:
|
||||
|
||||
- CPU:4 核及以上
|
||||
- 内存:8GB 及以上
|
||||
- 磁盘:100GB 可用空间
|
||||
- 系统:Ubuntu 22.04 LTS、群晖 DSM、飞牛 fnOS 或其他支持 Docker / Podman 的 Linux 环境
|
||||
- Docker:Docker 24+ 和 Docker Compose V2
|
||||
|
||||
### 2.2 必填环境变量
|
||||
|
||||
部署前至少准备:
|
||||
|
||||
```env
|
||||
POSTGRES_PASSWORD=<generate-a-strong-random-password>
|
||||
MINIO_ROOT_USER=<generate-a-storage-admin-user>
|
||||
MINIO_ROOT_PASSWORD=<generate-a-strong-random-password>
|
||||
APP_IMAGE=registry.example.com/flnxi/material/flnxi-material:1.0.1
|
||||
SERVER_IP=192.168.x.x
|
||||
NEXTAUTH_URL=http://192.168.x.x:4320
|
||||
NEXTAUTH_SECRET=<generate-a-strong-random-secret>
|
||||
S3_BUCKET=flnxi-materials
|
||||
LICENSE_SERVER_URL=https://your-license-server.example.com
|
||||
LICENSE_PROJECT_CODE=flnxi-sucai
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `SERVER_IP` 必须是用户浏览器能访问到的 NAS / 服务器地址。
|
||||
- `NEXTAUTH_URL` 必须和最终访问入口一致。
|
||||
- `APP_IMAGE` 必须写完整镜像地址和版本号,不要只写 `latest`。
|
||||
- `POSTGRES_IMAGE`、`MINIO_IMAGE`、`MINIO_MC_IMAGE` 建议同样固定版本;离线或受控网络交付时,应写成私有 Registry 里的固定版本镜像。
|
||||
- `LICENSE_PROJECT_CODE` 必须和 `license-server` 中项目表的 `projects.code` 一致。
|
||||
- 不要把真实密码、许可证地址、对象存储密钥写入 Git。
|
||||
|
||||
### 2.3 HTTP Registry 前置检查
|
||||
|
||||
如果 Registry 使用 `http://`,本机 Docker 和目标机器 Docker 都要先允许这个 Registry。否则会出现 `HTTP response to HTTPS client`,表现为登录、推送或拉取镜像失败。
|
||||
|
||||
Linux 目标机常见配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"insecure-registries": ["registry.example.com:5000"]
|
||||
}
|
||||
```
|
||||
|
||||
配置位置通常是 `/etc/docker/daemon.json`。改完后重启 Docker:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart docker
|
||||
docker info | grep -A 5 "Insecure Registries"
|
||||
```
|
||||
|
||||
Docker Desktop 需要在设置里的 Docker Engine 配置同样的 `insecure-registries`,保存并重启 Docker。
|
||||
|
||||
## 3. 标准部署步骤
|
||||
|
||||
### 3.1 准备环境文件
|
||||
|
||||
在目标机器部署目录准备 `.env`:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
再按实际环境补齐数据库、MinIO、NextAuth、许可证和访问地址。
|
||||
|
||||
### 3.2 启动服务
|
||||
|
||||
正式客户交付先通过 `deploy-registry.ps1` 完成版本化镜像发布,并把远端 `.env` 里的 `APP_IMAGE` 写到目标版本,再执行下面的启动命令。
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
如果需要本机重新构建镜像:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
```
|
||||
|
||||
### 3.3 初始化数据库
|
||||
|
||||
主应用容器入口脚本 `scripts/docker-entrypoint.sh` 会在启动时自动执行:
|
||||
|
||||
```bash
|
||||
npx prisma migrate deploy
|
||||
```
|
||||
|
||||
所以常规部署不需要手动再跑一次数据库迁移。
|
||||
|
||||
如果历史模板数据需要补齐,或自动迁移失败后要人工补救,再按实际容器名执行:
|
||||
|
||||
```bash
|
||||
docker exec <APP_CONTAINER_NAME> npm run migrate:template-data
|
||||
docker exec <APP_CONTAINER_NAME> npm run verify:template-data
|
||||
```
|
||||
|
||||
默认容器名是 `flnxi-material`;如果 `.env` 配了 `APP_CONTAINER_NAME`,以实际值为准。
|
||||
|
||||
如是全新环境,继续按 `docs/初次部署与出厂设置指南.md` 走系统初始化流程。
|
||||
|
||||
### 3.4 验证服务
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
curl http://<SERVER_IP>:4320/api/health
|
||||
docker logs flnxi-material --tail 100
|
||||
```
|
||||
|
||||
如果 `.env` 覆盖了 `APP_PORT` 或 `APP_CONTAINER_NAME`,验证命令也要同步替换成实际端口和容器名。
|
||||
|
||||
页面验证:
|
||||
|
||||
- 登录页:`http://<SERVER_IP>:4320/login`
|
||||
- 首页:`http://<SERVER_IP>:4320`
|
||||
- 管理后台:`http://<SERVER_IP>:4320/admin`
|
||||
- 模板管理:在 `http://<SERVER_IP>:4320/admin` 后台内打开;当前没有独立的 `/admin/templates` 路由
|
||||
|
||||
## 4. 版本化 Registry 发布
|
||||
|
||||
当前仓库的主发布脚本是 `deploy-registry.ps1`,用于构建、推送和远程更新主应用、Agent 以及运行时镜像。它默认会把版本镜像写入远端 `.env`,客户升级不要再回退到默认 `latest`。
|
||||
|
||||
`DeployApp` 远程部署现在默认包含第一轮升级保护:
|
||||
|
||||
- 升级前在远端创建 `backups/deploy-时间戳/`,保存升级前 `.env`。
|
||||
- 如果远端 PostgreSQL 是当前 Compose 管理的 `postgres` 服务,会先执行 `pg_dump` 备份;如果使用外部数据库,会提示需要确认外部备份。
|
||||
- 部署失败时默认尝试恢复升级前 `.env`,并按旧运行镜像或旧 `APP_IMAGE` 重建应用容器。
|
||||
- 部署后会在远端校验应用容器实际运行镜像、镜像内 `VERSION.txt` 和 Prisma 迁移状态,然后再做外部 `/api/health` 检查。
|
||||
|
||||
只有在已有其它备份和回滚体系时,才考虑使用 `-NoRemoteDbBackup` 或 `-NoRollback`。
|
||||
|
||||
典型用法:
|
||||
|
||||
```powershell
|
||||
$registryPassword = Read-Host 'Registry密码' -AsSecureString
|
||||
$sshPassword = Read-Host 'SSH密码' -AsSecureString
|
||||
.\deploy-registry.ps1 -Action DeployApp -DeployChannel release -Version 1.0.1 -RegistryHost registry.example.com:5000 -Server 192.168.1.20 -RegistryPassword $registryPassword -Password $sshPassword
|
||||
.\deploy-registry.ps1 -Action PublishRelease -Version 1.0.1 -RegistryHost registry.example.com:5000 -RegistryPassword $registryPassword
|
||||
.\deploy-registry.ps1 -Action PublishAgent -Version 1.0.1 -RegistryHost registry.example.com:5000 -RegistryPassword $registryPassword
|
||||
.\deploy-registry.ps1 -Action SyncRuntime -RegistryHost registry.example.com:5000 -RegistryPassword $registryPassword
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 客户升级和内部发布优先按版本 tag 走,不要把 `latest` 当成客户部署依据。
|
||||
- 客户现场部署使用 `-DeployChannel release -Version x.x.x`;测试通道只用于内部验证,必须显式传 `-DeployChannel test`。
|
||||
- `DeployApp` 远程部署时默认会更新远端 `.env` 的 `APP_IMAGE`、访问地址和运行时镜像;只有临时验证时才使用 `-NoUpdateRemoteEnv`。
|
||||
- `DeployApp` 默认会做远端 `.env` 备份、Compose PostgreSQL 备份、失败自动回滚、运行镜像校验、版本文件校验和 Prisma 迁移状态校验。
|
||||
- 旧的 `deploy-new-fixed.ps1` 仍可作为内部维护参考,但不再作为当前主线部署入口。
|
||||
- 如果要做远程更新,先确认 `Registry`、SSH、`.env` 和目标机器都属于当前维护环境。
|
||||
|
||||
## 5. 部署前检查清单
|
||||
|
||||
- [ ] 已确认目标机器品牌、系统、Docker / Podman 支持情况
|
||||
- [ ] 已确认端口、域名、证书、反向代理方案
|
||||
- [ ] 已确认数据目录、对象存储目录和备份位置
|
||||
- [ ] `.env` 已填写真实环境变量
|
||||
- [ ] `APP_IMAGE` 已固定到完整版本镜像,不使用 `latest`
|
||||
- [ ] 如果 Registry 是 HTTP,本机和目标机 Docker 都已配置 `insecure-registries`
|
||||
- [ ] `NEXTAUTH_URL` 与最终访问地址一致
|
||||
- [ ] `S3_BUCKET`、`S3_PUBLIC_DOMAIN` 口径正确
|
||||
- [ ] `LICENSE_SERVER_URL` 和 `LICENSE_PROJECT_CODE` 已确认
|
||||
- [ ] 已执行与本次改动相关的测试或命令
|
||||
- [ ] 涉及数据库变更时已有备份和回滚方案
|
||||
|
||||
## 6. 部署后冒烟测试
|
||||
|
||||
- [ ] `/api/health` 正常
|
||||
- [ ] 登录 / 退出正常
|
||||
- [ ] 首次初始化或激活流程正常
|
||||
- [ ] 首页素材列表可加载
|
||||
- [ ] 素材上传、预览、下载正常
|
||||
- [ ] 后台模板管理可打开
|
||||
- [ ] 系统设置可读取和保存
|
||||
- [ ] 许可证状态显示正确
|
||||
- [ ] 容器日志没有持续错误
|
||||
|
||||
## 7. 数据备份与回滚
|
||||
|
||||
`deploy-registry.ps1 -Action DeployApp` 会默认做一轮升级前备份和失败回滚。下面命令主要用于人工兜底、外部数据库场景或单独恢复演练。
|
||||
|
||||
### 7.1 手动备份数据库
|
||||
|
||||
```bash
|
||||
docker exec flnxi-postgres pg_dump \
|
||||
-U postgres \
|
||||
-d flnxi \
|
||||
-F c \
|
||||
-f /tmp/flnxi-backup-$(date +%Y%m%d-%H%M%S).dump
|
||||
```
|
||||
|
||||
再把备份文件复制到宿主机或外部备份位置。外部数据库不受 Compose 脚本管理,必须按客户现场数据库方案单独备份。
|
||||
|
||||
### 7.2 手动回滚应用
|
||||
|
||||
如使用镜像部署,回滚到上一镜像版本时,直接改完整 `APP_IMAGE`。不要只改 `IMAGE_TAG`,因为完整 `APP_IMAGE` 的优先级更高。
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml down
|
||||
cp .env .env.rollback-$(date +%Y%m%d-%H%M%S)
|
||||
sed -i 's#^APP_IMAGE=.*#APP_IMAGE=registry.example.com/flnxi/material/flnxi-material:<previous-version>#' .env
|
||||
docker compose -f docker-compose.prod.yml pull app
|
||||
docker compose -f docker-compose.prod.yml up -d app
|
||||
curl http://<SERVER_IP>:4320/api/health
|
||||
```
|
||||
|
||||
回滚前先确认数据库是否需要同时回滚。只回滚应用镜像不等于回滚数据库结构;如果本次升级包含数据库迁移,必须先确认备份和恢复路径。
|
||||
|
||||
### 7.3 手动恢复数据库
|
||||
|
||||
恢复前必须确认已停止会写入数据库的应用容器,并确认备份文件正确。
|
||||
|
||||
```bash
|
||||
docker exec -i flnxi-postgres pg_restore \
|
||||
-U postgres \
|
||||
-d flnxi \
|
||||
-c \
|
||||
/tmp/flnxi-backup-YYYYMMDD-HHMMSS.dump
|
||||
```
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
### 应用访问不到
|
||||
|
||||
优先检查:
|
||||
|
||||
- `docker compose -f docker-compose.prod.yml ps`
|
||||
- `APP_PORT` 是否和访问端口一致
|
||||
- `SERVER_IP` 和 `NEXTAUTH_URL` 是否填写成浏览器可访问地址
|
||||
- 防火墙是否放通端口
|
||||
|
||||
### MinIO 预览或下载异常
|
||||
|
||||
优先检查:
|
||||
|
||||
- `S3_BUCKET`
|
||||
- `MINIO_ROOT_USER`
|
||||
- `MINIO_ROOT_PASSWORD`
|
||||
- `S3_PUBLIC_DOMAIN`
|
||||
- MinIO 控制台是否能看到桶和对象
|
||||
- `S3_SET_ACL` 是否仍为默认 `0`。私有化交付默认不公开素材桶;只有兼容旧公开对象模式时才允许显式改成 `1`。
|
||||
- 当前生产 Compose 的 `minio-init` 会默认执行 `mc anonymous set none`,用于关闭 MinIO 桶匿名下载。如果客户历史环境曾经公开过桶,升级后应重跑 `minio-init` 或手动执行同等命令确认桶已恢复私有。
|
||||
|
||||
### 许可证异常
|
||||
|
||||
优先检查:
|
||||
|
||||
- `LICENSE_SERVER_URL`
|
||||
- `LICENSE_PROJECT_CODE`
|
||||
- 许可证服务是否存在对应项目编码
|
||||
- 主应用 `/api/license/check`、`/api/license/verify` 响应
|
||||
|
||||
### 数据迁移失败
|
||||
|
||||
优先查看:
|
||||
|
||||
```bash
|
||||
docker logs flnxi-material --tail 200
|
||||
docker exec flnxi-material npx prisma validate
|
||||
docker exec flnxi-material npm run verify:template-data
|
||||
```
|
||||
|
||||
## 9. 相关文档
|
||||
|
||||
- `docs/初次部署与出厂设置指南.md`
|
||||
- `docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md`
|
||||
- `docs/许可证服务接入说明.md`
|
||||
- `scripts/README.md`
|
||||
@ -1,344 +0,0 @@
|
||||
# 3d.yydd.ai 访问问题修复报告
|
||||
|
||||
**问题发现时间**:2025-11-05 21:30
|
||||
**修复完成时间**:2025-11-05 21:37
|
||||
**修复状态**:✅ **完全解决**
|
||||
**修复耗时**:7分钟
|
||||
|
||||
---
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
用户报告 **3d.yydd.ai 无法访问**,HTTP和HTTPS都连接超时。
|
||||
|
||||
### 症状
|
||||
```
|
||||
访问 https://3d.yydd.ai
|
||||
↓
|
||||
连接超时(10秒后放弃)
|
||||
↓
|
||||
无法访问网站
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 问题诊断过程
|
||||
|
||||
### 第1步:网络连通性检查
|
||||
```bash
|
||||
ping 180.97.221.140
|
||||
✅ 网络正常,延迟6ms,丢包率0%
|
||||
```
|
||||
|
||||
### 第2步:端口可达性检查
|
||||
```bash
|
||||
nc -zv 180.97.221.140 80
|
||||
nc -zv 180.97.221.140 443
|
||||
✅ 80端口和443端口都可连接
|
||||
```
|
||||
|
||||
### 第3步:服务状态检查
|
||||
```bash
|
||||
# 公网服务器
|
||||
FRPS服务: ✅ 运行中
|
||||
Nginx服务: ✅ 运行中
|
||||
443端口监听: ✅ 正常
|
||||
|
||||
# NAS服务器
|
||||
Docker容器: ✅ 运行中
|
||||
本地4320端口: ✅ 正常响应
|
||||
FRPC客户端: ⚠️ 有配置错误
|
||||
```
|
||||
|
||||
### 第4步:FRP穿透检查
|
||||
```bash
|
||||
# 测试公网服务器localhost:2320
|
||||
curl http://localhost:2320
|
||||
❌ 无响应!
|
||||
```
|
||||
|
||||
**🔴 问题定位:FRP隧道未正常工作**
|
||||
|
||||
### 第5步:FRPC日志检查
|
||||
```
|
||||
FRPC日志显示:
|
||||
json: unknown field "maxPoolCount"
|
||||
json: unknown field "maxPoolCount"
|
||||
...(重复多次)
|
||||
```
|
||||
|
||||
**🎯 根本原因找到:FRPS配置中的 `max_pool_count` 字段与新版FRPC不兼容!**
|
||||
|
||||
---
|
||||
|
||||
## 🔧 问题根本原因
|
||||
|
||||
### 版本兼容性问题
|
||||
|
||||
**FRPS(服务器端)**:
|
||||
- 版本:较旧版本
|
||||
- 配置格式:`.ini` (已废弃)
|
||||
- 配置项:`max_pool_count = 50`
|
||||
|
||||
**FRPC(客户端)**:
|
||||
- 版本:新版本(Docker镜像 `snowdreamtech/frpc:latest`)
|
||||
- 配置格式:`.toml` (新格式)
|
||||
- 不支持:`maxPoolCount` 字段(已在新版本中废弃或改名)
|
||||
|
||||
### 配置冲突链路
|
||||
|
||||
```
|
||||
FRPS服务器 (配置中有 max_pool_count)
|
||||
↓
|
||||
通过控制连接发送配置给FRPC
|
||||
↓
|
||||
FRPC接收到包含 maxPoolCount 的JSON配置
|
||||
↓
|
||||
❌ FRPC解析失败:unknown field "maxPoolCount"
|
||||
↓
|
||||
虽然有错误,但不影响基本连接建立
|
||||
↓
|
||||
但导致隧道不稳定或部分功能异常
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 修复步骤
|
||||
|
||||
#### 1. 备份原配置
|
||||
```bash
|
||||
cp /etc/frp/frps.ini /etc/frp/frps.ini.bak
|
||||
```
|
||||
|
||||
#### 2. 移除不兼容字段
|
||||
```bash
|
||||
sed -i '/max_pool_count/d' /etc/frp/frps.ini
|
||||
```
|
||||
|
||||
#### 3. 重启FRPS服务
|
||||
```bash
|
||||
systemctl restart frps
|
||||
```
|
||||
|
||||
#### 4. 重启FRPC客户端
|
||||
```bash
|
||||
docker restart frpc-youyidian
|
||||
```
|
||||
|
||||
#### 5. 验证修复
|
||||
```bash
|
||||
curl -I https://3d.yydd.ai
|
||||
✅ HTTP/2 200 OK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 修复后状态
|
||||
|
||||
### 访问测试结果
|
||||
|
||||
**HTTP访问**:
|
||||
```
|
||||
curl -I http://3d.yydd.ai
|
||||
✅ HTTP/1.1 301 → 自动跳转到HTTPS
|
||||
✅ HTTP/2 307 → 应用重定向到/login
|
||||
✅ HTTP/2 200 → 页面成功加载
|
||||
```
|
||||
|
||||
**HTTPS访问**:
|
||||
```
|
||||
curl -I https://3d.yydd.ai
|
||||
✅ HTTP/2 307 → 重定向到/login
|
||||
✅ HTTP/2 200 → 页面成功加载
|
||||
✅ SSL/TLS加密:正常
|
||||
✅ HTTP/2协议:正常
|
||||
```
|
||||
|
||||
### 服务状态
|
||||
|
||||
| 组件 | 状态 | 详情 |
|
||||
|------|------|------|
|
||||
| 网络连通性 | ✅ 正常 | 6ms延迟,0%丢包 |
|
||||
| FRPS服务 | ✅ 运行中 | 无错误日志 |
|
||||
| FRPC客户端 | ✅ 运行中 | 隧道正常工作 |
|
||||
| Nginx代理 | ✅ 正常 | 正确转发请求 |
|
||||
| SSL证书 | ✅ 有效 | Let's Encrypt |
|
||||
| HTTP→HTTPS | ✅ 正常 | 自动重定向 |
|
||||
| Docker容器 | ✅ 运行中 | 应用正常响应 |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 遗留警告(可忽略)
|
||||
|
||||
FRPC日志中仍显示:
|
||||
```
|
||||
json: unknown field "maxPoolCount"
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 这是旧的日志消息,缓存在Docker日志中
|
||||
- 不影响当前功能
|
||||
- 下次容器重建时会自动清除
|
||||
- **实际隧道已正常工作**
|
||||
|
||||
**验证方法**:
|
||||
```bash
|
||||
# 查看新日志(如果需要)
|
||||
docker logs -f frpc-youyidian --since 1m
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 问题影响分析
|
||||
|
||||
### 影响时间线
|
||||
|
||||
```
|
||||
时间未知 - 2025-11-05 21:30: FRP隧道异常,网站无法访问
|
||||
2025-11-05 21:30 - 21:37: 问题诊断和修复
|
||||
2025-11-05 21:37 至今: 网站恢复正常
|
||||
```
|
||||
|
||||
### 影响范围
|
||||
|
||||
- ✅ **已解决**:网站无法访问的问题
|
||||
- ✅ **已解决**:FRP隧道稳定性问题
|
||||
- ✅ **已解决**:HTTP/HTTPS访问正常
|
||||
|
||||
### 用户影响
|
||||
|
||||
- **影响用户**:所有访问 3d.yydd.ai 的用户
|
||||
- **影响功能**:完全无法访问网站
|
||||
- **业务影响**:网站暂时下线
|
||||
- **修复后**:所有功能恢复正常
|
||||
|
||||
---
|
||||
|
||||
## 📚 技术细节
|
||||
|
||||
### 修改前的FRPS配置(片段)
|
||||
```ini
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 7000
|
||||
max_pool_count = 50 ← 此行导致问题
|
||||
```
|
||||
|
||||
### 修改后的FRPS配置(片段)
|
||||
```ini
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 7000
|
||||
# max_pool_count已移除
|
||||
```
|
||||
|
||||
### FRPC配置(正常,未修改)
|
||||
```toml
|
||||
serverAddr = "180.97.221.140"
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "youyidian-3d"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 4320
|
||||
remotePort = 2320
|
||||
|
||||
[auth]
|
||||
method = "token"
|
||||
token = "LiuYunKu_NAS_2024_SecureToken"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔮 预防措施
|
||||
|
||||
### 1. 版本兼容性管理
|
||||
- ✅ 建议FRPS升级到新版本并使用TOML配置
|
||||
- ✅ 或FRPC降级到与当前FRPS兼容的版本
|
||||
- ✅ 锁定FRPC Docker镜像版本,避免自动更新
|
||||
|
||||
### 2. 监控告警
|
||||
建议配置以下监控:
|
||||
```bash
|
||||
# 网站可用性监控
|
||||
每分钟检查: curl -f https://3d.yydd.ai
|
||||
|
||||
# FRP隧道监控
|
||||
检查FRPC日志中的ERROR级别错误
|
||||
|
||||
# 服务状态监控
|
||||
systemctl status frps
|
||||
docker ps | grep frpc-youyidian
|
||||
```
|
||||
|
||||
### 3. 配置备份
|
||||
```bash
|
||||
# 定期备份FRPS配置
|
||||
cp /etc/frp/frps.ini /backup/frps.ini.$(date +%Y%m%d)
|
||||
|
||||
# 定期备份FRPC配置
|
||||
cp /mnt/tank/apps/frp/frpc.toml /backup/frpc.toml.$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
- [x] HTTP访问正常(80端口)
|
||||
- [x] HTTPS访问正常(443端口)
|
||||
- [x] HTTP自动跳转HTTPS
|
||||
- [x] SSL证书有效
|
||||
- [x] HTTP/2协议启用
|
||||
- [x] 应用页面正常加载
|
||||
- [x] FRPS服务运行正常
|
||||
- [x] FRPC客户端运行正常
|
||||
- [x] FRP隧道工作正常
|
||||
- [x] Nginx反向代理正常
|
||||
- [x] Docker容器运行正常
|
||||
|
||||
---
|
||||
|
||||
## 📝 经验总结
|
||||
|
||||
### 问题原因
|
||||
1. **版本不匹配**:旧版FRPS配置与新版FRPC不兼容
|
||||
2. **配置迁移不完整**:FRPS仍使用废弃的.ini格式和旧字段
|
||||
|
||||
### 解决方法
|
||||
1. **移除废弃配置**:删除不兼容的 `max_pool_count` 字段
|
||||
2. **重启服务**:使新配置生效
|
||||
|
||||
### 教训
|
||||
1. ⚠️ Docker镜像使用 `latest` 标签会自动更新,可能导致兼容性问题
|
||||
2. ⚠️ FRP服务器和客户端版本应保持同步
|
||||
3. ⚠️ 配置文件格式(.ini vs .toml)需要注意兼容性
|
||||
4. ✅ 及时查看日志可以快速定位问题
|
||||
5. ✅ 备份配置文件后再修改
|
||||
|
||||
---
|
||||
|
||||
## 🎉 最终结果
|
||||
|
||||
**✅ 3d.yydd.ai 完全恢复正常访问!**
|
||||
|
||||
- ✅ HTTP访问:正常(自动跳转HTTPS)
|
||||
- ✅ HTTPS访问:正常
|
||||
- ✅ SSL加密:正常
|
||||
- ✅ HTTP/2:正常
|
||||
- ✅ 钉钉登录:可用
|
||||
- ✅ 素材管理:可用
|
||||
- ✅ 大文件上传:支持(10GB)
|
||||
|
||||
**所有功能完全恢复!** 🚀
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**:2025-11-05 21:40
|
||||
**修复工程师**:AI Assistant
|
||||
**问题级别**:P0(网站完全不可用)
|
||||
**修复状态**:✅ 已完全解决
|
||||
|
||||
|
||||
@ -1,231 +0,0 @@
|
||||
# AI聊天助手使用指南
|
||||
|
||||
## 🎯 功能介绍
|
||||
|
||||
AI聊天助手是一个集成了Google Gemini AI的智能对话工具,支持文字+图片多模态交互,可以进行图片编辑、内容生成等多种操作。
|
||||
|
||||
## 📦 功能特性
|
||||
|
||||
### ✅ 图片发送功能
|
||||
- **点击上传**: 点击图片图标选择文件
|
||||
- **拖拽上传**: 拖拽图片到聊天窗口
|
||||
- **粘贴上传**: 直接粘贴剪贴板中的图片(Ctrl/Cmd+V)
|
||||
- **多图支持**: 一次最多发送5张图片
|
||||
- **实时预览**: 上传后立即显示预览
|
||||
- **删除功能**: 可以删除已上传的图片
|
||||
|
||||
### ✅ 图片接收功能
|
||||
- **AI生成图片**: 支持接收AI生成的图片
|
||||
- **Base64图片**: 自动解析Base64格式
|
||||
- **InlineData**: 支持Gemini API的inline data格式
|
||||
- **点击查看**: 点击图片在新窗口查看大图
|
||||
- **下载图片**: Hover图片显示下载按钮
|
||||
|
||||
### ✅ 对话功能
|
||||
- **流式对话**: 支持连续多轮对话
|
||||
- **上下文记忆**: 保留对话历史和图片信息
|
||||
- **实时响应**: 快速的AI响应
|
||||
- **多模态理解**: 同时理解文字和图片
|
||||
|
||||
## ⚙️ 配置步骤
|
||||
|
||||
### 1. 获取Gemini API Key
|
||||
|
||||
访问 [Google AI Studio](https://aistudio.google.com/app/apikey) 获取API密钥。
|
||||
|
||||
### 2. 配置环境变量
|
||||
|
||||
在项目根目录的 `.env` 文件中添加:
|
||||
|
||||
```env
|
||||
# Gemini AI Configuration
|
||||
GEMINI_API_KEY=your_gemini_api_key_here
|
||||
GEMINI_API_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
||||
GEMINI_MODEL=gemini-2.0-flash-exp
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
- `GEMINI_API_KEY`: 你的Gemini API密钥(必填)
|
||||
- `GEMINI_API_BASE_URL`: API基础URL(可选,默认值如上)
|
||||
- `GEMINI_MODEL`: 使用的模型名称(可选,默认:gemini-2.0-flash-exp)
|
||||
|
||||
**可用模型:**
|
||||
- `gemini-2.0-flash-exp` - 最新实验版(推荐)
|
||||
- `gemini-1.5-pro` - 专业版
|
||||
- `gemini-1.5-flash` - 快速版
|
||||
|
||||
### 3. 启动项目
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
### 基本对话
|
||||
|
||||
1. 点击右下角的紫色AI助手按钮
|
||||
2. 在输入框输入文字
|
||||
3. 按Enter发送(Shift+Enter换行)
|
||||
|
||||
### 发送图片
|
||||
|
||||
**方式1:点击上传**
|
||||
1. 点击输入框左侧的图片图标
|
||||
2. 选择要上传的图片
|
||||
3. 可以继续添加更多图片(最多5张)
|
||||
4. 输入描述文字(可选)
|
||||
5. 点击发送
|
||||
|
||||
**方式2:拖拽上传**
|
||||
1. 打开聊天窗口
|
||||
2. 直接拖拽图片到聊天区域
|
||||
3. 松开鼠标完成上传
|
||||
4. 输入描述文字(可选)
|
||||
5. 点击发送
|
||||
|
||||
**方式3:粘贴上传**
|
||||
1. 复制图片到剪贴板
|
||||
2. 在聊天窗口按 Ctrl+V (Windows) 或 Cmd+V (Mac)
|
||||
3. 图片自动添加到预览区
|
||||
4. 输入描述文字(可选)
|
||||
5. 点击发送
|
||||
|
||||
### 查看和下载AI返回的图片
|
||||
|
||||
1. AI返回的图片会显示在消息气泡中
|
||||
2. **点击图片** - 在新窗口查看大图
|
||||
3. **Hover图片** - 显示下载按钮
|
||||
4. **点击下载** - 保存图片到本地
|
||||
|
||||
## 💡 使用技巧
|
||||
|
||||
### 图片编辑示例
|
||||
|
||||
```
|
||||
"请帮我把这张图片的背景改成蓝色"
|
||||
"给这张图片添加一个卡通滤镜"
|
||||
"帮我去除图片中的文字"
|
||||
"把这张图片转换成黑白的"
|
||||
```
|
||||
|
||||
### 图片生成示例
|
||||
|
||||
```
|
||||
"生成一张日落海滩的图片"
|
||||
"创建一个可爱的卡通猫咪"
|
||||
"设计一个科技感的logo"
|
||||
```
|
||||
|
||||
### 图片分析示例
|
||||
|
||||
```
|
||||
"这张图片里有什么?"
|
||||
"帮我识别图片中的文字"
|
||||
"分析这张图片的构图"
|
||||
"这是什么品种的狗?"
|
||||
```
|
||||
|
||||
## 📊 技术架构
|
||||
|
||||
```
|
||||
Frontend (AIChatWidget)
|
||||
↓
|
||||
API Route (/api/ai-chat)
|
||||
↓
|
||||
Gemini AI API
|
||||
```
|
||||
|
||||
### 数据流程
|
||||
|
||||
1. **图片上传** → Base64编码 → 前端预览
|
||||
2. **发送消息** → 构建对话历史 → 调用API
|
||||
3. **API处理** → 转换格式 → 调用Gemini
|
||||
4. **接收响应** → 解析图片/文字 → 前端显示
|
||||
|
||||
## 🔧 故障排查
|
||||
|
||||
### 常见问题
|
||||
|
||||
**Q: 发送消息时提示"未授权"**
|
||||
A: 请确保已登录系统
|
||||
|
||||
**Q: 提示"Gemini API未配置"**
|
||||
A: 检查.env文件中的GEMINI_API_KEY是否正确设置
|
||||
|
||||
**Q: 图片上传后无法发送**
|
||||
A: 检查图片大小,建议单张图片不超过4MB
|
||||
|
||||
**Q: AI响应很慢**
|
||||
A: 可能是网络问题或API限制,请稍后重试
|
||||
|
||||
**Q: 图片无法显示**
|
||||
A: 检查浏览器控制台是否有错误信息
|
||||
|
||||
### API错误代码
|
||||
|
||||
- **401**: API密钥无效或过期
|
||||
- **429**: 请求频率过高,请稍后重试
|
||||
- **500**: 服务器错误,请联系管理员
|
||||
|
||||
## 🎨 自定义配置
|
||||
|
||||
### 修改最大图片数量
|
||||
|
||||
编辑 `components/AIChatWidget.tsx`:
|
||||
|
||||
```typescript
|
||||
// 将5改为你想要的数量
|
||||
setImages(prev => [...prev, ...files].slice(0, 5))
|
||||
```
|
||||
|
||||
### 修改模型参数
|
||||
|
||||
编辑 `app/api/ai-chat/route.ts`:
|
||||
|
||||
```typescript
|
||||
generationConfig: {
|
||||
temperature: 1, // 创造性(0-2)
|
||||
topK: 40, // 采样范围
|
||||
topP: 0.95, // 核采样
|
||||
maxOutputTokens: 8192 // 最大输出长度
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **API配额**: Gemini API有使用限制,请合理使用
|
||||
2. **图片大小**: 建议单张图片不超过4MB
|
||||
3. **隐私安全**: 不要上传敏感或私人图片
|
||||
4. **网络要求**: 需要稳定的网络连接
|
||||
5. **浏览器兼容**: 推荐使用Chrome、Firefox、Edge等现代浏览器
|
||||
|
||||
## 🆕 更新日志
|
||||
|
||||
### v1.0.0 (2025-10-27)
|
||||
- ✅ 初始版本发布
|
||||
- ✅ 支持文字+图片多模态对话
|
||||
- ✅ 集成Gemini AI API
|
||||
- ✅ 支持拖拽、粘贴上传图片
|
||||
- ✅ 支持图片查看和下载
|
||||
- ✅ 对话历史管理
|
||||
- ✅ 黑夜模式支持
|
||||
|
||||
## 🔗 相关链接
|
||||
|
||||
- [Gemini API文档](https://ai.google.dev/docs)
|
||||
- [项目GitHub](https://github.com/your-repo)
|
||||
- [问题反馈](https://github.com/your-repo/issues)
|
||||
|
||||
## 👥 管理员账号
|
||||
|
||||
```
|
||||
用户名: admin
|
||||
邮箱: admin@youyidian3d.com
|
||||
密码: admin123456
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**享受AI助手带来的强大功能吧!🎉**
|
||||
|
||||
@ -1,420 +0,0 @@
|
||||
# 素材卡片增强功能说明 v2.2
|
||||
|
||||
## 📅 更新日期:2025-10-17
|
||||
|
||||
---
|
||||
|
||||
## ✨ 新增功能概览
|
||||
|
||||
### 1. 📊 下载次数统计
|
||||
- **自动计数**:每次下载自动增加计数
|
||||
- **实时显示**:卡片上显示下载次数图标和数字
|
||||
- **数据库持久化**:所有统计数据保存在数据库
|
||||
|
||||
### 2. ❤️ 点赞功能
|
||||
- **一键点赞**:点击红心按钮即可点赞
|
||||
- **取消点赞**:再次点击可取消点赞
|
||||
- **唯一性**:每个用户对每个素材只能点赞一次
|
||||
- **实时反馈**:点赞后立即更新UI状态
|
||||
|
||||
### 3. 🎨 卡片信息优化
|
||||
- **SPU标签**:图片上叠加显示SPU
|
||||
- **彩色标签**:店铺(蓝色)和场景类别(绿色)标签
|
||||
- **文件信息**:显示完整的压缩包文件名
|
||||
- **统计图标**:下载和点赞次数带图标显示
|
||||
- **视觉优化**:更现代的布局和配色
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 数据库变更
|
||||
|
||||
### Material模型新增字段
|
||||
|
||||
```prisma
|
||||
model Material {
|
||||
// ... 原有字段 ...
|
||||
downloadCount Int @default(0) // 下载次数
|
||||
likeCount Int @default(0) // 点赞次数
|
||||
likes MaterialLike[] // 点赞关系
|
||||
|
||||
@@index([downloadCount])
|
||||
@@index([likeCount])
|
||||
}
|
||||
```
|
||||
|
||||
### 新增MaterialLike模型
|
||||
|
||||
```prisma
|
||||
model MaterialLike {
|
||||
id String @id @default(uuid())
|
||||
materialId String
|
||||
userId String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([materialId, userId]) // 确保一个用户只能点赞一次
|
||||
@@map("material_likes")
|
||||
@@index([materialId])
|
||||
@@index([userId])
|
||||
}
|
||||
```
|
||||
|
||||
### User模型更新
|
||||
|
||||
```prisma
|
||||
model User {
|
||||
// ... 原有字段 ...
|
||||
materials Material[]
|
||||
likes MaterialLike[] // 新增点赞关系
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 API更新
|
||||
|
||||
### 1. GET /api/materials
|
||||
**更新内容**:返回数据包含点赞状态
|
||||
|
||||
**响应数据**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"productModel": "SPU-001",
|
||||
"categoryName": "天猫旗舰店",
|
||||
"sceneName": "客厅",
|
||||
"authorName": "设计师A",
|
||||
"zipFileName": "zip-xxxx-素材名称.zip",
|
||||
"downloadCount": 25,
|
||||
"likeCount": 10,
|
||||
"isLiked": true, // ← 新增:当前用户是否点赞
|
||||
// ... 其他字段
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**逻辑改进**:
|
||||
- 获取session判断是否登录
|
||||
- 如果登录,查询当前用户的所有点赞记录
|
||||
- 为每个素材添加`isLiked`字段
|
||||
|
||||
### 2. POST /api/materials/like
|
||||
**新增API**:处理点赞/取消点赞
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"materialId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"liked": true, // true=点赞成功,false=取消点赞
|
||||
"message": "点赞成功"
|
||||
}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- 检查用户是否已点赞
|
||||
- 如果未点赞:创建点赞记录,likeCount +1
|
||||
- 如果已点赞:删除点赞记录,likeCount -1
|
||||
- 使用唯一索引确保数据一致性
|
||||
|
||||
### 3. GET /api/materials/download
|
||||
**更新内容**:自动增加下载次数
|
||||
|
||||
**新增逻辑**:
|
||||
```typescript
|
||||
// 增加下载次数
|
||||
await prisma.material.update({
|
||||
where: { id },
|
||||
data: {
|
||||
downloadCount: {
|
||||
increment: 1
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI设计详解
|
||||
|
||||
### 卡片布局结构
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ 预览图 (200px高度) │
|
||||
│ ┌───────────────┐ │
|
||||
│ │ SPU标签(左上) │ │
|
||||
│ └───────────────┘ │
|
||||
├─────────────────────────────┤
|
||||
│ 【店铺标签】【场景标签】 │
|
||||
│ │
|
||||
│ 作者:xxx │
|
||||
│ 文件:xxx.zip │
|
||||
│ 时间:2025-10-17 12:00 │
|
||||
│ │
|
||||
│ ───────────────────────── │
|
||||
│ 📥 25 ❤️ 10 │
|
||||
│ ───────────────────────── │
|
||||
│ │
|
||||
│ [❤️] [📥 下载] │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### 配色方案
|
||||
|
||||
#### 店铺标签
|
||||
- **亮色模式**:蓝色背景 (#DBEAFE) + 蓝色文字 (#1D4ED8)
|
||||
- **暗色模式**:深蓝背景 (blue-900/30) + 浅蓝文字 (#93C5FD)
|
||||
|
||||
#### 场景类别标签
|
||||
- **亮色模式**:绿色背景 (#D1FAE5) + 绿色文字 (#047857)
|
||||
- **暗色模式**:深绿背景 (green-900/30) + 浅绿文字 (#6EE7B7)
|
||||
|
||||
#### 点赞按钮
|
||||
- **未点赞**:灰色背景,空心图标
|
||||
- **已点赞**:红色背景 (#EF4444),实心图标
|
||||
|
||||
---
|
||||
|
||||
## 💡 功能特性
|
||||
|
||||
### 点赞功能
|
||||
|
||||
#### 1. 防重复点赞
|
||||
- 数据库层面:`@@unique([materialId, userId])`
|
||||
- 应用层面:查询后再执行操作
|
||||
|
||||
#### 2. 状态同步
|
||||
- 点赞后自动刷新列表
|
||||
- 实时更新点赞数量和状态
|
||||
- UI即时反馈
|
||||
|
||||
#### 3. 视觉反馈
|
||||
```typescript
|
||||
// 已点赞
|
||||
className="bg-red-500 text-white hover:bg-red-600"
|
||||
fill="currentColor" // 实心
|
||||
|
||||
// 未点赞
|
||||
className="bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
fill="none" // 空心
|
||||
```
|
||||
|
||||
### 下载统计
|
||||
|
||||
#### 1. 自动计数
|
||||
- 每次成功下载文件前计数 +1
|
||||
- 使用数据库increment操作确保原子性
|
||||
- 不受前端刷新影响
|
||||
|
||||
#### 2. 实时显示
|
||||
- 下载后立即刷新列表
|
||||
- 显示更新后的下载次数
|
||||
|
||||
---
|
||||
|
||||
## 📱 响应式设计
|
||||
|
||||
### 网格布局
|
||||
- **Mobile (sm)**:1列
|
||||
- **Tablet (lg)**:3列
|
||||
- **Desktop (xl)**:4列
|
||||
|
||||
### 卡片尺寸
|
||||
- **预览图高度**:200px
|
||||
- **卡片宽度**:自适应 (grid布局)
|
||||
- **间距**:gap-6 (1.5rem)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 用户交互流程
|
||||
|
||||
### 点赞流程
|
||||
```
|
||||
用户点击点赞按钮
|
||||
↓
|
||||
发送POST请求到 /api/materials/like
|
||||
↓
|
||||
服务器检查点赞状态
|
||||
↓
|
||||
├─ 未点赞 → 创建记录 + likeCount++
|
||||
└─ 已点赞 → 删除记录 + likeCount--
|
||||
↓
|
||||
返回新状态
|
||||
↓
|
||||
前端刷新列表
|
||||
↓
|
||||
UI更新(图标填充、颜色、数量)
|
||||
```
|
||||
|
||||
### 下载流程
|
||||
```
|
||||
用户点击下载按钮
|
||||
↓
|
||||
发送GET请求到 /api/materials/download
|
||||
↓
|
||||
服务器 downloadCount++
|
||||
↓
|
||||
读取文件并返回
|
||||
↓
|
||||
浏览器下载文件
|
||||
↓
|
||||
前端刷新列表(显示新的下载次数)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 用户体验提升
|
||||
|
||||
### Before vs After
|
||||
|
||||
| 功能 | 之前 | 现在 |
|
||||
|------|------|------|
|
||||
| 信息显示 | 基本信息 | **完整信息**(SPU、店铺、场景、文件名) |
|
||||
| 统计数据 | 无 | **下载次数**、**点赞次数** |
|
||||
| 互动功能 | 仅下载 | 下载 + **点赞** |
|
||||
| 视觉设计 | 简单列表 | **彩色标签**、**图标**、**分层布局** |
|
||||
| 用户反馈 | 下载后无变化 | **实时更新统计数据** |
|
||||
|
||||
### 数据洞察
|
||||
- ✅ 了解哪些素材最受欢迎(点赞数)
|
||||
- ✅ 了解哪些素材使用最多(下载数)
|
||||
- ✅ 用户可以收藏喜欢的素材(点赞)
|
||||
- ✅ 提供数据支持运营决策
|
||||
|
||||
---
|
||||
|
||||
## 🔧 部署步骤
|
||||
|
||||
### 1. 数据库迁移
|
||||
|
||||
```bash
|
||||
ssh root@192.168.58.20
|
||||
cd /mnt/tank/apps/youyidian-3d
|
||||
|
||||
# 运行数据库迁移
|
||||
docker compose exec app npx prisma db push
|
||||
```
|
||||
|
||||
**迁移内容**:
|
||||
- 添加 `downloadCount` 字段(默认0)
|
||||
- 添加 `likeCount` 字段(默认0)
|
||||
- 创建 `material_likes` 表
|
||||
- 创建索引
|
||||
|
||||
### 2. 重新部署代码
|
||||
|
||||
```bash
|
||||
# 本地打包
|
||||
cd /Users/lanmu/Desktop/gongxiangp2
|
||||
./update-deploy.sh
|
||||
```
|
||||
|
||||
或手动部署:
|
||||
|
||||
```bash
|
||||
# 打包
|
||||
tar -czf update.tar.gz --exclude='node_modules' --exclude='.next' .
|
||||
|
||||
# 上传
|
||||
scp update.tar.gz root@192.168.58.20:/mnt/tank/apps/youyidian-3d/
|
||||
|
||||
# 部署
|
||||
ssh root@192.168.58.20
|
||||
cd /mnt/tank/apps/youyidian-3d
|
||||
tar -xzf update.tar.gz
|
||||
docker compose down
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
### 3. 初始化现有数据(可选)
|
||||
|
||||
如果有旧数据,下载和点赞次数会默认为0。可以手动初始化:
|
||||
|
||||
```bash
|
||||
docker compose exec -T app npx prisma db execute --schema=/app/prisma/schema.prisma --stdin <<< "
|
||||
-- 所有现有素材设置默认值
|
||||
UPDATE materials
|
||||
SET \"downloadCount\" = 0, \"likeCount\" = 0
|
||||
WHERE \"downloadCount\" IS NULL OR \"likeCount\" IS NULL;
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 测试清单
|
||||
|
||||
### 功能测试
|
||||
- [ ] 查看首页,卡片显示完整信息
|
||||
- [ ] 点击点赞按钮,按钮变红
|
||||
- [ ] 再次点击,取消点赞,按钮变灰
|
||||
- [ ] 点赞次数实时更新
|
||||
- [ ] 下载素材,下载次数 +1
|
||||
- [ ] 刷新页面,点赞状态保持
|
||||
- [ ] 多个用户对同一素材点赞,计数正确
|
||||
|
||||
### UI测试
|
||||
- [ ] 标签颜色正确显示
|
||||
- [ ] SPU标签在图片左上角
|
||||
- [ ] 图标正常显示
|
||||
- [ ] 黑暗模式下样式正常
|
||||
- [ ] 响应式布局正常
|
||||
|
||||
### 边界测试
|
||||
- [ ] 未登录用户点赞(应提示登录)
|
||||
- [ ] 重复点赞(应正常切换)
|
||||
- [ ] 大量点赞/下载(性能正常)
|
||||
- [ ] 网络异常(显示错误提示)
|
||||
|
||||
---
|
||||
|
||||
## 📊 数据示例
|
||||
|
||||
### 热门素材排行
|
||||
|
||||
**按点赞数排序**:
|
||||
```sql
|
||||
SELECT "productModel", "categoryName", "likeCount"
|
||||
FROM materials
|
||||
ORDER BY "likeCount" DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
**按下载数排序**:
|
||||
```sql
|
||||
SELECT "productModel", "categoryName", "downloadCount"
|
||||
FROM materials
|
||||
ORDER BY "downloadCount" DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 效果展示
|
||||
|
||||
### 卡片信息完整度
|
||||
✅ **之前**:仅显示基本信息
|
||||
✅ **现在**:
|
||||
- SPU标签(图片上)
|
||||
- 店铺标签(蓝色)
|
||||
- 场景类别标签(绿色)
|
||||
- 作者信息
|
||||
- 文件名
|
||||
- 创建时间
|
||||
- 下载次数(带图标)
|
||||
- 点赞次数(带图标)
|
||||
- 点赞按钮(可交互)
|
||||
- 下载按钮(带图标)
|
||||
|
||||
---
|
||||
|
||||
**功能完成!准备部署!** 🚀
|
||||
|
||||
@ -1,310 +0,0 @@
|
||||
# 并发上传冲突修复报告
|
||||
|
||||
## 问题描述
|
||||
|
||||
用户反馈:多人同时上传时会出现错误。
|
||||
|
||||
## 问题分析
|
||||
|
||||
### 根本原因
|
||||
|
||||
在 `app/api/materials/upload/route.ts` 中,所有文件名都使用 `Date.now()` 生成唯一标识:
|
||||
|
||||
```typescript
|
||||
// 旧代码示例
|
||||
const tempPsdFileName = `temp-psd-${Date.now()}-${zipFile.name}`
|
||||
const zipFileName = `zip-${Date.now()}-${zipFile.name}`
|
||||
const previewFileName = `preview-${Date.now()}-${processedFileName}`
|
||||
```
|
||||
|
||||
### 并发问题
|
||||
|
||||
当多个用户在**同一毫秒**内上传文件时:
|
||||
- ✗ 文件名可能相同
|
||||
- ✗ 文件会相互覆盖
|
||||
- ✗ 导致上传失败或数据混乱
|
||||
- ✗ S3 上传冲突
|
||||
- ✗ 本地文件系统冲突
|
||||
|
||||
### 触发场景
|
||||
|
||||
```
|
||||
时间: 2025-10-26 17:30:45.123
|
||||
|
||||
用户A上传: temp-psd-1730012445123-design.psd
|
||||
用户B上传: temp-psd-1730012445123-design.psd ← 文件名冲突!
|
||||
|
||||
结果: 其中一个上传失败或覆盖另一个文件
|
||||
```
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 1. 新增唯一文件名生成函数
|
||||
|
||||
```typescript
|
||||
import { randomBytes } from 'crypto'
|
||||
|
||||
// 生成唯一文件名(避免并发冲突)
|
||||
function generateUniqueFileName(prefix: string, ext: string): string {
|
||||
const timestamp = Date.now()
|
||||
const randomId = randomBytes(8).toString('hex') // 16个字符的随机字符串
|
||||
return `${prefix}-${timestamp}-${randomId}${ext}`
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修复所有文件名生成位置
|
||||
|
||||
#### 临时 PSD 文件
|
||||
```typescript
|
||||
// 修复前
|
||||
const tempPsdFileName = `temp-psd-${Date.now()}-${zipFile.name}`
|
||||
|
||||
// 修复后
|
||||
const tempPsdFileName = generateUniqueFileName('temp-psd', `.${zipFile.name.split('.').pop()}`)
|
||||
```
|
||||
|
||||
#### 临时预览文件
|
||||
```typescript
|
||||
// 修复前
|
||||
const tempPreviewPath = path.join(uploadDir, `temp-preview-${Date.now()}.jpg`)
|
||||
|
||||
// 修复后
|
||||
const tempPreviewPath = path.join(uploadDir, generateUniqueFileName('temp-preview', '.jpg'))
|
||||
```
|
||||
|
||||
#### S3 预览文件(自动生成)
|
||||
```typescript
|
||||
// 修复前
|
||||
previewFileName = `preview-${Date.now()}-generated.webp`
|
||||
|
||||
// 修复后
|
||||
previewFileName = generateUniqueFileName('preview', '-generated.webp')
|
||||
```
|
||||
|
||||
#### S3 预览文件(手动上传)
|
||||
```typescript
|
||||
// 修复前
|
||||
previewFileName = `preview-${Date.now()}-${processedFileName}`
|
||||
|
||||
// 修复后
|
||||
const fileExt = path.extname(processedFileName)
|
||||
previewFileName = generateUniqueFileName('preview', fileExt)
|
||||
```
|
||||
|
||||
#### 素材文件(压缩包/PSD)
|
||||
```typescript
|
||||
// 修复前
|
||||
const zipFileName = `zip-${Date.now()}-${zipFile.name}`
|
||||
|
||||
// 修复后
|
||||
const zipFileExt = path.extname(zipFile.name)
|
||||
const zipFileName = generateUniqueFileName('material', zipFileExt)
|
||||
```
|
||||
|
||||
### 3. 文件名格式对比
|
||||
|
||||
#### 修复前
|
||||
```
|
||||
temp-psd-1730012445123-design.psd
|
||||
preview-1730012445123-thumbnail.webp
|
||||
zip-1730012445123-model.zip
|
||||
```
|
||||
|
||||
#### 修复后
|
||||
```
|
||||
temp-psd-1730012445123-a1b2c3d4e5f6g7h8.psd
|
||||
preview-1730012445123-9i8j7k6l5m4n3o2p.webp
|
||||
material-1730012445123-1a2b3c4d5e6f7g8h.zip
|
||||
```
|
||||
|
||||
### 4. 唯一性保证
|
||||
|
||||
**组合方式:**
|
||||
- 时间戳:`Date.now()` (13位数字)
|
||||
- 随机ID:`randomBytes(8).toString('hex')` (16个字符)
|
||||
|
||||
**冲突概率:**
|
||||
- 同一毫秒内:1 / (2^64) ≈ 0.00000000000000005%
|
||||
- 实际上几乎不可能冲突
|
||||
|
||||
**优势:**
|
||||
- ✓ 支持高并发(数百个同时上传)
|
||||
- ✓ 无需数据库查询
|
||||
- ✓ 性能开销极小
|
||||
- ✓ 文件名可读性好(保留时间戳)
|
||||
|
||||
## 技术细节
|
||||
|
||||
### randomBytes 说明
|
||||
|
||||
```typescript
|
||||
import { randomBytes } from 'crypto'
|
||||
|
||||
// 生成8字节(64位)的随机数据
|
||||
const buffer = randomBytes(8)
|
||||
|
||||
// 转换为16进制字符串(16个字符)
|
||||
const hex = buffer.toString('hex')
|
||||
|
||||
// 示例输出
|
||||
'a1b2c3d4e5f6g7h8'
|
||||
'9i8j7k6l5m4n3o2p'
|
||||
'1a2b3c4d5e6f7g8h'
|
||||
```
|
||||
|
||||
### Node.js crypto 模块
|
||||
|
||||
- **内置模块**:无需额外安装
|
||||
- **加密安全**:使用系统随机数生成器
|
||||
- **高性能**:生成速度极快(微秒级)
|
||||
- **跨平台**:所有 Node.js 环境可用
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 1. 单人上传测试
|
||||
```bash
|
||||
# 应正常工作,与之前行为一致
|
||||
- 上传单个文件
|
||||
- 检查文件保存成功
|
||||
- 验证 S3 上传成功
|
||||
```
|
||||
|
||||
### 2. 并发上传测试
|
||||
```bash
|
||||
# 模拟多人同时上传
|
||||
- 10个用户同时上传不同文件
|
||||
- 检查所有上传都成功
|
||||
- 验证没有文件覆盖
|
||||
- 检查数据库记录完整
|
||||
```
|
||||
|
||||
### 3. 压力测试
|
||||
```bash
|
||||
# 测试极限并发
|
||||
- 50-100个用户同时上传
|
||||
- 监控服务器资源
|
||||
- 检查错误日志
|
||||
- 验证文件完整性
|
||||
```
|
||||
|
||||
## 部署说明
|
||||
|
||||
### 修改文件
|
||||
- `app/api/materials/upload/route.ts` - 上传 API 路由
|
||||
|
||||
### 依赖变化
|
||||
- **无新增依赖** - 使用 Node.js 内置 `crypto` 模块
|
||||
|
||||
### 数据库变化
|
||||
- **无数据库变更** - 只修改文件名生成逻辑
|
||||
|
||||
### 兼容性
|
||||
- ✓ 完全向后兼容
|
||||
- ✓ 不影响已上传文件
|
||||
- ✓ 新旧文件可以共存
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 1. 拉取最新代码
|
||||
```bash
|
||||
cd /mnt/PoolOne/apps/youyidian-3d
|
||||
git pull origin main
|
||||
```
|
||||
|
||||
### 2. 重新构建镜像
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose build --no-cache
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 3. 验证部署
|
||||
```bash
|
||||
# 查看日志
|
||||
docker compose logs -f app
|
||||
|
||||
# 测试上传
|
||||
# 访问系统并进行上传测试
|
||||
```
|
||||
|
||||
## 监控建议
|
||||
|
||||
### 查看上传日志
|
||||
```bash
|
||||
# 查看完整上传流程
|
||||
docker compose logs -f app | grep "\[Upload\]"
|
||||
|
||||
# 查看文件名生成
|
||||
docker compose logs app | grep "fileName"
|
||||
|
||||
# 查看错误
|
||||
docker compose logs app | grep "error"
|
||||
```
|
||||
|
||||
### 关键日志标识
|
||||
```
|
||||
[Upload] Starting upload process...
|
||||
[Upload] Temporary PSD saved: temp-psd-xxx-yyy.psd
|
||||
[Upload] File saved: material-xxx-yyy.zip
|
||||
[Upload] Upload completed successfully!
|
||||
```
|
||||
|
||||
## 性能影响
|
||||
|
||||
### 生成随机ID的性能
|
||||
```javascript
|
||||
// 基准测试
|
||||
console.time('generateUniqueFileName')
|
||||
for (let i = 0; i < 100000; i++) {
|
||||
generateUniqueFileName('test', '.jpg')
|
||||
}
|
||||
console.timeEnd('generateUniqueFileName')
|
||||
// 结果:约 200-300ms (10万次调用)
|
||||
// 单次调用:< 0.003ms (完全可以忽略)
|
||||
```
|
||||
|
||||
### 对上传流程的影响
|
||||
- **额外开销**:< 0.01ms per upload
|
||||
- **内存占用**:无额外占用
|
||||
- **CPU 使用**:可忽略不计
|
||||
|
||||
## 相关 Git 提交
|
||||
|
||||
```bash
|
||||
commit 030fb2a
|
||||
Author: AI Assistant
|
||||
Date: 2025-10-26
|
||||
|
||||
fix: Prevent concurrent upload conflicts with unique file names
|
||||
|
||||
- Add generateUniqueFileName function using crypto.randomBytes
|
||||
- Replace Date.now() with timestamp + random hex for all file names
|
||||
- Ensures unique file names even when multiple users upload simultaneously
|
||||
- Fixes: temp PSD files, temp preview files, S3 preview files, and material files
|
||||
- Resolves reported issue of upload errors with concurrent users
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
### 问题
|
||||
- 多人同时上传会因文件名冲突导致失败
|
||||
|
||||
### 原因
|
||||
- 使用 `Date.now()` 生成文件名,并发时可能重复
|
||||
|
||||
### 解决
|
||||
- 使用 `Date.now() + randomBytes(8)` 生成唯一文件名
|
||||
- 保证即使在同一毫秒内也不会冲突
|
||||
|
||||
### 效果
|
||||
- ✓ 支持任意数量用户同时上传
|
||||
- ✓ 零性能影响
|
||||
- ✓ 零额外依赖
|
||||
- ✓ 完全向后兼容
|
||||
|
||||
---
|
||||
|
||||
**修复时间:** 2025-10-26
|
||||
**修复版本:** v2.6.1
|
||||
**状态:** ✅ 已完成并推送
|
||||
|
||||
@ -1,369 +0,0 @@
|
||||
# 客户一键部署指南(Registry 版本)
|
||||
|
||||
## 🎯 目标
|
||||
|
||||
让其他公司能够**一条命令**完成部署,**无需源码**,直接从私有镜像仓库拉取镜像。
|
||||
|
||||
## ✅ 当前方案已实现
|
||||
|
||||
搭建完 Docker Registry 后,完全符合"一键部署"的目标:
|
||||
|
||||
1. ✅ **无需源码**:客户不需要源代码,只需要配置文件
|
||||
2. ✅ **一条命令**:`docker compose up -d` 即可完成部署
|
||||
3. ✅ **私有镜像**:从 Registry 拉取,代码不泄露
|
||||
4. ✅ **自动化**:自动配置数据库、MinIO、网络等
|
||||
|
||||
## 📦 客户部署流程
|
||||
|
||||
### 方案 A:最简单(推荐 ⭐⭐⭐)
|
||||
|
||||
**客户只需要 3 步:**
|
||||
|
||||
#### 步骤 1:下载部署包
|
||||
|
||||
提供压缩包,包含:
|
||||
- `docker-compose.prod.yml` - 生产配置
|
||||
- `.env.example` - 环境变量模板
|
||||
- `README.md` - 部署说明
|
||||
|
||||
#### 步骤 2:配置环境变量
|
||||
|
||||
```bash
|
||||
# 复制模板
|
||||
cp .env.example .env
|
||||
|
||||
# 编辑配置(只需修改必要的)
|
||||
nano .env
|
||||
```
|
||||
|
||||
**最小配置**(其他使用默认值):
|
||||
```bash
|
||||
# Registry 地址(必须)
|
||||
DOCKER_REGISTRY=45.249.208.118:5000
|
||||
|
||||
# 服务器 IP(必须)
|
||||
SERVER_IP=客户服务器IP
|
||||
|
||||
# 数据库密码(建议修改)
|
||||
POSTGRES_PASSWORD=客户设置的密码
|
||||
MINIO_ROOT_PASSWORD=客户设置的密码
|
||||
```
|
||||
|
||||
#### 步骤 3:一键启动
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
**完成!** 等待 1-2 分钟,访问 `http://客户服务器IP:4320`
|
||||
|
||||
---
|
||||
|
||||
### 方案 B:全自动安装(包含 Docker 安装)
|
||||
|
||||
如果客户服务器没有 Docker,提供自动安装脚本:
|
||||
|
||||
#### 步骤 1:下载部署包
|
||||
|
||||
#### 步骤 2:运行自动安装脚本
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
bash install-auto.sh
|
||||
|
||||
# Windows
|
||||
install-auto-windows.bat
|
||||
```
|
||||
|
||||
脚本会自动:
|
||||
- ✅ 检测并安装 Docker(如未安装)
|
||||
- ✅ 检测并安装 Docker Compose(如未安装)
|
||||
- ✅ 配置 Docker 允许不安全的 Registry
|
||||
- ✅ 创建配置文件
|
||||
- ✅ 拉取镜像
|
||||
- ✅ 启动服务
|
||||
- ✅ 初始化数据库
|
||||
|
||||
---
|
||||
|
||||
## 🔧 配置文件说明
|
||||
|
||||
### docker-compose.prod.yml(已配置好)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
# 从 Registry 拉取镜像(无需源码)
|
||||
image: ${DOCKER_REGISTRY:-45.249.208.118:5000}/flnxi-material:pro-${IMAGE_TAG:-latest}
|
||||
# ... 其他配置已自动设置
|
||||
```
|
||||
|
||||
### .env 文件(客户只需修改必要项)
|
||||
|
||||
```bash
|
||||
# ============================================
|
||||
# Registry 配置(必须)
|
||||
# ============================================
|
||||
DOCKER_REGISTRY=45.249.208.118:5000
|
||||
IMAGE_TAG=latest
|
||||
|
||||
# ============================================
|
||||
# 服务器配置(必须)
|
||||
# ============================================
|
||||
SERVER_IP=客户服务器IP地址
|
||||
APP_PORT=4320
|
||||
|
||||
# ============================================
|
||||
# 数据库配置(建议修改密码)
|
||||
# ============================================
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=客户设置的密码
|
||||
POSTGRES_DB=flnxi
|
||||
|
||||
# ============================================
|
||||
# MinIO 配置(建议修改密码)
|
||||
# ============================================
|
||||
MINIO_ROOT_USER=minioadmin
|
||||
MINIO_ROOT_PASSWORD=客户设置的密码
|
||||
S3_BUCKET=youyidian3d
|
||||
|
||||
# ============================================
|
||||
# 认证配置(建议修改)
|
||||
# ============================================
|
||||
NEXTAUTH_SECRET=客户生成的随机密钥
|
||||
NEXTAUTH_URL=http://${SERVER_IP}:${APP_PORT}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署包内容
|
||||
|
||||
### 最小部署包(推荐)
|
||||
|
||||
```
|
||||
deploy-package/
|
||||
├── docker-compose.prod.yml # 生产配置(已配置好)
|
||||
├── .env.example # 环境变量模板
|
||||
├── README.md # 部署说明
|
||||
└── install.sh # 快速安装脚本(可选)
|
||||
```
|
||||
|
||||
### 完整部署包(包含自动安装)
|
||||
|
||||
```
|
||||
deploy-package/
|
||||
├── docker-compose.prod.yml # 生产配置
|
||||
├── .env.example # 环境变量模板
|
||||
├── README.md # 部署说明
|
||||
├── install-auto.sh # 全自动安装(Linux)
|
||||
├── install-auto-windows.bat # 全自动安装(Windows)
|
||||
└── install.sh # 快速安装(已有 Docker)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 客户部署检查清单
|
||||
|
||||
### 部署前
|
||||
- [ ] 服务器已安装 Docker(或使用自动安装脚本)
|
||||
- [ ] 服务器已安装 Docker Compose(或使用自动安装脚本)
|
||||
- [ ] 服务器有至少 2GB 内存
|
||||
- [ ] 服务器有至少 20GB 磁盘空间
|
||||
- [ ] 服务器可以访问 Registry(45.249.208.118:5000)
|
||||
|
||||
### 部署中
|
||||
- [ ] 已配置 `.env` 文件
|
||||
- [ ] 已配置 `DOCKER_REGISTRY` 环境变量
|
||||
- [ ] 已配置 `SERVER_IP` 环境变量
|
||||
- [ ] 已修改数据库密码(生产环境)
|
||||
|
||||
### 部署后
|
||||
- [ ] 服务正常启动(`docker compose ps`)
|
||||
- [ ] 可以访问应用(`http://服务器IP:4320`)
|
||||
- [ ] 数据库连接正常
|
||||
- [ ] MinIO 存储正常
|
||||
- [ ] 可以上传素材
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Registry 访问配置
|
||||
|
||||
### 如果 Registry 需要认证
|
||||
|
||||
#### 方式 1:在 .env 中配置(推荐)
|
||||
|
||||
```bash
|
||||
# 添加到 .env 文件
|
||||
REGISTRY_USER=admin
|
||||
REGISTRY_PASSWORD=客户获取的密码
|
||||
```
|
||||
|
||||
#### 方式 2:在服务器上登录
|
||||
|
||||
```bash
|
||||
# 客户在服务器上执行
|
||||
docker login 45.249.208.118:5000 -u admin -p 密码
|
||||
```
|
||||
|
||||
### 如果 Registry 不需要认证(HTTP)
|
||||
|
||||
#### 配置 Docker 允许不安全的 Registry
|
||||
|
||||
```bash
|
||||
# 在服务器上配置
|
||||
cat > /etc/docker/daemon.json <<EOF
|
||||
{
|
||||
"insecure-registries": ["45.249.208.118:5000"]
|
||||
}
|
||||
EOF
|
||||
|
||||
# 重启 Docker
|
||||
systemctl restart docker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 客户部署文档模板
|
||||
|
||||
创建 `docs/guides/CUSTOMER_DEPLOYMENT_README.md` 供客户使用:
|
||||
|
||||
```markdown
|
||||
# FLNXI素材库 - 快速部署指南
|
||||
|
||||
## 🚀 一键部署(3 步完成)
|
||||
|
||||
### 步骤 1:配置环境变量
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
nano .env # 修改必要的配置
|
||||
```
|
||||
|
||||
**必须修改**:
|
||||
- `DOCKER_REGISTRY` - Registry 地址
|
||||
- `SERVER_IP` - 您的服务器 IP
|
||||
- `POSTGRES_PASSWORD` - 数据库密码(建议修改)
|
||||
- `MINIO_ROOT_PASSWORD` - 存储密码(建议修改)
|
||||
|
||||
### 步骤 2:配置 Docker(如果 Registry 使用 HTTP)
|
||||
|
||||
```bash
|
||||
# 允许不安全的 Registry
|
||||
cat > /etc/docker/daemon.json <<EOF
|
||||
{
|
||||
"insecure-registries": ["45.249.208.118:5000"]
|
||||
}
|
||||
EOF
|
||||
|
||||
# 重启 Docker
|
||||
systemctl restart docker
|
||||
```
|
||||
|
||||
### 步骤 3:启动服务
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
**完成!** 等待 1-2 分钟,访问 `http://您的服务器IP:4320`
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如有问题,请联系技术支持。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 与目标的一致性
|
||||
|
||||
### ✅ 已实现的目标
|
||||
|
||||
1. **一条命令部署** ✅
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
2. **无需源码** ✅
|
||||
- 客户不需要源代码
|
||||
- 只需要配置文件和镜像
|
||||
|
||||
3. **私有镜像** ✅
|
||||
- 从 Registry 拉取
|
||||
- 代码不泄露
|
||||
|
||||
4. **自动化配置** ✅
|
||||
- 自动配置数据库
|
||||
- 自动配置 MinIO
|
||||
- 自动配置网络
|
||||
|
||||
### 📊 对比
|
||||
|
||||
| 特性 | 当前方案(Registry) | 之前方案(tar 上传) |
|
||||
|------|---------------------|---------------------|
|
||||
| 部署命令 | `docker compose up -d` | 需要上传 tar + 加载 + 部署 |
|
||||
| 需要源码 | ❌ 不需要 | ✅ 需要 |
|
||||
| 代码保护 | ✅ 私有镜像 | ❌ 源码可见 |
|
||||
| 部署时间 | 1-2 分钟 | 5-10 分钟 |
|
||||
| 客户操作 | 3 步 | 10+ 步 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 下一步
|
||||
|
||||
1. ✅ **搭建 Registry**(已完成)
|
||||
2. ✅ **推送镜像到 Registry**(使用 `一键镜像部署.ps1`)
|
||||
3. ⏳ **创建客户部署包**(包含配置文件)
|
||||
4. ⏳ **编写客户部署文档**(简化版)
|
||||
5. ⏳ **测试客户部署流程**(模拟客户环境)
|
||||
|
||||
---
|
||||
|
||||
## 📦 创建客户部署包脚本
|
||||
|
||||
可以创建一个脚本自动生成客户部署包:
|
||||
|
||||
```powershell
|
||||
# scripts/deploy/create-customer-package.ps1
|
||||
# 创建客户部署包
|
||||
|
||||
$packageDir = "customer-deploy-package"
|
||||
New-Item -ItemType Directory -Path $packageDir -Force | Out-Null
|
||||
|
||||
# 复制必要文件
|
||||
Copy-Item "docker-compose.prod.yml" "$packageDir/"
|
||||
Copy-Item ".env.example" "$packageDir/"
|
||||
|
||||
# 创建客户 README
|
||||
@"
|
||||
# FLNXI素材库 - 快速部署指南
|
||||
|
||||
## 一键部署(3 步)
|
||||
|
||||
1. 配置 .env 文件
|
||||
2. 运行: docker compose -f docker-compose.prod.yml up -d
|
||||
3. 访问: http://您的服务器IP:4320
|
||||
|
||||
详细说明请查看 README.md
|
||||
"@ | Out-File "$packageDir/README.md" -Encoding UTF8
|
||||
|
||||
# 打包
|
||||
Compress-Archive -Path "$packageDir/*" -DestinationPath "customer-deploy-package.zip"
|
||||
|
||||
Write-Host "✓ 客户部署包已创建: customer-deploy-package.zip" -ForegroundColor Green
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
**是的,搭建完 Docker Registry 后,完全符合"一键部署"的目标!**
|
||||
|
||||
客户现在只需要:
|
||||
1. 下载部署包(配置文件)
|
||||
2. 修改 `.env` 文件(3-5 个配置项)
|
||||
3. 运行 `docker compose up -d`
|
||||
|
||||
**无需源码,一条命令,完全自动化!** 🎉
|
||||
|
||||
|
||||
|
||||
@ -1,520 +0,0 @@
|
||||
# FLNXI素材库 - 安装使用手册
|
||||
|
||||
> 专业的3D素材管理与分享平台
|
||||
|
||||
## 📋 系统要求
|
||||
|
||||
- **操作系统**: Linux (推荐 Ubuntu 20.04+/CentOS 7+)
|
||||
- **Docker**: 20.10 或更高版本
|
||||
- **Docker Compose**: 2.0 或更高版本
|
||||
- **内存**: 最低 2GB,推荐 4GB+
|
||||
- **磁盘**: 最低 10GB 可用空间
|
||||
- **网络**: 可访问互联网(下载镜像)
|
||||
|
||||
## 🚀 快速安装
|
||||
|
||||
### 第一步:检查 Docker 环境
|
||||
|
||||
如果未安装 Docker,请先安装:
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
sudo systemctl start docker
|
||||
sudo systemctl enable docker
|
||||
```
|
||||
|
||||
**CentOS:**
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
sudo systemctl start docker
|
||||
sudo systemctl enable docker
|
||||
```
|
||||
|
||||
验证安装:
|
||||
```bash
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
### 第二步:解压安装包
|
||||
|
||||
```bash
|
||||
# 解压文件
|
||||
tar -xzf flnxi-material-vX.X.X.tar.gz
|
||||
cd flnxi-material-vX.X.X
|
||||
|
||||
# 添加执行权限
|
||||
chmod +x *.sh
|
||||
```
|
||||
|
||||
### 第三步:配置服务器信息
|
||||
|
||||
编辑 `.env` 文件:
|
||||
|
||||
```bash
|
||||
vim .env
|
||||
# 或
|
||||
nano .env
|
||||
```
|
||||
|
||||
**必须修改的配置:**
|
||||
|
||||
```bash
|
||||
# 修改为您的服务器IP地址
|
||||
SERVER_IP=192.168.1.100
|
||||
```
|
||||
|
||||
**可选修改(建议):**
|
||||
|
||||
```bash
|
||||
# 修改端口(如果有冲突)
|
||||
APP_PORT=4320
|
||||
MINIO_API_PORT=9000
|
||||
MINIO_CONSOLE_PORT=9001
|
||||
|
||||
# 修改密码(建议使用强密码)
|
||||
POSTGRES_PASSWORD=your-strong-password
|
||||
MINIO_ROOT_PASSWORD=your-strong-password
|
||||
NEXTAUTH_SECRET=your-random-secret-key
|
||||
```
|
||||
|
||||
### 第四步:运行安装
|
||||
|
||||
```bash
|
||||
./install.sh
|
||||
```
|
||||
|
||||
安装过程约需 5-10 分钟,脚本会自动:
|
||||
- ✅ 检测环境
|
||||
- ✅ 配置服务
|
||||
- ✅ 下载镜像
|
||||
- ✅ 启动服务
|
||||
- ✅ 初始化数据库
|
||||
- ✅ 配置存储
|
||||
|
||||
### 第五步:访问系统
|
||||
|
||||
安装完成后,通过浏览器访问:
|
||||
|
||||
**应用首页:**
|
||||
```
|
||||
http://YOUR_SERVER_IP:4320
|
||||
```
|
||||
|
||||
**MinIO 管理控制台:**
|
||||
```
|
||||
http://YOUR_SERVER_IP:9001
|
||||
账号: minioadmin
|
||||
密码: (查看 .env 文件中的 MINIO_ROOT_PASSWORD)
|
||||
```
|
||||
|
||||
## 👤 首次使用
|
||||
|
||||
### 1. 注册账号
|
||||
|
||||
访问应用首页,点击「注册」按钮创建账号。
|
||||
|
||||
### 2. 设置管理员权限
|
||||
|
||||
注册后,需要将账号提升为管理员:
|
||||
|
||||
**方法一:使用 Prisma Studio(推荐)**
|
||||
|
||||
```bash
|
||||
docker compose exec app npx prisma studio
|
||||
```
|
||||
|
||||
然后在浏览器中打开 http://localhost:5555,找到您的用户,将 `role` 字段改为 `ADMIN`。
|
||||
|
||||
**方法二:使用数据库命令**
|
||||
|
||||
```bash
|
||||
docker compose exec postgres psql -U postgres -d flnxi -c \
|
||||
"UPDATE users SET role='ADMIN' WHERE email='your@email.com';"
|
||||
```
|
||||
|
||||
将 `your@email.com` 替换为您的注册邮箱。
|
||||
|
||||
### 3. 开始使用
|
||||
|
||||
现在您可以:
|
||||
- ✅ 上传 3D 素材
|
||||
- ✅ 管理用户
|
||||
- ✅ 配置系统设置
|
||||
- ✅ 查看统计数据
|
||||
|
||||
## 🔧 常用操作
|
||||
|
||||
### 启动服务
|
||||
|
||||
```bash
|
||||
# 快速启动
|
||||
./quick-start.sh
|
||||
|
||||
# 或使用 docker compose
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 停止服务
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### 重启服务
|
||||
|
||||
```bash
|
||||
docker compose restart
|
||||
|
||||
# 重启特定服务
|
||||
docker compose restart app
|
||||
```
|
||||
|
||||
### 查看日志
|
||||
|
||||
```bash
|
||||
# 查看所有日志
|
||||
docker compose logs -f
|
||||
|
||||
# 查看应用日志
|
||||
docker compose logs -f app
|
||||
|
||||
# 查看最近100行日志
|
||||
docker compose logs --tail=100
|
||||
```
|
||||
|
||||
### 查看服务状态
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### 进入容器
|
||||
|
||||
```bash
|
||||
# 进入应用容器
|
||||
docker compose exec app sh
|
||||
|
||||
# 进入数据库容器
|
||||
docker compose exec postgres psql -U postgres -d flnxi
|
||||
```
|
||||
|
||||
## 💾 数据备份
|
||||
|
||||
### 备份数据库
|
||||
|
||||
```bash
|
||||
# 导出数据库
|
||||
docker compose exec postgres pg_dump -U postgres flnxi > backup_$(date +%Y%m%d).sql
|
||||
|
||||
# 恢复数据库
|
||||
docker compose exec -T postgres psql -U postgres flnxi < backup_20240101.sql
|
||||
```
|
||||
|
||||
### 备份上传文件
|
||||
|
||||
```bash
|
||||
# 备份 uploads 目录
|
||||
tar -czf uploads_backup_$(date +%Y%m%d).tar.gz uploads/
|
||||
|
||||
# 恢复文件
|
||||
tar -xzf uploads_backup_20240101.tar.gz
|
||||
```
|
||||
|
||||
### 备份 MinIO 数据
|
||||
|
||||
```bash
|
||||
# 导出 MinIO 数据
|
||||
docker compose exec minio tar -czf /tmp/minio_backup.tar.gz /data
|
||||
docker compose cp minio:/tmp/minio_backup.tar.gz ./minio_backup_$(date +%Y%m%d).tar.gz
|
||||
```
|
||||
|
||||
### 定时备份(推荐)
|
||||
|
||||
创建自动备份脚本:
|
||||
|
||||
```bash
|
||||
cat > backup.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
BACKUP_DIR="/path/to/backups"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
# 备份数据库
|
||||
docker compose exec -T postgres pg_dump -U postgres flnxi > $BACKUP_DIR/db_$DATE.sql
|
||||
|
||||
# 备份文件
|
||||
tar -czf $BACKUP_DIR/uploads_$DATE.tar.gz uploads/
|
||||
|
||||
# 删除7天前的备份
|
||||
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
|
||||
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
|
||||
EOF
|
||||
|
||||
chmod +x backup.sh
|
||||
|
||||
# 添加到 crontab(每天凌晨2点备份)
|
||||
crontab -e
|
||||
# 添加: 0 2 * * * /path/to/backup.sh
|
||||
```
|
||||
|
||||
## 🔄 系统更新
|
||||
|
||||
### 更新到新版本
|
||||
|
||||
```bash
|
||||
# 1. 备份数据(重要!)
|
||||
./backup.sh
|
||||
|
||||
# 2. 停止服务
|
||||
docker compose down
|
||||
|
||||
# 3. 更新镜像(如有新版本的 docker-compose.yml,请替换)
|
||||
docker compose pull
|
||||
|
||||
# 4. 启动服务
|
||||
docker compose up -d
|
||||
|
||||
# 5. 查看日志确认启动成功
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
## 🔥 防火墙配置
|
||||
|
||||
如果无法访问服务,请检查防火墙设置:
|
||||
|
||||
**Ubuntu (ufw):**
|
||||
```bash
|
||||
sudo ufw allow 4320/tcp
|
||||
sudo ufw allow 9000/tcp
|
||||
sudo ufw allow 9001/tcp
|
||||
sudo ufw reload
|
||||
```
|
||||
|
||||
**CentOS (firewalld):**
|
||||
```bash
|
||||
sudo firewall-cmd --permanent --add-port=4320/tcp
|
||||
sudo firewall-cmd --permanent --add-port=9000/tcp
|
||||
sudo firewall-cmd --permanent --add-port=9001/tcp
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
## ⚠️ 故障排查
|
||||
|
||||
### 问题1: 无法访问应用
|
||||
|
||||
**检查服务状态:**
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
**查看日志:**
|
||||
```bash
|
||||
docker compose logs app
|
||||
```
|
||||
|
||||
**检查端口:**
|
||||
```bash
|
||||
netstat -tlnp | grep 4320
|
||||
```
|
||||
|
||||
### 问题2: 数据库连接失败
|
||||
|
||||
**检查数据库状态:**
|
||||
```bash
|
||||
docker compose ps postgres
|
||||
docker compose logs postgres
|
||||
```
|
||||
|
||||
**重启数据库:**
|
||||
```bash
|
||||
docker compose restart postgres
|
||||
```
|
||||
|
||||
### 问题3: 图片无法显示
|
||||
|
||||
**检查 MinIO 状态:**
|
||||
```bash
|
||||
docker compose ps minio
|
||||
docker compose logs minio
|
||||
```
|
||||
|
||||
**验证 MinIO 配置:**
|
||||
```bash
|
||||
docker compose exec minio mc alias set local http://localhost:9000 minioadmin YOUR_PASSWORD
|
||||
docker compose exec minio mc ls local/
|
||||
```
|
||||
|
||||
**检查 .env 配置:**
|
||||
```bash
|
||||
# 确认 SERVER_IP 配置正确
|
||||
cat .env | grep SERVER_IP
|
||||
```
|
||||
|
||||
### 问题4: 磁盘空间不足
|
||||
|
||||
**查看磁盘使用:**
|
||||
```bash
|
||||
df -h
|
||||
docker system df
|
||||
```
|
||||
|
||||
**清理 Docker 缓存:**
|
||||
```bash
|
||||
docker system prune -a
|
||||
```
|
||||
|
||||
**查看日志文件大小:**
|
||||
```bash
|
||||
docker compose logs --tail=0 | wc -l
|
||||
```
|
||||
|
||||
### 问题5: 服务无响应
|
||||
|
||||
**重启所有服务:**
|
||||
```bash
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
**完全重建:**
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## 📊 性能优化
|
||||
|
||||
### 调整 Docker 资源
|
||||
|
||||
编辑 `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
```
|
||||
|
||||
### 启用日志轮转
|
||||
|
||||
编辑 `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
```
|
||||
|
||||
### 配置 Nginx 反向代理(推荐)
|
||||
|
||||
可以使用 Nginx 提供:
|
||||
- SSL/HTTPS 支持
|
||||
- 静态文件缓存
|
||||
- 负载均衡
|
||||
- 更好的性能
|
||||
|
||||
配置示例(nginx.conf):
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
|
||||
client_max_body_size 100M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:4320;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
location /minio/ {
|
||||
proxy_pass http://localhost:9000/;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔒 安全建议
|
||||
|
||||
1. **修改默认密码**: 务必在 `.env` 中修改所有默认密码
|
||||
2. **启用 HTTPS**: 使用 Nginx + Let's Encrypt 配置 SSL
|
||||
3. **限制访问**: 配置防火墙只开放必要端口
|
||||
4. **定期备份**: 设置自动备份,并保存到异地
|
||||
5. **及时更新**: 定期检查并更新到最新版本
|
||||
6. **监控日志**: 定期查看日志,发现异常及时处理
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
遇到问题时,请提供以下信息:
|
||||
|
||||
```bash
|
||||
# 1. 系统信息
|
||||
uname -a
|
||||
docker --version
|
||||
docker compose version
|
||||
|
||||
# 2. 服务状态
|
||||
docker compose ps
|
||||
|
||||
# 3. 日志信息
|
||||
docker compose logs --tail=100
|
||||
|
||||
# 4. 配置信息(注意隐藏密码)
|
||||
cat .env | grep -v PASSWORD | grep -v SECRET
|
||||
```
|
||||
|
||||
## 📝 常见问题 FAQ
|
||||
|
||||
**Q: 可以在 Windows 上安装吗?**
|
||||
A: 可以,但推荐使用 Linux。Windows 需要先安装 Docker Desktop。
|
||||
|
||||
**Q: 如何修改端口?**
|
||||
A: 编辑 `.env` 文件中的 `APP_PORT`,然后执行 `docker compose up -d`。
|
||||
|
||||
**Q: 数据存储在哪里?**
|
||||
A:
|
||||
- 数据库: Docker volume `postgres_data`
|
||||
- MinIO 文件: Docker volume `minio_data`
|
||||
- 上传文件: `./uploads` 目录
|
||||
|
||||
**Q: 如何迁移到新服务器?**
|
||||
A:
|
||||
1. 备份所有数据
|
||||
2. 在新服务器安装系统
|
||||
3. 恢复数据
|
||||
4. 修改 `.env` 中的 SERVER_IP
|
||||
|
||||
**Q: 忘记管理员密码怎么办?**
|
||||
A: 可以直接在数据库中重置密码,或创建新管理员账号。
|
||||
|
||||
**Q: 支持多少用户同时在线?**
|
||||
A: 根据服务器配置,推荐配置:2GB内存支持 50-100 用户。
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本软件为商业授权软件,使用受许可协议约束。
|
||||
|
||||
---
|
||||
|
||||
**感谢使用FLNXI素材库管理系统!**
|
||||
|
||||
如有疑问,请联系技术支持。
|
||||
|
||||
|
||||
|
||||
@ -1,404 +0,0 @@
|
||||
# FLNXI素材库 - 部署指南(开发者)
|
||||
|
||||
## 项目打包出售方案
|
||||
|
||||
本项目已配置为可打包成私有 Docker 镜像进行销售的完整方案,包含以下组件:
|
||||
|
||||
- ✅ **主应用**: Next.js 应用(私有镜像)
|
||||
- ✅ **PostgreSQL**: 数据库(公开镜像)
|
||||
- ✅ **MinIO**: 对象存储(公开镜像)
|
||||
- ✅ **自动配置**: 一键安装脚本
|
||||
|
||||
## 架构说明
|
||||
|
||||
### 完整架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 客户服务器 (一键部署) │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ 应用服务 │ │ PostgreSQL │ │
|
||||
│ │ (私有镜像) │←→│ 数据库 │ │
|
||||
│ └──────┬───────┘ └──────────────┘ │
|
||||
│ │ │
|
||||
│ ↓ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ MinIO │ │
|
||||
│ │ 对象存储 │ │
|
||||
│ └──────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 核心优势
|
||||
|
||||
1. **完全独立**: 客户无需外部依赖,所有服务都在本地运行
|
||||
2. **一键部署**: 自动配置数据库、MinIO、网络等
|
||||
3. **数据私有**: 所有数据保存在客户服务器,安全可控
|
||||
4. **易于维护**: 标准 Docker 部署,方便升级和维护
|
||||
|
||||
## 构建和发布流程
|
||||
|
||||
### 1. 准备私有镜像仓库
|
||||
|
||||
选择以下任一方式:
|
||||
|
||||
#### 方案A: 使用 Docker Hub (私有仓库)
|
||||
|
||||
```bash
|
||||
# 登录 Docker Hub
|
||||
docker login
|
||||
|
||||
# 设置环境变量
|
||||
export DOCKER_REGISTRY=yourusername
|
||||
```
|
||||
|
||||
#### 方案B: 使用阿里云容器镜像服务
|
||||
|
||||
```bash
|
||||
# 登录阿里云
|
||||
docker login --username=your_username registry.cn-hangzhou.aliyuncs.com
|
||||
|
||||
# 设置环境变量
|
||||
export DOCKER_REGISTRY=registry.cn-hangzhou.aliyuncs.com/your_namespace
|
||||
```
|
||||
|
||||
#### 方案C: 自建 Harbor 私有仓库
|
||||
|
||||
```bash
|
||||
# 登录 Harbor
|
||||
docker login your-harbor.com
|
||||
|
||||
# 设置环境变量
|
||||
export DOCKER_REGISTRY=your-harbor.com/project
|
||||
```
|
||||
|
||||
### 2. 修改构建配置
|
||||
|
||||
编辑 `build-and-push.sh`,修改默认仓库地址:
|
||||
|
||||
```bash
|
||||
DOCKER_REGISTRY="${DOCKER_REGISTRY:-your-registry.com}"
|
||||
```
|
||||
|
||||
### 3. 构建并推送镜像
|
||||
|
||||
```bash
|
||||
# 添加执行权限
|
||||
chmod +x build-and-push.sh
|
||||
|
||||
# 执行构建
|
||||
./build-and-push.sh
|
||||
```
|
||||
|
||||
脚本会:
|
||||
1. 提示输入版本号(如 1.0.0)
|
||||
2. 构建 Docker 镜像
|
||||
3. 测试镜像是否正常
|
||||
4. 推送到私有仓库
|
||||
5. 生成客户发布包
|
||||
|
||||
### 4. 发布包内容
|
||||
|
||||
生成的发布包 `release-vX.X.X.tar.gz` 包含:
|
||||
|
||||
```
|
||||
release-v1.0.0/
|
||||
├── docker-compose.yml # Docker 编排文件
|
||||
├── .env # 环境配置模板
|
||||
├── install.sh # 一键安装脚本
|
||||
├── quick-start.sh # 快速启动脚本
|
||||
├── README.md # 客户使用文档
|
||||
└── IMAGE_INFO.txt # 镜像信息
|
||||
```
|
||||
|
||||
## 客户使用流程
|
||||
|
||||
### 1. 提供给客户
|
||||
|
||||
将以下内容提供给客户:
|
||||
|
||||
1. 发布包:`release-vX.X.X.tar.gz`
|
||||
2. 镜像访问权限(如果使用私有仓库)
|
||||
3. 使用文档:发布包内的 `README.md`
|
||||
|
||||
### 2. 客户安装步骤
|
||||
|
||||
```bash
|
||||
# 1. 解压
|
||||
tar -xzf release-v1.0.0.tar.gz
|
||||
cd release-v1.0.0
|
||||
|
||||
# 2. 配置环境(修改 .env 文件)
|
||||
vim .env
|
||||
|
||||
# 3. 安装
|
||||
chmod +x *.sh
|
||||
./install.sh
|
||||
```
|
||||
|
||||
### 3. 安装过程自动完成
|
||||
|
||||
- ✅ 检测 Docker 环境
|
||||
- ✅ 自动检测服务器 IP
|
||||
- ✅ 生成随机安全密码
|
||||
- ✅ 拉取镜像(PostgreSQL, MinIO, 应用)
|
||||
- ✅ 启动所有服务
|
||||
- ✅ 初始化数据库
|
||||
- ✅ 配置 MinIO bucket
|
||||
- ✅ 显示访问信息
|
||||
|
||||
## 镜像更新流程
|
||||
|
||||
### 发布新版本
|
||||
|
||||
```bash
|
||||
# 1. 更新代码
|
||||
git pull
|
||||
|
||||
# 2. 修改版本号(建议在 package.json 中同步)
|
||||
vim package.json
|
||||
|
||||
# 3. 构建新版本
|
||||
./build-and-push.sh
|
||||
# 输入新版本号,如: 1.1.0
|
||||
|
||||
# 4. 将新的发布包提供给客户
|
||||
# release-v1.1.0.tar.gz
|
||||
```
|
||||
|
||||
### 客户升级步骤
|
||||
|
||||
```bash
|
||||
# 1. 备份数据
|
||||
docker compose exec postgres pg_dump -U postgres flnxi > backup.sql
|
||||
tar -czf uploads_backup.tar.gz uploads/
|
||||
|
||||
# 2. 停止服务
|
||||
docker compose down
|
||||
|
||||
# 3. 拉取新镜像(编辑 .env 修改 IMAGE_TAG)
|
||||
vim .env # 修改 IMAGE_TAG=1.1.0
|
||||
docker compose pull
|
||||
|
||||
# 4. 启动服务
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## MinIO 配置说明
|
||||
|
||||
### 自动初始化
|
||||
|
||||
`minio-init` 服务会自动:
|
||||
|
||||
1. 连接到 MinIO
|
||||
2. 创建指定的 bucket(默认: youyidian3d)
|
||||
3. 设置 bucket 为公开读取
|
||||
4. 完成后自动退出
|
||||
|
||||
### 访问配置
|
||||
|
||||
应用内部访问 MinIO:
|
||||
```
|
||||
S3_ENDPOINT=http://minio:9000
|
||||
```
|
||||
|
||||
外部访问(用户浏览器):
|
||||
```
|
||||
S3_PUBLIC_DOMAIN=http://${SERVER_IP}:9000/${S3_BUCKET}
|
||||
```
|
||||
|
||||
### MinIO 控制台
|
||||
|
||||
客户可以通过 MinIO 控制台管理文件:
|
||||
|
||||
```
|
||||
地址: http://SERVER_IP:9001
|
||||
用户名: minioadmin
|
||||
密码: (在 .env 中配置)
|
||||
```
|
||||
|
||||
## 定价和授权策略建议
|
||||
|
||||
### 方案A: 按实例授权
|
||||
|
||||
- 一次性购买
|
||||
- 绑定服务器 IP 或 MAC 地址
|
||||
- 通过 License Key 激活
|
||||
|
||||
### 方案B: 订阅制
|
||||
|
||||
- 按年/月收费
|
||||
- 定期验证 License
|
||||
- 过期后停止服务
|
||||
|
||||
### 方案C: 版本授权
|
||||
|
||||
- 买断当前版本
|
||||
- 升级新版本需额外付费
|
||||
- 提供有限期的更新支持
|
||||
|
||||
### 实施 License 验证
|
||||
|
||||
可以在应用中添加 License 验证逻辑:
|
||||
|
||||
```typescript
|
||||
// lib/license.ts
|
||||
export async function verifyLicense(licenseKey: string) {
|
||||
// 1. 验证 License Key 格式
|
||||
// 2. 联网验证(可选)
|
||||
// 3. 检查过期时间
|
||||
// 4. 验证服务器信息
|
||||
return { valid: true, expiresAt: '2025-12-31' }
|
||||
}
|
||||
```
|
||||
|
||||
## 安全措施
|
||||
|
||||
### 1. 代码保护
|
||||
|
||||
- ✅ 使用 Docker 镜像(代码不可见)
|
||||
- ✅ Next.js 生产构建(代码混淆)
|
||||
- ⚠️ 建议:添加代码加密
|
||||
|
||||
### 2. 镜像保护
|
||||
|
||||
```bash
|
||||
# 使用私有仓库
|
||||
DOCKER_REGISTRY=your-private-registry.com
|
||||
|
||||
# 启用镜像签名(Docker Content Trust)
|
||||
export DOCKER_CONTENT_TRUST=1
|
||||
```
|
||||
|
||||
### 3. License 保护
|
||||
|
||||
在应用启动时验证 License:
|
||||
|
||||
```typescript
|
||||
// middleware.ts 或 app/layout.tsx
|
||||
import { verifyLicense } from '@/lib/license'
|
||||
|
||||
// 启动时验证
|
||||
if (!await verifyLicense(process.env.LICENSE_KEY)) {
|
||||
throw new Error('Invalid license')
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 数据库加密
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
postgres:
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
# 启用数据加密
|
||||
command: postgres -c ssl=on -c ssl_cert_file=/certs/cert.pem
|
||||
```
|
||||
|
||||
## 技术支持方案
|
||||
|
||||
### 日志收集
|
||||
|
||||
```bash
|
||||
# 收集所有日志
|
||||
docker compose logs > logs.txt
|
||||
|
||||
# 收集系统信息
|
||||
docker compose ps >> logs.txt
|
||||
docker version >> logs.txt
|
||||
```
|
||||
|
||||
### 远程支持
|
||||
|
||||
```bash
|
||||
# 方案1: 通过 SSH 远程协助
|
||||
ssh user@customer-server
|
||||
|
||||
# 方案2: 使用 Portainer(可选)
|
||||
docker run -d -p 9000:9000 \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
portainer/portainer-ce
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何限制客户的部署数量?
|
||||
|
||||
A: 可以通过以下方式:
|
||||
1. License Key 绑定 MAC 地址
|
||||
2. 在应用中实施激活验证
|
||||
3. 通过 API 统计和限制激活次数
|
||||
|
||||
### Q: 如何防止客户转售?
|
||||
|
||||
A:
|
||||
1. License 绑定硬件信息
|
||||
2. 定期联网验证
|
||||
3. 在用户协议中明确禁止
|
||||
|
||||
### Q: 客户如何迁移到新服务器?
|
||||
|
||||
A: 提供迁移服务:
|
||||
1. 备份数据
|
||||
2. 导出数据库和文件
|
||||
3. 在新服务器安装
|
||||
4. 恢复数据
|
||||
5. 重新激活 License(如果绑定硬件)
|
||||
|
||||
### Q: 如何提供试用版?
|
||||
|
||||
A:
|
||||
1. 提供 30 天试用 License
|
||||
2. 限制功能(如用户数、存储空间)
|
||||
3. 试用期后要求输入正式 License
|
||||
|
||||
## 成本估算
|
||||
|
||||
### 开发成本
|
||||
- 私有镜像仓库: $0-50/月(取决于方案)
|
||||
- SSL 证书: $0(Let's Encrypt)或 $10-100/年
|
||||
- 域名: $10-20/年(如果需要)
|
||||
|
||||
### 客户成本
|
||||
- 服务器: 2GB 内存,10GB 磁盘,约 $5-20/月
|
||||
- 备份存储: 额外空间需求
|
||||
- 带宽: 根据用户量
|
||||
|
||||
## 总结
|
||||
|
||||
这套方案提供了:
|
||||
|
||||
✅ **完整的私有部署方案**
|
||||
- 包含数据库、对象存储、应用
|
||||
- 一键安装,自动配置
|
||||
- 所有数据客户私有
|
||||
|
||||
✅ **商业化友好**
|
||||
- Docker 镜像保护代码
|
||||
- 易于添加 License 验证
|
||||
- 支持版本管理和升级
|
||||
|
||||
✅ **易于销售和交付**
|
||||
- 标准化部署流程
|
||||
- 完整的客户文档
|
||||
- 简化技术支持
|
||||
|
||||
✅ **可持续发展**
|
||||
- 容易发布更新
|
||||
- 支持多种授权模式
|
||||
- 便于扩展功能
|
||||
|
||||
开始构建您的第一个发布版本:
|
||||
|
||||
```bash
|
||||
./build-and-push.sh
|
||||
```
|
||||
|
||||
祝生意兴隆!🚀
|
||||
|
||||
|
||||
@ -1,249 +0,0 @@
|
||||
# 部署流程优化方案
|
||||
|
||||
## 当前流程分析
|
||||
|
||||
### 当前部署步骤(总耗时约 5-10 分钟)
|
||||
1. **npm run check** (可选,~30秒) - 类型检查和 lint
|
||||
2. **docker build** (~3-5分钟) - 构建镜像
|
||||
3. **docker save** (~30秒-1分钟) - 打包镜像为 tar
|
||||
4. **scp/rsync 上传** (~1-2分钟) - 上传 tar 文件(~120MB)
|
||||
5. **服务器 docker load** (~30秒-1分钟) - 加载镜像
|
||||
6. **docker compose up** (~30秒) - 启动容器
|
||||
7. **数据库备份和迁移** (~10-30秒)
|
||||
8. **健康检查** (~10-20秒)
|
||||
|
||||
### 瓶颈分析
|
||||
- **最大瓶颈**:Docker 构建(3-5分钟)
|
||||
- **次要瓶颈**:文件上传(1-2分钟,取决于网络)
|
||||
- **可优化点**:某些步骤可以并行或跳过
|
||||
|
||||
## 优化方案
|
||||
|
||||
### 1. 启用 BuildKit 缓存(推荐 ⭐⭐⭐)
|
||||
|
||||
**优化效果**:构建时间从 3-5 分钟减少到 1-3 分钟(如果依赖未变化)
|
||||
|
||||
**实施方法**:
|
||||
|
||||
在部署脚本开头添加:
|
||||
```powershell
|
||||
# 启用 BuildKit
|
||||
$env:DOCKER_BUILDKIT = "1"
|
||||
$env:COMPOSE_DOCKER_CLI_BUILD = "1"
|
||||
|
||||
# 构建时使用缓存挂载
|
||||
docker build --progress=plain \
|
||||
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
||||
-t $fullImage \
|
||||
-f $Dockerfile \
|
||||
$buildArgs \
|
||||
$Context
|
||||
```
|
||||
|
||||
**修改部署脚本**:
|
||||
```powershell
|
||||
# 在 local-build-and-upload.ps1 的构建部分添加
|
||||
if (-not $env:DOCKER_BUILDKIT) {
|
||||
$env:DOCKER_BUILDKIT = "1"
|
||||
Write-Host "已启用 BuildKit 缓存" -ForegroundColor Green
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 使用 Docker 层缓存(已部分实现,可优化)
|
||||
|
||||
**当前状态**:Dockerfile 已经优化了层顺序,但可以进一步优化
|
||||
|
||||
**优化建议**:
|
||||
- ✅ 已实现:package.json 在前,源代码在后
|
||||
- ✅ 已实现:使用多阶段构建
|
||||
- ⚠️ 可优化:添加 `--cache-from` 参数使用之前的镜像作为缓存源
|
||||
|
||||
**实施方法**:
|
||||
```powershell
|
||||
# 尝试从之前的镜像加载缓存
|
||||
$previousImage = "${ImageName}:previous"
|
||||
docker image inspect $previousImage 2>&1 | Out-Null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
$cacheFrom = "--cache-from=$previousImage"
|
||||
} else {
|
||||
$cacheFrom = ""
|
||||
}
|
||||
|
||||
docker build --progress=plain $cacheFrom -t $fullImage ...
|
||||
```
|
||||
|
||||
### 3. 压缩 tar 文件(可选)
|
||||
|
||||
**优化效果**:上传时间从 1-2 分钟减少到 30秒-1分钟(取决于压缩率)
|
||||
|
||||
**权衡**:
|
||||
- ✅ 优点:上传文件更小,上传更快
|
||||
- ❌ 缺点:压缩需要额外时间(~30秒),服务器解压也需要时间
|
||||
|
||||
**实施方法**:
|
||||
```powershell
|
||||
# 使用 gzip 压缩
|
||||
Write-Host "正在压缩镜像..." -ForegroundColor Yellow
|
||||
$compressedPath = "${archivePath}.gz"
|
||||
docker save $fullImage | gzip > $compressedPath
|
||||
|
||||
# 上传压缩文件
|
||||
Invoke-SCPUpload -LocalPath $compressedPath ...
|
||||
|
||||
# 服务器端解压并加载
|
||||
Invoke-SSHCommand -Command "gunzip -c ${remoteTarPath}.gz | docker load"
|
||||
```
|
||||
|
||||
**建议**:如果网络速度较慢(<10MB/s),启用压缩;如果网络速度较快(>20MB/s),不压缩更快。
|
||||
|
||||
### 4. 并行执行某些步骤
|
||||
|
||||
**优化效果**:总耗时减少 30-60 秒
|
||||
|
||||
**可并行的步骤**:
|
||||
1. 构建镜像的同时,可以检查服务器连接
|
||||
2. 打包镜像的同时,可以准备上传脚本
|
||||
3. 上传文件的同时,可以准备部署命令
|
||||
|
||||
**实施方法**:
|
||||
```powershell
|
||||
# 使用 PowerShell 后台任务
|
||||
$buildJob = Start-Job -ScriptBlock { docker build ... }
|
||||
$checkJob = Start-Job -ScriptBlock { Test-SSHConnection ... }
|
||||
|
||||
# 等待构建完成
|
||||
Wait-Job $buildJob
|
||||
Receive-Job $buildJob
|
||||
```
|
||||
|
||||
### 5. 智能跳过不必要的步骤
|
||||
|
||||
**优化效果**:节省 30-60 秒
|
||||
|
||||
**可跳过的步骤**:
|
||||
- 如果只是代码变更(没有依赖变化),可以跳过 `npm run check`
|
||||
- 如果镜像已存在且未变化,可以跳过构建
|
||||
- 如果数据库 schema 未变化,可以跳过迁移
|
||||
|
||||
**实施方法**:
|
||||
```powershell
|
||||
# 检查是否有依赖变化
|
||||
$packageHash = (Get-FileHash package.json).Hash
|
||||
$lastHash = Get-Content .last-package-hash -ErrorAction SilentlyContinue
|
||||
|
||||
if ($packageHash -eq $lastHash) {
|
||||
Write-Host "依赖未变化,跳过检查" -ForegroundColor Yellow
|
||||
$SkipCheck = $true
|
||||
} else {
|
||||
$packageHash | Out-File .last-package-hash
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 使用 Docker Registry(长期优化)
|
||||
|
||||
**优化效果**:完全避免上传 tar 文件,部署时间减少 1-2 分钟
|
||||
|
||||
**方案**:
|
||||
- 在服务器上搭建私有 Docker Registry(如 Harbor)
|
||||
- 或者使用 Docker Hub / 阿里云容器镜像服务
|
||||
- 构建后直接 push 到 registry
|
||||
- 服务器端直接 pull
|
||||
|
||||
**实施方法**:
|
||||
```powershell
|
||||
# 构建后直接 push
|
||||
docker build -t registry.example.com/flnxi-material:latest .
|
||||
docker push registry.example.com/flnxi-material:latest
|
||||
|
||||
# 服务器端直接 pull
|
||||
docker pull registry.example.com/flnxi-material:latest
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
**权衡**:
|
||||
- ✅ 优点:不需要上传大文件,部署更快
|
||||
- ❌ 缺点:需要搭建和维护 registry,增加复杂度
|
||||
|
||||
### 7. 增量部署(仅代码变更时)
|
||||
|
||||
**优化效果**:如果只是代码变更,可以只上传变更文件,在服务器上重新构建
|
||||
|
||||
**方案**:
|
||||
- 使用 rsync 同步源代码到服务器
|
||||
- 在服务器上执行构建(利用服务器更强的性能)
|
||||
|
||||
**实施方法**:
|
||||
```powershell
|
||||
# 只同步源代码
|
||||
rsync -avz --exclude 'node_modules' --exclude '.next' \
|
||||
./ ${Username}@${Server}:${RemotePath}/source/
|
||||
|
||||
# 在服务器上构建
|
||||
Invoke-SSHCommand -Command "cd ${RemotePath}/source && docker build ..."
|
||||
```
|
||||
|
||||
**权衡**:
|
||||
- ✅ 优点:上传文件小,速度快
|
||||
- ❌ 缺点:需要在服务器上安装构建环境,占用服务器资源
|
||||
|
||||
## 推荐优化方案(按优先级)
|
||||
|
||||
### 优先级 1:立即实施(效果明显,实施简单)
|
||||
1. ✅ **启用 BuildKit 缓存** - 构建时间减少 50-70%
|
||||
2. ✅ **使用 `--cache-from`** - 进一步利用缓存
|
||||
3. ✅ **智能跳过检查** - 节省 30-60 秒
|
||||
|
||||
### 优先级 2:中期实施(效果中等,需要测试)
|
||||
4. ⚠️ **压缩 tar 文件** - 如果网络较慢,效果明显
|
||||
5. ⚠️ **并行执行步骤** - 节省 30-60 秒
|
||||
|
||||
### 优先级 3:长期考虑(需要架构调整)
|
||||
6. 🚀 **使用 Docker Registry** - 最佳长期方案
|
||||
7. 🚀 **增量部署** - 适合频繁小更新
|
||||
|
||||
## 实施建议
|
||||
|
||||
### 快速优化(5分钟实施)
|
||||
修改 `local-build-and-upload.ps1`:
|
||||
1. 在脚本开头添加 `$env:DOCKER_BUILDKIT = "1"`
|
||||
2. 添加 `--cache-from` 参数
|
||||
3. 添加智能跳过检查逻辑
|
||||
|
||||
### 中期优化(30分钟实施)
|
||||
1. 添加压缩选项(可选参数)
|
||||
2. 实现并行执行
|
||||
3. 优化错误处理和日志
|
||||
|
||||
### 长期优化(需要规划)
|
||||
1. 评估 Docker Registry 方案
|
||||
2. 考虑 CI/CD 集成
|
||||
3. 优化服务器资源使用
|
||||
|
||||
## 预期效果
|
||||
|
||||
### 优化前
|
||||
- 总耗时:5-10 分钟
|
||||
- 构建:3-5 分钟
|
||||
- 上传:1-2 分钟
|
||||
- 部署:1-2 分钟
|
||||
|
||||
### 优化后(实施优先级 1)
|
||||
- 总耗时:2-4 分钟(减少 50-60%)
|
||||
- 构建:1-2 分钟(使用缓存)
|
||||
- 上传:1-2 分钟(不变)
|
||||
- 部署:30秒-1分钟(优化后)
|
||||
|
||||
### 优化后(实施所有优先级 1+2)
|
||||
- 总耗时:1.5-3 分钟(减少 70-80%)
|
||||
- 构建:1-2 分钟(使用缓存)
|
||||
- 上传:30秒-1分钟(压缩后)
|
||||
- 部署:30秒(优化后)
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **BuildKit 要求**:需要 Docker 19.03+ 和启用 BuildKit
|
||||
2. **缓存失效**:如果 package.json 变化,缓存会失效
|
||||
3. **网络依赖**:上传速度取决于网络,压缩可能不总是更快
|
||||
4. **服务器资源**:确保服务器有足够资源处理构建和部署
|
||||
|
||||
|
||||
@ -1,189 +0,0 @@
|
||||
# Docker 多阶段构建优化指南
|
||||
|
||||
## 概述
|
||||
|
||||
本文档说明了对 Dockerfile 进行的多阶段构建优化,实现了 `deps -> builder -> runner` 的清晰分离,提高了构建效率和镜像大小。
|
||||
|
||||
## 优化内容
|
||||
|
||||
### 1. 多阶段构建结构
|
||||
|
||||
#### 阶段 1: `base` - 基础镜像
|
||||
- 使用 `node:18-alpine` 作为基础镜像
|
||||
- 配置阿里云镜像源加速
|
||||
|
||||
#### 阶段 2: `deps` - 依赖安装
|
||||
- **构建时依赖**:使用虚拟包 `.build-deps` 安装编译工具
|
||||
- `python3`, `make`, `g++`, `pkgconfig` (编译 canvas 等原生模块)
|
||||
- `openssl-dev`, `cairo-dev`, `pango-dev` 等开发头文件
|
||||
- **安装 npm 依赖**:运行 `npm ci` 安装所有依赖(包括 devDependencies)
|
||||
- **清理构建依赖**:安装完成后删除 `.build-deps`,只保留运行时库
|
||||
- **保留运行时库**:保留 `cairo`, `pango`, `jpeg`, `giflib` 等运行时所需的库
|
||||
|
||||
#### 阶段 3: `builder` - 构建应用
|
||||
- 从 `deps` 阶段复制 `node_modules`(已编译好的依赖)
|
||||
- 复制应用源代码
|
||||
- 生成 Prisma Client
|
||||
- 构建 Next.js 应用(standalone 模式)
|
||||
|
||||
#### 阶段 4: `runner` - 生产运行
|
||||
- **最小运行时环境**:只安装运行时必需的库
|
||||
- 不包含开发工具(`make`, `g++`, `python3` 等)
|
||||
- 不包含开发头文件(`*-dev` 包)
|
||||
- 只包含运行时库(`cairo`, `pango`, `jpeg` 等)
|
||||
- **复制构建产物**:
|
||||
- `.next/standalone` - Next.js standalone 输出
|
||||
- `.next/static` - 静态资源
|
||||
- `public` - 公共静态文件
|
||||
- `prisma` - Prisma schema 和迁移文件
|
||||
- `lib` - 运行时模块(configLoader 等)
|
||||
- 关键原生模块(`.prisma`, `canvas`, `ag-psd`)
|
||||
|
||||
### 2. 关键优化点
|
||||
|
||||
#### ✅ 使用虚拟包清理构建依赖
|
||||
```dockerfile
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
python3 make g++ pkgconfig ...
|
||||
RUN npm ci
|
||||
RUN apk del .build-deps # 清理构建工具
|
||||
```
|
||||
|
||||
#### ✅ 分离构建时和运行时依赖
|
||||
- **构建时需要**:编译工具、开发头文件
|
||||
- **运行时需要**:运行时库、已编译的二进制文件
|
||||
|
||||
#### ✅ 利用 Docker 构建缓存
|
||||
- 依赖文件(`package.json`, `package-lock.json`)变化少,缓存命中率高
|
||||
- 源代码变化时,只需重新构建,不需要重新安装依赖
|
||||
|
||||
#### ✅ 最小化最终镜像
|
||||
- 最终镜像不包含:
|
||||
- Node.js 开发工具
|
||||
- 编译工具链
|
||||
- 开发头文件
|
||||
- 源代码(只有构建产物)
|
||||
|
||||
### 3. 镜像大小对比
|
||||
|
||||
**优化前**:
|
||||
- 包含所有构建工具和开发依赖
|
||||
- 镜像大小:~800MB+
|
||||
|
||||
**优化后**:
|
||||
- 只包含运行时必需的库
|
||||
- 镜像大小:~400-500MB(减少约 40%)
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 本地构建
|
||||
|
||||
```bash
|
||||
# 使用缓存构建(推荐)
|
||||
docker build -t flnxi-material .
|
||||
|
||||
# 不使用缓存构建(完全重新构建)
|
||||
docker build --no-cache -t flnxi-material .
|
||||
```
|
||||
|
||||
### 2. 使用 Docker Compose
|
||||
|
||||
```bash
|
||||
# 使用缓存构建(推荐)
|
||||
docker compose build app
|
||||
|
||||
# 不使用缓存构建
|
||||
docker compose build --no-cache app
|
||||
|
||||
# 构建并启动
|
||||
docker compose up -d --build app
|
||||
```
|
||||
|
||||
### 3. 使用优化部署脚本
|
||||
|
||||
```powershell
|
||||
# 使用缓存部署(推荐,速度快)
|
||||
.\scripts\deploy\deploy-optimized.ps1
|
||||
|
||||
# 不使用缓存部署(完全重新构建)
|
||||
.\scripts\deploy\deploy-optimized.ps1 -NoCache
|
||||
```
|
||||
|
||||
## 部署脚本优化
|
||||
|
||||
### 新脚本特性
|
||||
|
||||
1. **智能缓存使用**:
|
||||
- 默认使用 Docker 构建缓存
|
||||
- 只在必要时使用 `--no-cache`
|
||||
|
||||
2. **更好的错误处理**:
|
||||
- 检查 Docker 和 Docker Compose 是否安装
|
||||
- 等待容器就绪
|
||||
- 显示详细的部署日志
|
||||
|
||||
3. **优雅停止**:
|
||||
- 使用 `--timeout` 参数优雅停止旧容器
|
||||
- 避免数据丢失
|
||||
|
||||
4. **状态检查**:
|
||||
- 检查容器是否正常启动
|
||||
- 显示容器状态和日志
|
||||
|
||||
### 脚本参数
|
||||
|
||||
- `-Server`: 服务器地址(默认: 45.249.208.118)
|
||||
- `-User`: SSH 用户(默认: root)
|
||||
- `-Password`: SSH 密码
|
||||
- `-ProjectPath`: 项目路径(默认: /root/youyidian-3d)
|
||||
- `-NoCache`: 不使用构建缓存(完全重新构建)
|
||||
- `-UseSSHKey`: 使用 SSH 密钥(未来功能)
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 为什么需要复制 `canvas` 等原生模块?
|
||||
|
||||
A: Next.js 的 standalone 输出应该已经包含了所有依赖,但某些原生模块(如 `canvas`)可能需要额外的系统库支持。为了确保这些模块正常工作,我们显式复制它们。
|
||||
|
||||
### Q2: 构建时间会变长吗?
|
||||
|
||||
A: 不会。实际上,由于使用了构建缓存,只有在依赖文件变化时才需要重新安装依赖。源代码变化时,只需要重新构建,不需要重新安装依赖。
|
||||
|
||||
### Q3: 如何验证优化是否生效?
|
||||
|
||||
A: 可以检查最终镜像的大小和内容:
|
||||
```bash
|
||||
# 查看镜像大小
|
||||
docker images flnxi-material
|
||||
|
||||
# 查看镜像内容
|
||||
docker run --rm flnxi-material ls -la /app
|
||||
|
||||
# 检查是否包含编译工具
|
||||
docker run --rm flnxi-material which g++
|
||||
# 应该返回空(不包含 g++)
|
||||
```
|
||||
|
||||
### Q4: 如果构建失败怎么办?
|
||||
|
||||
A: 检查以下几点:
|
||||
1. 确保所有依赖文件(`package.json`, `package-lock.json`)已上传
|
||||
2. 检查 Dockerfile 中的路径是否正确
|
||||
3. 查看构建日志中的错误信息
|
||||
4. 如果问题持续,可以尝试使用 `--no-cache` 完全重新构建
|
||||
|
||||
## 进一步优化建议
|
||||
|
||||
1. **使用 SSH 密钥**:避免在脚本中硬编码密码
|
||||
2. **使用 CI/CD**:自动化构建和部署流程
|
||||
3. **镜像多架构支持**:支持 ARM 和 x86 架构
|
||||
4. **健康检查**:添加容器健康检查
|
||||
5. **日志聚合**:使用日志聚合工具(如 ELK)
|
||||
|
||||
## 参考
|
||||
|
||||
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
|
||||
- [Next.js Docker 部署指南](https://nextjs.org/docs/deployment#docker-image)
|
||||
- [Alpine Linux 包管理](https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management)
|
||||
|
||||
|
||||
@ -1,332 +0,0 @@
|
||||
# Docker Registry 部署方案
|
||||
|
||||
## 概述
|
||||
|
||||
使用 Docker Registry 可以完全避免上传大文件(~120MB tar),部署时间从 5-10 分钟减少到 2-3 分钟。
|
||||
|
||||
## 方案对比
|
||||
|
||||
### 当前方案(tar 文件上传)
|
||||
```
|
||||
本地构建 → docker save → scp 上传 (120MB) → 服务器 docker load → 部署
|
||||
耗时:5-10 分钟
|
||||
```
|
||||
|
||||
### Registry 方案
|
||||
```
|
||||
本地构建 → docker push → 服务器 docker pull → 部署
|
||||
耗时:2-3 分钟
|
||||
```
|
||||
|
||||
## 方案选择
|
||||
|
||||
### 方案 1:使用 Docker Hub(最简单)
|
||||
|
||||
**优点**:
|
||||
- ✅ 无需搭建服务器
|
||||
- ✅ 免费(公开仓库)或付费(私有仓库)
|
||||
- ✅ 全球 CDN 加速
|
||||
- ✅ 操作简单
|
||||
|
||||
**缺点**:
|
||||
- ❌ 公开仓库不安全(代码可能泄露)
|
||||
- ❌ 私有仓库需要付费($7/月)
|
||||
- ❌ 依赖外部服务
|
||||
|
||||
**适用场景**:个人项目或小团队
|
||||
|
||||
### 方案 2:使用阿里云容器镜像服务(推荐 ⭐)
|
||||
|
||||
**优点**:
|
||||
- ✅ 国内访问速度快
|
||||
- ✅ 免费额度充足(个人版免费)
|
||||
- ✅ 支持私有仓库
|
||||
- ✅ 与阿里云其他服务集成
|
||||
|
||||
**缺点**:
|
||||
- ❌ 需要阿里云账号
|
||||
- ❌ 依赖外部服务
|
||||
|
||||
**适用场景**:国内部署,推荐使用
|
||||
|
||||
### 方案 3:自建 Harbor Registry(最灵活)
|
||||
|
||||
**优点**:
|
||||
- ✅ 完全控制
|
||||
- ✅ 不依赖外部服务
|
||||
- ✅ 支持多租户
|
||||
- ✅ 支持镜像扫描、安全策略
|
||||
|
||||
**缺点**:
|
||||
- ❌ 需要服务器资源
|
||||
- ❌ 需要维护
|
||||
- ❌ 配置复杂
|
||||
|
||||
**适用场景**:企业级部署,需要完全控制
|
||||
|
||||
## 实施方案
|
||||
|
||||
### 方案 1:Docker Hub(示例)
|
||||
|
||||
#### 1. 注册 Docker Hub 账号
|
||||
访问 https://hub.docker.com 注册账号
|
||||
|
||||
#### 2. 登录 Docker Hub
|
||||
```powershell
|
||||
docker login
|
||||
# 输入用户名和密码
|
||||
```
|
||||
|
||||
#### 3. 修改部署脚本
|
||||
|
||||
在 `local-build-and-upload.ps1` 中添加:
|
||||
|
||||
```powershell
|
||||
# 配置 Registry
|
||||
$RegistryHost = "docker.io" # Docker Hub
|
||||
$RegistryUser = "your-username"
|
||||
$RegistryImage = "${RegistryUser}/flnxi-material"
|
||||
|
||||
# 构建并标记镜像
|
||||
$fullImage = "${RegistryImage}:${Tag}"
|
||||
docker build -t $fullImage -f $Dockerfile $buildArgs $Context
|
||||
|
||||
# Push 到 Registry
|
||||
Write-Host "===> 推送镜像到 Docker Hub" -ForegroundColor Cyan
|
||||
docker push $fullImage
|
||||
|
||||
# 服务器端 Pull 并部署
|
||||
Invoke-SSHCommand -Command "docker pull ${fullImage}"
|
||||
Invoke-SSHCommand -Command "cd ${RemotePath} && docker compose up -d"
|
||||
```
|
||||
|
||||
### 方案 2:阿里云容器镜像服务(推荐)
|
||||
|
||||
#### 1. 创建阿里云账号和容器镜像服务
|
||||
|
||||
1. 访问 https://www.aliyun.com 注册账号
|
||||
2. 进入「容器镜像服务 ACR」
|
||||
3. 创建「个人版实例」(免费)
|
||||
4. 创建命名空间(如:`youyidian`)
|
||||
5. 创建镜像仓库(如:`3d-material`)
|
||||
|
||||
#### 2. 获取登录凭证
|
||||
|
||||
在镜像仓库页面,点击「登录指令」,获取:
|
||||
- 登录地址:`registry.cn-hangzhou.aliyuncs.com`(根据地域不同)
|
||||
- 用户名:通常是阿里云账号
|
||||
- 密码:在「访问凭证」中设置
|
||||
|
||||
#### 3. 修改部署脚本
|
||||
|
||||
创建新文件(位于项目根目录)`一键镜像部署.ps1`:
|
||||
|
||||
```powershell
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Server,
|
||||
|
||||
[string]$Username = "root",
|
||||
[string]$Password,
|
||||
|
||||
[string]$RegistryHost = "registry.cn-hangzhou.aliyuncs.com",
|
||||
[string]$RegistryNamespace = "youyidian",
|
||||
[string]$RegistryImage = "3d-material",
|
||||
[string]$RegistryUser,
|
||||
[string]$RegistryPassword,
|
||||
|
||||
[string]$Tag = "latest",
|
||||
[string]$Dockerfile = "Dockerfile",
|
||||
[string]$Context = ".",
|
||||
[string]$RemotePath = "/root/deploy",
|
||||
[string]$ComposeFile = "docker-compose.prod.yml"
|
||||
)
|
||||
|
||||
# 启用 BuildKit
|
||||
$env:DOCKER_BUILDKIT = "1"
|
||||
|
||||
$projectRoot = Resolve-Path "$PSScriptRoot/../.."
|
||||
Push-Location $projectRoot
|
||||
try {
|
||||
# 构建镜像
|
||||
$fullImageName = "${RegistryHost}/${RegistryNamespace}/${RegistryImage}:${Tag}"
|
||||
Write-Host "===> 构建镜像: $fullImageName" -ForegroundColor Cyan
|
||||
|
||||
$buildArgs = @(
|
||||
"--build-arg", "DATABASE_URL=postgresql://placeholder:placeholder@localhost:5432/placeholder",
|
||||
"--build-arg", "NEXTAUTH_URL=http://45.249.208.118:4320",
|
||||
"--build-arg", "APP_PUBLIC_URL=http://45.249.208.118:4320"
|
||||
)
|
||||
|
||||
docker build -t $fullImageName -f $Dockerfile $buildArgs $Context
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "构建失败"
|
||||
}
|
||||
|
||||
# 登录 Registry
|
||||
Write-Host "===> 登录容器镜像服务" -ForegroundColor Cyan
|
||||
if ($RegistryPassword) {
|
||||
echo $RegistryPassword | docker login $RegistryHost -u $RegistryUser --password-stdin
|
||||
} else {
|
||||
docker login $RegistryHost -u $RegistryUser
|
||||
}
|
||||
|
||||
# 推送镜像
|
||||
Write-Host "===> 推送镜像到 Registry" -ForegroundColor Cyan
|
||||
docker push $fullImageName
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "推送失败"
|
||||
}
|
||||
|
||||
# 服务器端操作
|
||||
Write-Host "===> 在服务器上拉取并部署" -ForegroundColor Cyan
|
||||
|
||||
# 获取认证信息(复用原有逻辑)
|
||||
$AuthInfo = Get-AuthMethod -Server $Server -Username $Username -Password $Password
|
||||
|
||||
# 在服务器上登录 Registry(如果需要)
|
||||
$loginCmd = "echo '$RegistryPassword' | docker login $RegistryHost -u $RegistryUser --password-stdin"
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command $loginCmd -AuthInfo $AuthInfo
|
||||
|
||||
# 拉取镜像
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command "docker pull ${fullImageName}" -AuthInfo $AuthInfo
|
||||
|
||||
# 更新 docker-compose.yml 中的镜像名称
|
||||
$updateImageCmd = "cd ${RemotePath} && sed -i 's|image:.*flnxi-material.*|image: ${fullImageName}|g' docker-compose.yml"
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command $updateImageCmd -AuthInfo $AuthInfo
|
||||
|
||||
# 部署
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command "cd ${RemotePath} && docker compose up -d" -AuthInfo $AuthInfo
|
||||
|
||||
Write-Host "✓ 部署完成" -ForegroundColor Green
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 使用方式
|
||||
|
||||
```powershell
|
||||
# 设置环境变量(可选,避免在命令行中暴露密码)
|
||||
$env:ALIYUN_REGISTRY_USER = "your-username"
|
||||
$env:ALIYUN_REGISTRY_PASSWORD = "your-password"
|
||||
|
||||
# 执行部署
|
||||
.\一键镜像部署.ps1 `
|
||||
-Server 45.249.208.118 `
|
||||
-Username root `
|
||||
-Password "your-ssh-password" `
|
||||
-RegistryUser $env:ALIYUN_REGISTRY_USER `
|
||||
-RegistryPassword $env:ALIYUN_REGISTRY_PASSWORD `
|
||||
-Tag "v1.0.0"
|
||||
```
|
||||
|
||||
### 方案 3:自建 Harbor Registry
|
||||
|
||||
#### 1. 在服务器上安装 Harbor
|
||||
|
||||
```bash
|
||||
# 下载 Harbor
|
||||
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
|
||||
tar xvf harbor-offline-installer-v2.10.0.tgz
|
||||
cd harbor
|
||||
|
||||
# 配置 harbor.yml
|
||||
cp harbor.yml.tmpl harbor.yml
|
||||
# 编辑 harbor.yml,设置域名、密码等
|
||||
|
||||
# 安装
|
||||
./install.sh
|
||||
```
|
||||
|
||||
#### 2. 配置 Nginx 反向代理(可选)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name registry.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 使用方式
|
||||
|
||||
与方案 2 类似,只需修改 Registry 地址:
|
||||
|
||||
```powershell
|
||||
.\一键镜像部署.ps1 `
|
||||
-Server 45.249.208.118 `
|
||||
-RegistryHost "registry.yourdomain.com" `
|
||||
-RegistryUser "admin" `
|
||||
-RegistryPassword "Harbor12345"
|
||||
```
|
||||
|
||||
## 安全建议
|
||||
|
||||
### 1. 使用环境变量存储密码
|
||||
|
||||
```powershell
|
||||
# 创建 .env.registry 文件(不提交到 Git)
|
||||
$env:REGISTRY_PASSWORD = "your-password"
|
||||
|
||||
# 在脚本中使用
|
||||
-RegistryPassword $env:REGISTRY_PASSWORD
|
||||
```
|
||||
|
||||
### 2. 使用访问令牌(Token)
|
||||
|
||||
阿里云 ACR 支持访问令牌,比密码更安全:
|
||||
|
||||
1. 在 ACR 控制台创建访问令牌
|
||||
2. 使用令牌作为密码
|
||||
|
||||
### 3. 限制 Registry 访问
|
||||
|
||||
- 只允许特定 IP 访问
|
||||
- 使用 HTTPS
|
||||
- 定期轮换密码
|
||||
|
||||
## 成本对比
|
||||
|
||||
### 当前方案(tar 上传)
|
||||
- 成本:$0(使用现有服务器)
|
||||
- 时间:5-10 分钟
|
||||
|
||||
### Docker Hub
|
||||
- 成本:$0(公开)或 $7/月(私有)
|
||||
- 时间:2-3 分钟
|
||||
|
||||
### 阿里云 ACR
|
||||
- 成本:$0(个人版免费)
|
||||
- 时间:2-3 分钟(国内更快)
|
||||
|
||||
### Harbor(自建)
|
||||
- 成本:服务器资源(约 2GB RAM,20GB 磁盘)
|
||||
- 时间:2-3 分钟
|
||||
|
||||
## 推荐方案
|
||||
|
||||
**推荐使用阿里云容器镜像服务(ACR)**,原因:
|
||||
1. ✅ 免费(个人版)
|
||||
2. ✅ 国内访问速度快
|
||||
3. ✅ 配置简单
|
||||
4. ✅ 支持私有仓库
|
||||
5. ✅ 无需维护
|
||||
|
||||
## 迁移步骤
|
||||
|
||||
1. **创建 Registry 账号和仓库**(5 分钟)
|
||||
2. **修改部署脚本**(10 分钟)
|
||||
3. **测试部署**(5 分钟)
|
||||
4. **切换到新方案**(1 分钟)
|
||||
|
||||
总计:约 20 分钟即可完成迁移
|
||||
|
||||
|
||||
@ -1,400 +0,0 @@
|
||||
# 功能更新说明 v2.0
|
||||
|
||||
## 📅 更新日期:2025-10-16
|
||||
|
||||
---
|
||||
|
||||
## ✨ 新功能总览
|
||||
|
||||
### 1. ✅ 浏览器黑暗模式支持
|
||||
- **自动跟随系统**:自动识别系统黑暗模式设置
|
||||
- **全局适配**:所有页面和组件都支持黑暗模式
|
||||
- **流畅过渡**:主题切换平滑过渡
|
||||
|
||||
**使用方法**:
|
||||
- 在系统设置中启用黑暗模式,应用会自动切换
|
||||
- macOS: 系统偏好设置 → 通用 → 外观 → 深色
|
||||
- Windows: 设置 → 个性化 → 颜色 → 深色
|
||||
|
||||
---
|
||||
|
||||
### 2. ✅ 上传进度条和网络速度显示
|
||||
- **实时进度**:显示上传百分比
|
||||
- **速度显示**:实时显示上传速度(KB/s, MB/s)
|
||||
- **文件大小**:显示上传文件大小
|
||||
|
||||
**界面改进**:
|
||||
- 蓝色进度条动画
|
||||
- 上传速度实时更新
|
||||
- 上传中禁用取消和上传按钮
|
||||
|
||||
---
|
||||
|
||||
### 3. ✅ 支持7Z压缩格式
|
||||
- **新增格式**:除了ZIP、RAR,现在还支持7Z格式
|
||||
- **智能识别**:上传时自动验证文件格式
|
||||
- **友好提示**:不支持的格式会给出清晰的错误提示
|
||||
|
||||
**支持的压缩格式**:
|
||||
- ✅ .zip
|
||||
- ✅ .rar
|
||||
- ✅ .7z
|
||||
|
||||
---
|
||||
|
||||
### 4. ✅ 取消上传大小限制
|
||||
- **之前限制**:100MB
|
||||
- **现在限制**:10GB(基本无限制)
|
||||
- **适用场景**:支持大型3D模型文件上传
|
||||
|
||||
**注意事项**:
|
||||
- 上传大文件时建议使用内网模式
|
||||
- 公网上传速度取决于您的网络带宽
|
||||
|
||||
---
|
||||
|
||||
### 5. ✅ 内外网模式识别和提示
|
||||
- **自动识别**:系统根据访问地址自动识别当前访问模式
|
||||
- **智能提示**:首页顶部显示当前模式和替代访问地址
|
||||
- **模式说明**:
|
||||
- 🏠 **内网模式**:`http://192.168.58.20:4320` (绿色标识)
|
||||
- 局域网内访问
|
||||
- 速度更快,延迟更低
|
||||
- 适合上传大文件(可达3GB)
|
||||
- 显示外网地址供参考
|
||||
- 🌍 **公网模式**:`http://180.97.221.140:2320` (蓝色标识)
|
||||
- 互联网访问
|
||||
- 随时随地可访问
|
||||
- 适合外出使用
|
||||
- 提示内网地址供参考
|
||||
|
||||
**智能提示功能**:
|
||||
- 公网访问时,提示内网地址更快
|
||||
- 内网访问时,显示公网地址供外出使用
|
||||
- 自动检测当前网络环境
|
||||
|
||||
---
|
||||
|
||||
### 6. ✅ 一级分类功能
|
||||
这是本次最大的功能更新!
|
||||
|
||||
#### 6.1 数据库架构调整
|
||||
- **新增**:Category(一级分类)模型
|
||||
- **更新**:Material模型新增`categoryName`字段
|
||||
- **优化**:添加索引提升查询性能
|
||||
|
||||
#### 6.2 上传界面改进
|
||||
**新的上传流程**:
|
||||
1. 选择预览图片
|
||||
2. 输入商品型号
|
||||
3. **选择/新建一级分类** ⭐
|
||||
4. **选择/新建二级分类/场景** ⭐
|
||||
5. 选择压缩包
|
||||
6. 上传
|
||||
|
||||
**一级分类管理**:
|
||||
- 下拉选择现有分类
|
||||
- 快速新建分类(输入即保存)
|
||||
- 自动记忆,下次直接选择
|
||||
|
||||
**二级分类管理**:
|
||||
- 下拉选择现有场景
|
||||
- 快速新建场景
|
||||
- 支持快速切换
|
||||
|
||||
#### 6.3 首页筛选增强
|
||||
**新的筛选方式**:
|
||||
1. **搜索框**:搜索商品型号、一级分类、二级分类、作者
|
||||
2. **一级分类**:按钮式快速筛选
|
||||
3. **二级分类/场景**:按钮式快速筛选
|
||||
4. **组合筛选**:可同时使用多个筛选条件
|
||||
|
||||
**筛选状态显示**:
|
||||
- 当前筛选条件标签显示
|
||||
- 单独移除某个筛选条件
|
||||
- 一键清除所有筛选
|
||||
|
||||
#### 6.4 卡片信息展示
|
||||
每个素材卡片显示:
|
||||
- 预览图
|
||||
- 商品型号
|
||||
- **一级分类** ⭐
|
||||
- **二级分类** ⭐
|
||||
- 作者
|
||||
- 创建时间
|
||||
- 下载按钮
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 数据库更新
|
||||
|
||||
### 需要执行的迁移
|
||||
|
||||
连接到NAS并运行数据库迁移:
|
||||
|
||||
\`\`\`bash
|
||||
# 1. SSH到NAS
|
||||
ssh root@192.168.58.20
|
||||
# 密码:jsjj8888
|
||||
|
||||
# 2. 进入项目目录
|
||||
cd /mnt/tank/apps/youyidian-3d
|
||||
|
||||
# 3. 停止容器
|
||||
docker compose down
|
||||
|
||||
# 4. 更新代码(如果通过Git或上传)
|
||||
# 确保所有更新的文件都已上传
|
||||
|
||||
# 5. 重新构建并启动
|
||||
docker compose up -d --build
|
||||
|
||||
# 6. 运行数据库迁移
|
||||
docker compose exec app npx prisma db push
|
||||
|
||||
# 7. 初始化一级分类数据(可选)
|
||||
docker compose exec -T app npx prisma db execute --schema=/app/prisma/schema.prisma --stdin <<< "
|
||||
INSERT INTO categories (id, name, \"createdAt\") VALUES
|
||||
(gen_random_uuid(), '家居', NOW()),
|
||||
(gen_random_uuid(), '办公', NOW()),
|
||||
(gen_random_uuid(), '商业', NOW()),
|
||||
(gen_random_uuid(), '户外', NOW()),
|
||||
(gen_random_uuid(), '其他', NOW())
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
"
|
||||
|
||||
# 8. 验证部署
|
||||
docker compose ps
|
||||
docker compose logs --tail=50 app
|
||||
\`\`\`
|
||||
|
||||
---
|
||||
|
||||
## 🎨 界面改进
|
||||
|
||||
### 配色方案
|
||||
**亮色模式**:
|
||||
- 主色调:灰色系 (#F9FAFB, #FFFFFF)
|
||||
- 强调色:深灰 (#111827, #1F2937)
|
||||
- 边框:浅灰 (#E5E7EB)
|
||||
|
||||
**暗色模式**:
|
||||
- 主色调:深灰系 (#111827, #1F2937)
|
||||
- 强调色:中灰 (#374151, #4B5563)
|
||||
- 边框:深灰 (#374151)
|
||||
|
||||
### 组件样式
|
||||
- 圆角统一为12px(Apple风格)
|
||||
- 按钮添加hover和active动画
|
||||
- 卡片添加阴影提升层次感
|
||||
- 输入框focus状态优化
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能优化
|
||||
|
||||
1. **数据库索引**
|
||||
- 添加categoryName索引
|
||||
- 添加sceneName索引
|
||||
- 优化查询性能
|
||||
|
||||
2. **前端优化**
|
||||
- 使用XMLHttpRequest实现上传进度
|
||||
- 实时计算上传速度
|
||||
- 减少不必要的重渲染
|
||||
|
||||
3. **用户体验**
|
||||
- 自动保存常用分类和场景
|
||||
- 快速筛选和搜索
|
||||
- 一键清除所有筛选
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bug修复
|
||||
|
||||
1. ✅ 修复了下载文件路径问题
|
||||
2. ✅ 修复了Next.js API配置警告
|
||||
3. ✅ 修复了Prisma二进制目标问题
|
||||
4. ✅ 优化了文件名中文编码问题
|
||||
|
||||
---
|
||||
|
||||
## 📖 使用指南
|
||||
|
||||
### 上传素材(新流程)
|
||||
|
||||
1. **登录系统**
|
||||
- 使用您的账号登录
|
||||
|
||||
2. **点击"上传素材"按钮**
|
||||
|
||||
3. **填写信息**:
|
||||
- 选择预览图(支持JPG、PNG、WebP等)
|
||||
- 输入商品型号(如:XY-2024-001)
|
||||
- **选择一级分类**(如:家居、办公等)
|
||||
- 如果没有合适的,点击"新建分类"
|
||||
- **选择二级分类**(如:客厅、卧室等)
|
||||
- 如果没有合适的,点击"新建场景"
|
||||
- 选择压缩包(支持ZIP、RAR、7Z,最大10GB)
|
||||
|
||||
4. **开始上传**
|
||||
- 实时查看上传进度
|
||||
- 查看上传速度
|
||||
- 等待上传完成
|
||||
|
||||
5. **完成**
|
||||
- 上传成功后自动返回首页
|
||||
- 新上传的素材会出现在列表顶部
|
||||
|
||||
### 筛选和查找素材
|
||||
|
||||
1. **使用搜索框**
|
||||
- 输入关键词搜索
|
||||
- 支持搜索商品型号、分类、场景、作者
|
||||
|
||||
2. **使用一级分类筛选**
|
||||
- 点击分类按钮快速筛选
|
||||
- 再次点击取消筛选
|
||||
|
||||
3. **使用二级分类筛选**
|
||||
- 点击场景按钮筛选
|
||||
- 可与一级分类组合使用
|
||||
|
||||
4. **组合筛选**
|
||||
- 同时使用搜索、一级分类、二级分类
|
||||
- 查看当前筛选条件标签
|
||||
- 单独移除某个条件或一键清除
|
||||
|
||||
### 下载素材
|
||||
|
||||
1. 找到需要的素材卡片
|
||||
2. 点击"下载"按钮
|
||||
3. 浏览器会自动下载压缩包
|
||||
|
||||
---
|
||||
|
||||
## 🔧 技术栈
|
||||
|
||||
### 前端
|
||||
- Next.js 14.2.33
|
||||
- React 18
|
||||
- TypeScript
|
||||
- Tailwind CSS(支持黑暗模式)
|
||||
- NextAuth.js
|
||||
|
||||
### 后端
|
||||
- Next.js API Routes
|
||||
- Prisma ORM
|
||||
- PostgreSQL
|
||||
- S3对象存储(MinIO)
|
||||
|
||||
### 部署
|
||||
- Docker + Docker Compose
|
||||
- FRP内网穿透
|
||||
- TrueNAS(NAS服务器)
|
||||
|
||||
---
|
||||
|
||||
## 📝 API更新
|
||||
|
||||
### 新增API
|
||||
|
||||
**GET /api/categories**
|
||||
- 获取所有一级分类
|
||||
- 返回:`{ id, name, createdAt }[]`
|
||||
|
||||
**POST /api/categories**
|
||||
- 创建新的一级分类
|
||||
- 参数:`{ name: string }`
|
||||
- 返回:`{ id, name, createdAt }`
|
||||
|
||||
### 更新API
|
||||
|
||||
**POST /api/materials/upload**
|
||||
- 新增参数:`categoryName` (必填)
|
||||
- 其他参数保持不变
|
||||
|
||||
**GET /api/materials**
|
||||
- 新增查询参数:`?category=分类名`
|
||||
- 支持组合查询:`?category=家居&scene=客厅&search=关键词`
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署更新
|
||||
|
||||
### 本地开发
|
||||
|
||||
\`\`\`bash
|
||||
# 1. 拉取最新代码
|
||||
cd /Users/lanmu/Desktop/gongxiangp2
|
||||
|
||||
# 2. 安装依赖(如有新增)
|
||||
npm install
|
||||
|
||||
# 3. 生成Prisma Client
|
||||
npx prisma generate
|
||||
|
||||
# 4. 运行数据库迁移
|
||||
npx prisma db push
|
||||
|
||||
# 5. 启动开发服务器
|
||||
npm run dev
|
||||
\`\`\`
|
||||
|
||||
### NAS生产环境
|
||||
|
||||
\`\`\`bash
|
||||
# 1. 上传新代码到NAS
|
||||
sshpass -p 'jsjj8888' scp -r . root@192.168.58.20:/mnt/tank/apps/youyidian-3d/
|
||||
|
||||
# 2. SSH到NAS
|
||||
ssh root@192.168.58.20
|
||||
|
||||
# 3. 进入项目目录
|
||||
cd /mnt/tank/apps/youyidian-3d
|
||||
|
||||
# 4. 重新构建并启动
|
||||
docker compose down
|
||||
docker compose up -d --build
|
||||
|
||||
# 5. 运行数据库迁移
|
||||
docker compose exec app npx prisma db push
|
||||
|
||||
# 6. 检查状态
|
||||
docker compose ps
|
||||
docker compose logs --tail=50 app
|
||||
\`\`\`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 未来规划
|
||||
|
||||
### 短期(1-2周)
|
||||
- [ ] 添加批量下载功能
|
||||
- [ ] 添加素材收藏功能
|
||||
- [ ] 优化移动端体验
|
||||
|
||||
### 中期(1个月)
|
||||
- [ ] 添加素材评论功能
|
||||
- [ ] 添加素材标签系统
|
||||
- [ ] 实现高级搜索(多条件组合)
|
||||
|
||||
### 长期(3个月)
|
||||
- [ ] AI智能分类
|
||||
- [ ] 素材推荐系统
|
||||
- [ ] 数据统计和分析
|
||||
|
||||
---
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如有问题,请联系:
|
||||
- **开发者**:系统管理员
|
||||
- **更新时间**:2025-10-16
|
||||
- **版本**:v2.0.0
|
||||
|
||||
---
|
||||
|
||||
**感谢使用FLNXI素材库管理系统!** 🎉
|
||||
|
||||
|
||||
@ -1,279 +0,0 @@
|
||||
# 🔧 FRP 公网服务器诊断与修复报告
|
||||
|
||||
**服务器信息:**
|
||||
- IP地址:180.97.221.140
|
||||
- 用户名:root
|
||||
- 检查时间:2025年11月5日 13:48
|
||||
|
||||
---
|
||||
|
||||
## 📊 问题诊断
|
||||
|
||||
### ❌ 发现的问题
|
||||
|
||||
**严重错误:文件描述符数量耗尽**
|
||||
|
||||
1. **错误日志**:
|
||||
```
|
||||
[proxy.go:196] met temporary error: accept tcp: accept4: too many open files
|
||||
```
|
||||
|
||||
2. **根本原因**:
|
||||
- FRP 进程的文件描述符限制:**65,536**
|
||||
- 当前已打开的文件数:**65,536**(达到上限!)
|
||||
- 无法接受新的连接请求
|
||||
|
||||
3. **影响范围**:
|
||||
- ❌ youyidian-3d (2320端口) - 无法接受连接
|
||||
- ❌ nas-ssh (2322端口) - 无法接受连接
|
||||
- ❌ 其他所有代理服务都受影响
|
||||
|
||||
---
|
||||
|
||||
## 🔧 修复步骤
|
||||
|
||||
### 1. 备份原配置
|
||||
```bash
|
||||
cp /etc/systemd/system/frps.service /etc/systemd/system/frps.service.backup-20251105-134828
|
||||
```
|
||||
|
||||
### 2. 修改 systemd 服务配置
|
||||
**修改前:**
|
||||
```ini
|
||||
[Service]
|
||||
LimitNOFILE=65536
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```ini
|
||||
[Service]
|
||||
LimitNOFILE=1048576
|
||||
```
|
||||
|
||||
### 3. 应用配置并重启服务
|
||||
```bash
|
||||
# 重新加载 systemd 配置
|
||||
systemctl daemon-reload
|
||||
|
||||
# 重启 frps 服务
|
||||
systemctl restart frps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 修复验证
|
||||
|
||||
### 1. 进程状态
|
||||
- **新进程 ID**:4826
|
||||
- **文件描述符限制**:1,048,576(100万)✅
|
||||
- **当前打开文件数**:22(非常健康)✅
|
||||
- **运行状态**:active (running) ✅
|
||||
|
||||
### 2. 端口监听状态
|
||||
| 端口 | 状态 | 用途 |
|
||||
|------|------|------|
|
||||
| 7000 | ✅ 正常监听 | FRP 服务端主端口 |
|
||||
| 7500 | ✅ 正常监听 | Dashboard 管理界面 |
|
||||
| 2320 | ✅ 正常监听 | youyidian-3d 穿透 |
|
||||
| 2322 | ✅ 正常监听 | NAS SSH 穿透 |
|
||||
|
||||
### 3. 客户端连接状态
|
||||
所有客户端在服务重启后自动重新连接:
|
||||
|
||||
| 客户端 ID | 来源 IP | 代理服务 | 状态 |
|
||||
|-----------|---------|----------|------|
|
||||
| d5789e601269f688 | 58.216.227.194 | nas-ssh, youyidian-3d | ✅ 已连接 |
|
||||
| 3b9182d7c7dc5e78 | 180.111.177.153 | ssh-dao02 | ✅ 已连接 |
|
||||
| 1be0c673e19ea24a | 36.153.91.234 | bi2-* 系列 | ✅ 已连接 |
|
||||
|
||||
### 4. 外部连接测试
|
||||
```bash
|
||||
# NAS SSH 穿透测试
|
||||
nc -z -v -w5 180.97.221.140 2322
|
||||
✅ Connection succeeded!
|
||||
|
||||
# Web应用穿透测试
|
||||
nc -z -v -w5 180.97.221.140 2320
|
||||
✅ Connection succeeded!
|
||||
```
|
||||
|
||||
### 5. 最新日志(无错误)
|
||||
```
|
||||
2025/11/05 13:48:28 [I] frps started successfully
|
||||
2025/11/05 13:48:31 [I] new proxy [nas-ssh] type [tcp] success
|
||||
2025/11/05 13:48:31 [I] new proxy [youyidian-3d] type [tcp] success
|
||||
```
|
||||
|
||||
**✅ 没有再出现 "too many open files" 错误**
|
||||
|
||||
---
|
||||
|
||||
## 📝 当前 FRP 配置
|
||||
|
||||
### 服务端配置 (/etc/frp/frps.ini)
|
||||
```ini
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 7000
|
||||
dashboard_addr = 127.0.0.1
|
||||
dashboard_port = 7500
|
||||
dashboard_user = admin
|
||||
dashboard_pwd = Admin@frp2024
|
||||
authentication_method = token
|
||||
token = LiuYunKu_NAS_2024_SecureToken
|
||||
log_file = /var/log/frps.log
|
||||
log_level = info
|
||||
log_max_days = 7
|
||||
allow_ports = 2320-2400,5238,6000-6100,8000-8100
|
||||
max_pool_count = 50
|
||||
|
||||
# 上传优化配置
|
||||
heartbeat_timeout = 300
|
||||
tcp_mux = true
|
||||
tcp_mux_keepalive_interval = 60
|
||||
max_ports_per_client = 0
|
||||
transport_buffer_size = 16777216
|
||||
```
|
||||
|
||||
### Systemd 服务配置 (/etc/systemd/system/frps.service)
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Frp Server Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.ini
|
||||
LimitNOFILE=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 系统状态总结
|
||||
|
||||
### 硬件资源
|
||||
- **系统**:CentOS 7 (内核 3.10.0-957)
|
||||
- **FRP 版本**:0.52.3 (服务端)
|
||||
- **内存占用**:约 360MB
|
||||
- **运行时间**:重启后运行正常
|
||||
|
||||
### 系统限制
|
||||
- **系统级文件描述符限制**:1,000,000
|
||||
- **FRP进程限制**:1,048,576(已优化)✅
|
||||
- **当前使用**:22 / 1,048,576(0.002%)
|
||||
|
||||
### 网络连接
|
||||
- **活跃客户端**:3个
|
||||
- **活跃代理**:7个(nas-ssh, youyidian-3d, ssh-dao02, bi2-*)
|
||||
- **7000端口**:正常监听并接受连接
|
||||
|
||||
---
|
||||
|
||||
## 🔍 根本原因分析
|
||||
|
||||
### 为什么会出现 "too many open files"?
|
||||
|
||||
1. **限制过低**:原始限制 65,536 对于长时间运行的 FRP 服务器来说不足
|
||||
2. **连接累积**:FRP 作为穿透服务,会维持大量长连接
|
||||
3. **没有及时清理**:某些僵死连接可能没有被及时释放
|
||||
|
||||
### 为什么达到 65,536?
|
||||
|
||||
根据日志文件大小判断:
|
||||
- 每天日志约 15-27MB
|
||||
- 说明有大量的连接和传输活动
|
||||
- 多个客户端(至少3个)长期连接
|
||||
- 可能存在频繁的连接建立和断开
|
||||
|
||||
---
|
||||
|
||||
## 💡 后续建议
|
||||
|
||||
### 1. 定期监控
|
||||
建议设置监控脚本,定期检查文件描述符使用情况:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 保存为 /usr/local/bin/check-frps-health.sh
|
||||
|
||||
PROCESS_ID=$(pgrep frps)
|
||||
if [ -z "$PROCESS_ID" ]; then
|
||||
echo "❌ FRP服务未运行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FD_LIMIT=$(cat /proc/$PROCESS_ID/limits | grep "open files" | awk '{print $4}')
|
||||
FD_CURRENT=$(ls /proc/$PROCESS_ID/fd | wc -l)
|
||||
FD_PERCENT=$((FD_CURRENT * 100 / FD_LIMIT))
|
||||
|
||||
echo "文件描述符使用情况:"
|
||||
echo " 当前:$FD_CURRENT"
|
||||
echo " 限制:$FD_LIMIT"
|
||||
echo " 使用率:$FD_PERCENT%"
|
||||
|
||||
if [ $FD_PERCENT -gt 80 ]; then
|
||||
echo "⚠️ 警告:文件描述符使用率超过80%"
|
||||
fi
|
||||
```
|
||||
|
||||
### 2. 设置定时任务
|
||||
```bash
|
||||
# 每小时检查一次
|
||||
echo "0 * * * * /usr/local/bin/check-frps-health.sh >> /var/log/frps-health.log 2>&1" | crontab -
|
||||
```
|
||||
|
||||
### 3. 日志轮转
|
||||
当前日志每天自动分割,保留 7 天,配置合理 ✅
|
||||
|
||||
### 4. 升级建议
|
||||
服务端配置文件使用的是 `.ini` 格式,官方已弃用。建议未来升级到 `.toml` 或 `.yaml` 格式。
|
||||
|
||||
---
|
||||
|
||||
## ✅ 结论
|
||||
|
||||
**FRP 穿透服务已完全恢复正常!**
|
||||
|
||||
- ✅ 文件描述符限制已从 65,536 提升到 1,048,576
|
||||
- ✅ 服务成功重启,所有客户端自动重连
|
||||
- ✅ 所有代理服务正常工作
|
||||
- ✅ 不再出现 "too many open files" 错误
|
||||
- ✅ 外部端口连接测试全部通过
|
||||
|
||||
**您的 3D 共享平台(youyidian-3d)和 NAS SSH 穿透服务现已正常运行。**
|
||||
|
||||
---
|
||||
|
||||
## 📞 维护信息
|
||||
|
||||
### 相关命令
|
||||
```bash
|
||||
# 查看服务状态
|
||||
systemctl status frps
|
||||
|
||||
# 查看实时日志
|
||||
tail -f /var/log/frps.log
|
||||
|
||||
# 查看文件描述符使用
|
||||
ls /proc/$(pgrep frps)/fd | wc -l
|
||||
|
||||
# 重启服务
|
||||
systemctl restart frps
|
||||
```
|
||||
|
||||
### 备份文件位置
|
||||
- 配置备份:`/etc/systemd/system/frps.service.backup-20251105-134828`
|
||||
- 日志目录:`/var/log/frps*.log`
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**:2025年11月5日 13:50
|
||||
**操作人员**:AI 自动诊断与修复
|
||||
**服务器**:180.97.221.140
|
||||
**状态**:✅ 已修复并验证
|
||||
|
||||
@ -1,320 +0,0 @@
|
||||
# FRP穿透检查报告 - 3d.yydd.ai
|
||||
|
||||
**检查时间**:2025-11-05 21:26
|
||||
**公网服务器**:180.97.221.140
|
||||
**检查结果**:✅ **全部正常**
|
||||
|
||||
---
|
||||
|
||||
## 📊 检查结果总览
|
||||
|
||||
| 检查项 | 状态 | 详细信息 |
|
||||
|--------|------|----------|
|
||||
| 🌐 网络连通性 | ✅ 正常 | Ping延迟 6ms,丢包率 0% |
|
||||
| 🔧 FRPS服务 | ✅ 运行中 | 已运行 7小时+ |
|
||||
| 🎯 端口监听 | ✅ 正常 | 7000, 2320, 2322, 2323等 |
|
||||
| 🔗 活动连接 | ✅ 正常 | 3个FRP客户端连接 |
|
||||
| 📡 youyidian-3d | ✅ 正常 | 2320端口已监听并转发 |
|
||||
| 🌍 域名解析 | ✅ 正常 | 3d.yydd.ai → 180.97.221.140 |
|
||||
| 🔐 SSL证书 | ✅ 配置完成 | Let's Encrypt证书 |
|
||||
| 🚀 Nginx反向代理 | ✅ 正常 | HTTP→HTTPS重定向正常 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 核心配置详情
|
||||
|
||||
### 1. FRPS服务状态
|
||||
|
||||
```
|
||||
服务名称: frps.service
|
||||
状态: active (running)
|
||||
运行时间: 7小时+
|
||||
主进程PID: 4826
|
||||
配置文件: /etc/frp/frps.ini
|
||||
日志文件: /var/log/frps.log
|
||||
```
|
||||
|
||||
**关键配置**:
|
||||
```ini
|
||||
bind_port = 7000
|
||||
token = LiuYunKu_NAS_2024_SecureToken
|
||||
allow_ports = 2320-2400,5238,6000-6100,8000-8100
|
||||
heartbeat_timeout = 300
|
||||
tcp_mux = true
|
||||
max_ports_per_client = 0
|
||||
transport_buffer_size = 16777216 # 16MB 传输缓冲
|
||||
```
|
||||
|
||||
### 2. 端口监听情况
|
||||
|
||||
**FRP控制端口**:
|
||||
- `7000` - FRP主控制端口(已监听)
|
||||
- `7500` - Dashboard管理界面(127.0.0.1限制)
|
||||
|
||||
**业务端口**:
|
||||
- `2320` - youyidian-3d (FLNXI素材库平台)
|
||||
- `2322` - nas-ssh
|
||||
- `2323` - 其他服务
|
||||
- `6000`, `6002` - 其他FRP服务
|
||||
- `8000`, `8080` - 其他HTTP服务
|
||||
|
||||
### 3. youyidian-3d 穿透详情
|
||||
|
||||
**NAS侧(192.168.58.20)**:
|
||||
```
|
||||
服务端口: 4320 (Docker容器)
|
||||
FRP客户端: frpc-youyidian (运行中)
|
||||
本地配置: 4320 → 公网 2320
|
||||
```
|
||||
|
||||
**公网侧(180.97.221.140)**:
|
||||
```
|
||||
接收端口: 2320
|
||||
监听状态: 已监听
|
||||
Nginx反向代理: 2320 → 443 (HTTPS)
|
||||
域名绑定: 3d.yydd.ai
|
||||
```
|
||||
|
||||
**流量路径**:
|
||||
```
|
||||
用户浏览器
|
||||
↓ HTTPS (443)
|
||||
3d.yydd.ai (180.97.221.140)
|
||||
↓ Nginx反向代理
|
||||
localhost:2320 (公网服务器)
|
||||
↓ FRP隧道
|
||||
192.168.58.20:4320 (NAS Docker容器)
|
||||
↓ Next.js应用
|
||||
FLNXI素材库平台
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Nginx配置检查
|
||||
|
||||
### HTTP → HTTPS 强制重定向
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name 3d.yydd.ai;
|
||||
return 301 https://3d.yydd.ai$request_uri;
|
||||
}
|
||||
```
|
||||
✅ 状态:已配置,自动跳转HTTPS
|
||||
|
||||
### HTTPS反向代理配置
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name 3d.yydd.ai;
|
||||
|
||||
# SSL证书
|
||||
ssl_certificate /etc/letsencrypt/live/3d.yydd.ai/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/3d.yydd.ai/privkey.pem;
|
||||
|
||||
# 大文件上传配置
|
||||
client_max_body_size 0; # 无限制
|
||||
client_body_timeout 10800s; # 3小时超时
|
||||
|
||||
# 反向代理
|
||||
location / {
|
||||
proxy_pass http://localhost:2320;
|
||||
proxy_http_version 1.1;
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
|
||||
# 超时设置(支持大文件上传)
|
||||
proxy_connect_timeout 10800s; # 3小时
|
||||
proxy_send_timeout 10800s; # 3小时
|
||||
proxy_read_timeout 10800s; # 3小时
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**配置评估**:
|
||||
- ✅ SSL/TLS 加密:使用 Let's Encrypt 证书
|
||||
- ✅ HTTP/2 支持:已启用
|
||||
- ✅ 大文件上传:支持10GB文件(无限制)
|
||||
- ✅ 超时设置:3小时,适配大文件上传
|
||||
- ✅ 安全头:HSTS, X-Frame-Options等已配置
|
||||
- ✅ 缓冲关闭:适合流式上传
|
||||
|
||||
---
|
||||
|
||||
## 📊 连接状态统计
|
||||
|
||||
### FRP活动连接
|
||||
```
|
||||
总连接数: 3个
|
||||
控制连接: 7000端口
|
||||
代理连接: 包括 youyidian-3d, nas-ssh, bi2-ssh 等
|
||||
```
|
||||
|
||||
### Nginx活动连接
|
||||
```
|
||||
到2320端口的连接: 150+ 个
|
||||
Nginx worker进程: 4个
|
||||
连接状态: ESTABLISHED(活跃)
|
||||
```
|
||||
|
||||
**连接来源分析**:
|
||||
- `127.0.0.1` - 本地Nginx反向代理(主要流量)
|
||||
- `::1` (IPv6本地) - IPv6本地连接
|
||||
- `113.71.249.10` - 外部访问(真实用户)
|
||||
- `193.32.162.110` - SSH连接(bi2-ssh服务)
|
||||
|
||||
---
|
||||
|
||||
## 🌍 域名和DNS配置
|
||||
|
||||
### 域名信息
|
||||
```
|
||||
域名: 3d.yydd.ai
|
||||
解析IP: 180.97.221.140
|
||||
DNS类型: A记录
|
||||
TTL: 默认
|
||||
```
|
||||
|
||||
### 访问地址
|
||||
- ✅ HTTP: http://3d.yydd.ai → 自动跳转HTTPS
|
||||
- ✅ HTTPS: https://3d.yydd.ai → 正常访问
|
||||
- ✅ 内网: http://192.168.58.20:4320 → 正常访问(NAS内网)
|
||||
|
||||
---
|
||||
|
||||
## 🔍 详细日志分析
|
||||
|
||||
### FRPS最近活动(最后30条日志)
|
||||
|
||||
**正常日志**:
|
||||
```log
|
||||
2025/11/05 21:21:21 [I] [proxy.go:204] [youyidian-3d] get a user connection
|
||||
2025/11/05 21:21:47 [I] [proxy.go:204] [youyidian-3d] get a user connection
|
||||
2025/11/05 21:22:11 [I] [proxy.go:204] [youyidian-3d] get a user connection
|
||||
```
|
||||
✅ 持续接收用户连接请求
|
||||
|
||||
**警告日志**(可忽略):
|
||||
```log
|
||||
[W] [control.go:495] new proxy [youyidian-3d] type [tcp] error: proxy already exists
|
||||
```
|
||||
✅ 这是重连时的正常警告,不影响使用
|
||||
|
||||
### 日志结论
|
||||
- ✅ youyidian-3d 代理工作正常
|
||||
- ✅ 持续接收并处理用户请求
|
||||
- ✅ 无错误或异常断连
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总体评估
|
||||
|
||||
### ✅ 优点
|
||||
1. **FRP穿透稳定**:服务运行7小时+无中断
|
||||
2. **配置完善**:大文件上传、长连接支持完整
|
||||
3. **安全可靠**:HTTPS加密、安全头配置齐全
|
||||
4. **性能优化**:HTTP/2、TCP多路复用、大缓冲区
|
||||
5. **监控完善**:日志记录详细,便于问题排查
|
||||
|
||||
### 📈 性能指标
|
||||
- 网络延迟: **6ms**(极佳)
|
||||
- 丢包率: **0%**(完美)
|
||||
- 服务可用性: **100%**
|
||||
- SSL握手: 正常
|
||||
- HTTP/2: 已启用
|
||||
|
||||
### 🔧 配置亮点
|
||||
1. **上传优化**:
|
||||
- 无文件大小限制
|
||||
- 3小时超时设置
|
||||
- 16MB传输缓冲区
|
||||
- 关闭代理缓冲(流式传输)
|
||||
|
||||
2. **连接优化**:
|
||||
- TCP多路复用
|
||||
- 心跳超时300秒
|
||||
- 无客户端端口限制
|
||||
|
||||
3. **安全加固**:
|
||||
- Token认证
|
||||
- HTTPS强制
|
||||
- HSTS头
|
||||
- X-Frame-Options防护
|
||||
|
||||
---
|
||||
|
||||
## 🚀 可访问地址
|
||||
|
||||
### 公网访问
|
||||
- **HTTPS(推荐)**: https://3d.yydd.ai
|
||||
- **HTTP(自动跳转)**: http://3d.yydd.ai
|
||||
|
||||
### 内网访问(NAS局域网内)
|
||||
- **直接访问**: http://192.168.58.20:4320
|
||||
|
||||
### 管理界面
|
||||
- **FRP Dashboard**: http://180.97.221.140:7500
|
||||
- 用户名: admin
|
||||
- 密码: Admin@frp2024
|
||||
- 注意:仅127.0.0.1可访问(需SSH隧道)
|
||||
|
||||
---
|
||||
|
||||
## 📋 检查命令记录
|
||||
|
||||
```bash
|
||||
# 1. 检查FRPS服务状态
|
||||
systemctl status frps
|
||||
|
||||
# 2. 查看FRP配置
|
||||
cat /etc/frp/frps.ini
|
||||
|
||||
# 3. 检查端口监听
|
||||
netstat -tlnp | grep frps
|
||||
|
||||
# 4. 查看活动连接
|
||||
netstat -an | grep :7000 | grep ESTABLISHED | wc -l
|
||||
|
||||
# 5. 查看最近日志
|
||||
tail -30 /var/log/frps.log
|
||||
|
||||
# 6. 检查Nginx配置
|
||||
cat /etc/nginx/conf.d/3d-yydd-ai.conf
|
||||
|
||||
# 7. 验证域名解析
|
||||
nslookup 3d.yydd.ai
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 结论
|
||||
|
||||
**FRP穿透配置完全正常,无任何问题!**
|
||||
|
||||
所有服务运行稳定:
|
||||
- ✅ FRPS服务正常运行
|
||||
- ✅ youyidian-3d代理工作正常
|
||||
- ✅ Nginx反向代理配置正确
|
||||
- ✅ SSL证书有效
|
||||
- ✅ 域名解析正常
|
||||
- ✅ 网络连接稳定
|
||||
|
||||
**系统可以正常对外提供服务,支持:**
|
||||
- ✅ HTTPS安全访问
|
||||
- ✅ 10GB大文件上传
|
||||
- ✅ 2小时长连接保持
|
||||
- ✅ 钉钉登录功能
|
||||
- ✅ 完整的3D素材管理功能
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**:2025-11-05 21:26
|
||||
**下次检查建议**:定期查看 /var/log/frps.log 确保服务稳定
|
||||
**监控建议**:可考虑配置监控告警,及时发现服务异常
|
||||
|
||||
**🎊 FRP穿透工作完美!系统运行正常!** 🚀
|
||||
|
||||
|
||||
|
||||
@ -1,236 +0,0 @@
|
||||
# 🚨 FRP服务紧急恢复指南
|
||||
|
||||
## 问题说明
|
||||
|
||||
FRP客户端配置修改后无法连接,导致:
|
||||
- ❌ 网站无法访问(502错误)
|
||||
- ❌ SSH穿透断开(2322端口不可用)
|
||||
- ❌ 需要通过本地网络恢复
|
||||
|
||||
---
|
||||
|
||||
## 🔧 恢复步骤
|
||||
|
||||
### 方法1:通过本地网络SSH恢复(推荐)
|
||||
|
||||
1. **确保在同一局域网**
|
||||
- 连接到与NAS相同的网络
|
||||
|
||||
2. **SSH登录NAS**
|
||||
```bash
|
||||
# 使用NAS的本地IP(请替换为实际IP,如 192.168.1.x)
|
||||
ssh root@<NAS本地IP>
|
||||
```
|
||||
|
||||
3. **恢复备份配置**
|
||||
```bash
|
||||
# 查看备份文件
|
||||
ls -lh /mnt/tank/apps/frp/frpc.toml.backup-*
|
||||
|
||||
# 恢复最新备份(替换时间戳)
|
||||
cp /mnt/tank/apps/frp/frpc.toml.backup-20251030-* /mnt/tank/apps/frp/frpc.toml
|
||||
|
||||
# 重启FRP容器
|
||||
docker restart frpc-youyidian
|
||||
|
||||
# 等待3秒
|
||||
sleep 3
|
||||
|
||||
# 检查状态
|
||||
docker ps | grep frpc
|
||||
docker logs --tail 20 frpc-youyidian
|
||||
```
|
||||
|
||||
4. **验证恢复**
|
||||
```bash
|
||||
# 应该能看到 "login to server success"
|
||||
# 等待10秒后,从外网测试
|
||||
curl -I https://3d.yydd.ai
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方法2:通过TrueNAS Web界面恢复
|
||||
|
||||
1. **访问TrueNAS管理界面**
|
||||
- 浏览器打开:`http://<NAS本地IP>`
|
||||
- 登录管理账户
|
||||
|
||||
2. **打开Shell**
|
||||
- 顶部菜单:System → Shell
|
||||
|
||||
3. **执行恢复命令**
|
||||
```bash
|
||||
# 恢复配置
|
||||
cp /mnt/tank/apps/frp/frpc.toml.backup-20251030-* /mnt/tank/apps/frp/frpc.toml
|
||||
|
||||
# 重启容器
|
||||
docker restart frpc-youyidian
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方法3:手动编辑正确的配置
|
||||
|
||||
如果备份也有问题,使用原始配置:
|
||||
|
||||
1. **编辑配置文件**
|
||||
```bash
|
||||
vi /mnt/tank/apps/frp/frpc.toml
|
||||
```
|
||||
|
||||
2. **使用以下内容**(原始配置):
|
||||
```toml
|
||||
serverAddr = "180.97.221.140"
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "youyidian-3d"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 4320
|
||||
remotePort = 2320
|
||||
|
||||
[[proxies]]
|
||||
name = "nas-ssh"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
remotePort = 2322
|
||||
|
||||
[auth]
|
||||
method = "token"
|
||||
token = "LiuYunKu_NAS_2024_SecureToken"
|
||||
|
||||
[log]
|
||||
to = "/mnt/tank/apps/frp/frpc.log"
|
||||
level = "info"
|
||||
maxDays = 3
|
||||
```
|
||||
|
||||
3. **保存并重启**
|
||||
```bash
|
||||
# 保存后退出 vi(按 ESC,然后 :wq)
|
||||
docker restart frpc-youyidian
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证恢复成功
|
||||
|
||||
### 1. 检查容器日志
|
||||
```bash
|
||||
docker logs --tail 20 frpc-youyidian
|
||||
```
|
||||
|
||||
**应该看到**:
|
||||
```
|
||||
login to server success
|
||||
start proxy success
|
||||
```
|
||||
|
||||
### 2. 从外网测试
|
||||
```bash
|
||||
# 测试网站
|
||||
curl -I https://3d.yydd.ai
|
||||
|
||||
# 测试SSH
|
||||
ssh -o ConnectTimeout=10 root@180.97.221.140 -p 2322
|
||||
```
|
||||
|
||||
**应该返回**:
|
||||
- 网站:HTTP/2 200 或 301/302
|
||||
- SSH:能够连接并输入密码
|
||||
|
||||
---
|
||||
|
||||
## 🎯 恢复后的优化(可选)
|
||||
|
||||
服务恢复后,如果还想优化FRP配置,请使用**测试过的配置**:
|
||||
|
||||
```toml
|
||||
serverAddr = "180.97.221.140"
|
||||
serverPort = 7000
|
||||
|
||||
# 传输优化(可选)
|
||||
[transport]
|
||||
heartbeatInterval = 30
|
||||
heartbeatTimeout = 90
|
||||
tcpMux = true
|
||||
poolCount = 5
|
||||
|
||||
[[proxies]]
|
||||
name = "youyidian-3d"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 4320
|
||||
remotePort = 2320
|
||||
transport.useEncryption = false
|
||||
transport.useCompression = false
|
||||
|
||||
[[proxies]]
|
||||
name = "nas-ssh"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
remotePort = 2322
|
||||
|
||||
[auth]
|
||||
method = "token"
|
||||
token = "LiuYunKu_NAS_2024_SecureToken"
|
||||
|
||||
[log]
|
||||
to = "/mnt/tank/apps/frp/frpc.log"
|
||||
level = "info"
|
||||
maxDays = 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 预防措施
|
||||
|
||||
以后修改FRP配置时:
|
||||
|
||||
1. **先测试连接**
|
||||
```bash
|
||||
# 测试配置文件语法
|
||||
docker exec frpc-youyidian frpc verify -c /etc/frp/frpc.toml
|
||||
```
|
||||
|
||||
2. **保留备份窗口**
|
||||
- 修改后不要立即关闭SSH连接
|
||||
- 等待30秒确认FRP重新连接成功
|
||||
|
||||
3. **监控日志**
|
||||
```bash
|
||||
docker logs -f frpc-youyidian
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 需要帮助?
|
||||
|
||||
如果恢复遇到问题:
|
||||
|
||||
1. **找不到NAS本地IP**
|
||||
- 登录路由器查看设备列表
|
||||
- 或在TrueNAS物理屏幕上查看
|
||||
|
||||
2. **无法访问NAS**
|
||||
- 确保在同一局域网
|
||||
- 检查防火墙设置
|
||||
|
||||
3. **容器无法启动**
|
||||
```bash
|
||||
# 查看完整错误
|
||||
docker logs frpc-youyidian
|
||||
|
||||
# 重新创建容器(使用原配置)
|
||||
docker stop frpc-youyidian
|
||||
docker start frpc-youyidian
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**恢复后请告诉我结果,我将继续帮您解决上传问题!** 🚀
|
||||
|
||||
@ -1,594 +0,0 @@
|
||||
# 🛡️ Fail2ban 使用指南
|
||||
|
||||
**安装时间**:2025年11月5日 14:20
|
||||
**版本**:Fail2Ban v0.11.2
|
||||
**状态**:✅ 运行中并正在保护
|
||||
|
||||
---
|
||||
|
||||
## 🎉 安装成功摘要
|
||||
|
||||
### 立即生效的保护
|
||||
|
||||
✅ **Fail2ban 已在运行!**
|
||||
|
||||
```
|
||||
刚安装就已经封禁了 2 个攻击者:
|
||||
├─ 45.140.17.124 ← 尝试暴力破解
|
||||
├─ 196.251.69.73 ← 尝试暴力破解
|
||||
└─ 检测到 24 次失败登录尝试
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 当前配置
|
||||
|
||||
### 基本设置
|
||||
|
||||
| 配置项 | 值 | 说明 |
|
||||
|-------|---|------|
|
||||
| **最大重试次数** | 5 次 | 5次失败就封禁 |
|
||||
| **查找时间窗口** | 300 秒(5分钟) | 5分钟内计算失败次数 |
|
||||
| **封禁时间** | 600 秒(10分钟) | 封禁10分钟后自动解封 |
|
||||
| **保护服务** | SSH (sshd) | SSH登录保护 |
|
||||
| **白名单** | 58.216.0.0/16 | 您的管理IP不会被封 |
|
||||
|
||||
### 工作原理示例
|
||||
|
||||
```
|
||||
攻击者 1.2.3.4 尝试登录:
|
||||
|
||||
00:00 - 尝试 root/123456 ❌ 失败 (1/5)
|
||||
00:01 - 尝试 root/password ❌ 失败 (2/5)
|
||||
00:02 - 尝试 root/admin ❌ 失败 (3/5)
|
||||
00:03 - 尝试 root/12345678 ❌ 失败 (4/5)
|
||||
00:04 - 尝试 root/qwerty ❌ 失败 (5/5)
|
||||
↓
|
||||
🚫 立即封禁 1.2.3.4
|
||||
⏰ 10分钟后自动解封
|
||||
📝 记录到日志
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 常用管理命令
|
||||
|
||||
### 查看状态
|
||||
|
||||
#### 1. 查看总体状态
|
||||
```bash
|
||||
fail2ban-client status
|
||||
```
|
||||
|
||||
**输出示例**:
|
||||
```
|
||||
Status
|
||||
|- Number of jail: 1
|
||||
`- Jail list: sshd
|
||||
```
|
||||
|
||||
#### 2. 查看 SSH 详细状态
|
||||
```bash
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
**输出示例**:
|
||||
```
|
||||
Status for the jail: sshd
|
||||
|- Filter
|
||||
| |- Currently failed: 6 ← 当前检测到的失败次数
|
||||
| |- Total failed: 24 ← 总共失败次数
|
||||
`- Actions
|
||||
|- Currently banned: 2 ← 当前封禁的IP数量
|
||||
|- Total banned: 2 ← 总共封禁过的IP数量
|
||||
`- Banned IP list: 45.140.17.124 196.251.69.73
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 手动管理
|
||||
|
||||
#### 3. 手动封禁IP
|
||||
```bash
|
||||
# 封禁某个IP
|
||||
fail2ban-client set sshd banip 1.2.3.4
|
||||
|
||||
# 示例
|
||||
fail2ban-client set sshd banip 8.8.8.8
|
||||
```
|
||||
|
||||
#### 4. 手动解封IP
|
||||
```bash
|
||||
# 解封某个IP
|
||||
fail2ban-client set sshd unbanip 1.2.3.4
|
||||
|
||||
# 示例(如果误封了某个IP)
|
||||
fail2ban-client set sshd unbanip 58.216.201.86
|
||||
```
|
||||
|
||||
#### 5. 查看当前被封禁的IP
|
||||
```bash
|
||||
fail2ban-client get sshd banip
|
||||
```
|
||||
|
||||
或者使用 iptables 查看:
|
||||
```bash
|
||||
iptables -L f2b-sshd -n -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 日志查看
|
||||
|
||||
#### 6. 查看 Fail2ban 日志
|
||||
```bash
|
||||
# 查看最新日志
|
||||
tail -50 /var/log/fail2ban.log
|
||||
|
||||
# 实时监控日志
|
||||
tail -f /var/log/fail2ban.log
|
||||
|
||||
# 查看今天的封禁记录
|
||||
grep "Ban" /var/log/fail2ban.log | grep "$(date +%Y-%m-%d)"
|
||||
|
||||
# 查看今天的解封记录
|
||||
grep "Unban" /var/log/fail2ban.log | grep "$(date +%Y-%m-%d)"
|
||||
```
|
||||
|
||||
**日志示例**:
|
||||
```
|
||||
2025-11-05 14:20:50 [sshd] Ban 196.251.69.73 ← 封禁
|
||||
2025-11-05 14:30:50 [sshd] Unban 196.251.69.73 ← 解封
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 服务控制
|
||||
|
||||
#### 7. 重启 Fail2ban
|
||||
```bash
|
||||
systemctl restart fail2ban
|
||||
```
|
||||
|
||||
#### 8. 停止 Fail2ban
|
||||
```bash
|
||||
systemctl stop fail2ban
|
||||
```
|
||||
|
||||
#### 9. 启动 Fail2ban
|
||||
```bash
|
||||
systemctl start fail2ban
|
||||
```
|
||||
|
||||
#### 10. 查看服务状态
|
||||
```bash
|
||||
systemctl status fail2ban
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 防护效果对比
|
||||
|
||||
### 安装前 vs 安装后
|
||||
|
||||
#### 安装前(只有我们的防火墙脚本)
|
||||
|
||||
```
|
||||
攻击者 A:
|
||||
00:00 - 开始攻击
|
||||
00:30 - 持续攻击中(每分钟10次)
|
||||
01:00 - 我们的脚本运行,发现并封禁
|
||||
结果:攻击持续了 1 小时
|
||||
|
||||
负载:
|
||||
- SSH服务需要处理 600 次攻击请求
|
||||
- 日志文件增长 600 条记录
|
||||
```
|
||||
|
||||
#### 安装后(双重防护)
|
||||
|
||||
```
|
||||
攻击者 A:
|
||||
00:00 - 开始攻击
|
||||
00:01 - 失败 5 次
|
||||
00:01 - Fail2ban 立即封禁 ✅
|
||||
结果:攻击只持续了 1 分钟
|
||||
|
||||
负载:
|
||||
- SSH服务只需处理 5 次攻击请求
|
||||
- 日志文件只增长 5 条记录
|
||||
- 服务器负载降低 99%+ ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 双层防护体系
|
||||
|
||||
### 我们现在有两层保护
|
||||
|
||||
```
|
||||
攻击者
|
||||
↓
|
||||
┌─────────────────┐
|
||||
│ 第一层防护 │
|
||||
│ Fail2ban │
|
||||
│ (实时保护) │
|
||||
├─────────────────┤
|
||||
│ • 秒级响应 │
|
||||
│ • 5次失败封禁 │
|
||||
│ • 10分钟自动解封 │
|
||||
│ • 专门针对SSH │
|
||||
└─────────────────┘
|
||||
↓
|
||||
通过了第一层
|
||||
(持续攻击者)
|
||||
↓
|
||||
┌─────────────────┐
|
||||
│ 第二层防护 │
|
||||
│ 防火墙脚本 │
|
||||
│ (长期防护) │
|
||||
├─────────────────┤
|
||||
│ • 每小时巡逻 │
|
||||
│ • 100次封禁 │
|
||||
│ • 永久封禁 │
|
||||
│ • 保护所有服务 │
|
||||
└─────────────────┘
|
||||
↓
|
||||
您的服务器
|
||||
(安全无虞) ✅
|
||||
```
|
||||
|
||||
### 协同效果
|
||||
|
||||
| 场景 | Fail2ban | 防火墙脚本 | 结果 |
|
||||
|-----|----------|-----------|------|
|
||||
| **新攻击者** | ✅ 1分钟封禁 | - | 快速拦截 |
|
||||
| **持续攻击** | 10分钟后解封 | ✅ 永久封禁 | 彻底拦截 |
|
||||
| **慢速扫描** | ✅ 捕获 | ✅ 捕获 | 双重保险 |
|
||||
| **大规模攻击** | ✅ 实时拦截 | ✅ 批量封禁 | 完美防护 |
|
||||
|
||||
---
|
||||
|
||||
## 📈 实时监控
|
||||
|
||||
### 创建监控脚本
|
||||
|
||||
```bash
|
||||
# 创建监控脚本
|
||||
cat > /root/fail2ban-monitor.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# Fail2ban 监控脚本
|
||||
|
||||
echo "========================================"
|
||||
echo " Fail2ban 实时监控"
|
||||
echo " $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
echo "📊 总体状态:"
|
||||
fail2ban-client status
|
||||
echo ""
|
||||
|
||||
echo "🔍 SSH 详细信息:"
|
||||
fail2ban-client status sshd
|
||||
echo ""
|
||||
|
||||
echo "🚫 当前被封禁的IP(iptables视图):"
|
||||
iptables -L f2b-sshd -n -v --line-numbers | head -20
|
||||
echo ""
|
||||
|
||||
echo "📝 最近10次封禁记录:"
|
||||
grep "Ban" /var/log/fail2ban.log | tail -10
|
||||
echo ""
|
||||
|
||||
echo "🔓 最近10次解封记录:"
|
||||
grep "Unban" /var/log/fail2ban.log | tail -10
|
||||
echo ""
|
||||
|
||||
echo "📈 今天的统计:"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
BAN_COUNT=$(grep "Ban" /var/log/fail2ban.log | grep "$TODAY" | wc -l)
|
||||
UNBAN_COUNT=$(grep "Unban" /var/log/fail2ban.log | grep "$TODAY" | wc -l)
|
||||
echo " 封禁次数:$BAN_COUNT"
|
||||
echo " 解封次数:$UNBAN_COUNT"
|
||||
echo ""
|
||||
|
||||
echo "✅ 监控完成"
|
||||
EOF
|
||||
|
||||
chmod +x /root/fail2ban-monitor.sh
|
||||
```
|
||||
|
||||
**使用方法**:
|
||||
```bash
|
||||
# SSH 登录服务器后执行
|
||||
/root/fail2ban-monitor.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 配置文件位置
|
||||
|
||||
### 重要文件
|
||||
|
||||
| 文件 | 路径 | 说明 |
|
||||
|-----|------|------|
|
||||
| **主配置** | `/etc/fail2ban/jail.local` | 我们创建的配置 |
|
||||
| **默认配置** | `/etc/fail2ban/jail.conf` | 不要修改 |
|
||||
| **日志文件** | `/var/log/fail2ban.log` | Fail2ban日志 |
|
||||
| **SSH日志** | `/var/log/secure` | SSH登录日志 |
|
||||
|
||||
### 修改配置
|
||||
|
||||
如果需要调整配置:
|
||||
|
||||
```bash
|
||||
# 编辑配置文件
|
||||
vi /etc/fail2ban/jail.local
|
||||
|
||||
# 修改后重新加载
|
||||
fail2ban-client reload
|
||||
|
||||
# 或者重启服务
|
||||
systemctl restart fail2ban
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 常见场景
|
||||
|
||||
### 场景 1:查看谁在攻击我
|
||||
|
||||
```bash
|
||||
# 查看当前被封禁的IP
|
||||
fail2ban-client status sshd
|
||||
|
||||
# 查看今天所有被封禁的IP
|
||||
grep "Ban" /var/log/fail2ban.log | grep "$(date +%Y-%m-%d)" | awk '{print $NF}'
|
||||
```
|
||||
|
||||
### 场景 2:误封了自己的IP
|
||||
|
||||
```bash
|
||||
# 解封IP
|
||||
fail2ban-client set sshd unbanip 您的IP
|
||||
|
||||
# 将您的IP加入白名单(编辑配置)
|
||||
vi /etc/fail2ban/jail.local
|
||||
# 在 ignoreip 行添加您的IP
|
||||
# ignoreip = 127.0.0.1/8 ::1 58.216.0.0/16 您的IP
|
||||
|
||||
# 重新加载
|
||||
fail2ban-client reload
|
||||
```
|
||||
|
||||
### 场景 3:查看攻击统计
|
||||
|
||||
```bash
|
||||
# 今天的封禁统计
|
||||
echo "今天封禁IP数量:"
|
||||
grep "Ban" /var/log/fail2ban.log | grep "$(date +%Y-%m-%d)" | wc -l
|
||||
|
||||
# 历史总封禁数量
|
||||
echo "历史总封禁次数:"
|
||||
grep "Ban" /var/log/fail2ban.log | wc -l
|
||||
|
||||
# 最常被封禁的IP(Top 10)
|
||||
grep "Ban" /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -10
|
||||
```
|
||||
|
||||
### 场景 4:临时禁用 Fail2ban
|
||||
|
||||
```bash
|
||||
# 停止服务(紧急情况)
|
||||
systemctl stop fail2ban
|
||||
|
||||
# 恢复服务
|
||||
systemctl start fail2ban
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 当前运行状态
|
||||
|
||||
### 实时数据(安装时)
|
||||
|
||||
```
|
||||
✅ 服务状态:运行中
|
||||
✅ SSH保护:已启用
|
||||
📊 检测到失败:24 次
|
||||
🚫 已封禁IP:2 个
|
||||
├─ 45.140.17.124
|
||||
└─ 196.251.69.73
|
||||
⏰ 封禁时长:10 分钟
|
||||
🔄 自动解封:是
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 效果预测
|
||||
|
||||
### 预期改善
|
||||
|
||||
基于您服务器每天约 22,000 次 SSH 攻击:
|
||||
|
||||
#### 安装前
|
||||
```
|
||||
日均SSH攻击请求:~22,000 次
|
||||
服务器处理负载:高
|
||||
日志增长速度:快
|
||||
```
|
||||
|
||||
#### 安装后(预计)
|
||||
```
|
||||
日均SSH攻击请求:~1,000 次(减少 95%+)
|
||||
服务器处理负载:低 ✅
|
||||
日志增长速度:慢 ✅
|
||||
```
|
||||
|
||||
### 资源节省
|
||||
|
||||
```
|
||||
CPU 使用率:降低 5-10%
|
||||
内存占用:基本不变
|
||||
磁盘 I/O:大幅降低(日志减少)
|
||||
网络带宽:略微降低
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 白名单保护
|
||||
|
||||
当前白名单:
|
||||
- `127.0.0.1/8` - 本地地址
|
||||
- `58.216.0.0/16` - 您的管理IP段
|
||||
|
||||
**如果您的IP不在这个范围内**,建议添加到白名单:
|
||||
```bash
|
||||
vi /etc/fail2ban/jail.local
|
||||
# 修改 ignoreip 行
|
||||
fail2ban-client reload
|
||||
```
|
||||
|
||||
### 2. 自动解封机制
|
||||
|
||||
- 封禁IP会在 10 分钟后自动解封
|
||||
- 如果再次攻击,会再次被封禁
|
||||
- 累犯会被我们的防火墙脚本永久封禁
|
||||
|
||||
### 3. 与防火墙脚本的配合
|
||||
|
||||
两个系统互不冲突:
|
||||
- Fail2ban 管理短期封禁(10分钟)
|
||||
- 防火墙脚本管理永久封禁(累犯)
|
||||
|
||||
---
|
||||
|
||||
## 🔍 故障排查
|
||||
|
||||
### 问题 1:Fail2ban 没有封禁任何IP
|
||||
|
||||
**检查步骤**:
|
||||
```bash
|
||||
# 1. 检查服务是否运行
|
||||
systemctl status fail2ban
|
||||
|
||||
# 2. 检查配置是否正确
|
||||
fail2ban-client status
|
||||
|
||||
# 3. 查看日志是否有错误
|
||||
tail -50 /var/log/fail2ban.log
|
||||
```
|
||||
|
||||
### 问题 2:无法SSH登录
|
||||
|
||||
**可能原因**:自己的IP被封禁了
|
||||
|
||||
**解决方法**:
|
||||
```bash
|
||||
# 从其他机器SSH登录,或使用VNC等方式
|
||||
# 解封您的IP
|
||||
fail2ban-client set sshd unbanip 您的IP
|
||||
|
||||
# 添加到白名单
|
||||
vi /etc/fail2ban/jail.local
|
||||
fail2ban-client reload
|
||||
```
|
||||
|
||||
### 问题 3:服务无法启动
|
||||
|
||||
**检查步骤**:
|
||||
```bash
|
||||
# 查看详细错误
|
||||
systemctl status fail2ban -l
|
||||
|
||||
# 检查配置文件语法
|
||||
fail2ban-client -t
|
||||
|
||||
# 查看系统日志
|
||||
journalctl -u fail2ban -n 50
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 进阶配置(可选)
|
||||
|
||||
### 调整封禁参数
|
||||
|
||||
**更严格的保护**(3次失败就封禁):
|
||||
```bash
|
||||
vi /etc/fail2ban/jail.local
|
||||
|
||||
# 修改
|
||||
maxretry = 3 # 原来是 5
|
||||
findtime = 180 # 3分钟内
|
||||
bantime = 1800 # 封禁30分钟
|
||||
|
||||
# 重新加载
|
||||
fail2ban-client reload
|
||||
```
|
||||
|
||||
**更宽松的保护**(10次失败才封禁):
|
||||
```bash
|
||||
maxretry = 10
|
||||
findtime = 600
|
||||
bantime = 300
|
||||
```
|
||||
|
||||
### 添加邮件通知
|
||||
|
||||
```bash
|
||||
vi /etc/fail2ban/jail.local
|
||||
|
||||
# 修改
|
||||
destemail = your-email@example.com
|
||||
action = %(action_mwl)s # 发送邮件并包含日志
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
### 安装成功 ✅
|
||||
|
||||
- ✅ Fail2ban v0.11.2 已安装
|
||||
- ✅ SSH 保护已启用
|
||||
- ✅ 已封禁 2 个攻击者
|
||||
- ✅ 服务正在运行
|
||||
- ✅ 自动启动已配置
|
||||
|
||||
### 当前防护体系
|
||||
|
||||
```
|
||||
1️⃣ Fail2ban(实时防护)
|
||||
├─ 响应速度:秒级
|
||||
├─ 封禁条件:5次失败
|
||||
└─ 封禁时长:10分钟
|
||||
|
||||
2️⃣ 防火墙脚本(长期防护)
|
||||
├─ 响应速度:每小时
|
||||
├─ 封禁条件:100次连接
|
||||
└─ 封禁时长:永久
|
||||
|
||||
3️⃣ 强密码
|
||||
└─ 12位随机密码
|
||||
|
||||
= 三重防护 🛡️🛡️🛡️
|
||||
```
|
||||
|
||||
### 下一步建议
|
||||
|
||||
1. **每周查看一次**统计信息
|
||||
2. **观察一周**看封禁效果
|
||||
3. **根据需要**调整参数
|
||||
|
||||
---
|
||||
|
||||
**安装完成时间**:2025年11月5日 14:20
|
||||
**状态**:✅ 运行正常
|
||||
**防护等级**:⭐⭐⭐⭐⭐ 优秀
|
||||
|
||||
**您的服务器现在拥有企业级的安全防护!** 🎉
|
||||
|
||||
@ -1,445 +0,0 @@
|
||||
# 🎉 Fail2ban 安装完成报告
|
||||
|
||||
**完成时间**:2025年11月5日 14:22
|
||||
**服务器**:180.97.221.140
|
||||
**状态**:✅ 安装成功并正常运行
|
||||
|
||||
---
|
||||
|
||||
## ✅ 安装摘要
|
||||
|
||||
### 已完成的工作
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|-----|------|------|
|
||||
| ✅ 安装 Fail2ban | 完成 | v0.11.2 |
|
||||
| ✅ 配置 SSH 保护 | 完成 | 已启用 |
|
||||
| ✅ 启动服务 | 完成 | 运行中 |
|
||||
| ✅ 设置开机自启 | 完成 | 已配置 |
|
||||
| ✅ 创建监控脚本 | 完成 | /root/fail2ban-monitor.sh |
|
||||
| ✅ 验证运行 | 完成 | 已封禁2个攻击者 |
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 立即生效的保护
|
||||
|
||||
### Fail2ban 刚启动就捕获了攻击者!
|
||||
|
||||
```
|
||||
安装后 1 秒钟内:
|
||||
├─ 扫描了现有的 SSH 日志
|
||||
├─ 发现了 26 次失败登录
|
||||
├─ 识别出 2 个攻击者
|
||||
└─ 立即封禁:
|
||||
├─ 45.140.17.124 ✅
|
||||
└─ 196.251.69.73 ✅
|
||||
```
|
||||
|
||||
**这证明 Fail2ban 已经在工作了!** 🎯
|
||||
|
||||
---
|
||||
|
||||
## 📊 当前运行状态
|
||||
|
||||
### 实时数据
|
||||
|
||||
```
|
||||
服务状态: ✅ active (running)
|
||||
SSH 保护: ✅ 已启用
|
||||
检测到失败: 26 次
|
||||
当前封禁: 2 个 IP
|
||||
今天封禁总数: 2 次
|
||||
今天解封总数: 0 次(10分钟后会自动解封)
|
||||
```
|
||||
|
||||
### 被封禁的攻击者
|
||||
|
||||
| IP 地址 | 封禁时间 | 状态 | 解封时间 |
|
||||
|---------|---------|------|---------|
|
||||
| 45.140.17.124 | 14:20:50 | 🚫 已封禁 | 14:30:50(自动) |
|
||||
| 196.251.69.73 | 14:20:50 | 🚫 已封禁 | 14:30:50(自动) |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 配置详情
|
||||
|
||||
### 当前设置
|
||||
|
||||
```ini
|
||||
[DEFAULT]
|
||||
# 封禁10分钟
|
||||
bantime = 600
|
||||
|
||||
# 5分钟内计算失败次数
|
||||
findtime = 300
|
||||
|
||||
# 最多失败5次
|
||||
maxretry = 5
|
||||
|
||||
# 白名单(不会被封禁)
|
||||
ignoreip = 127.0.0.1/8 ::1 58.216.0.0/16
|
||||
|
||||
[sshd]
|
||||
# SSH 保护已启用
|
||||
enabled = true
|
||||
port = ssh
|
||||
logpath = /var/log/secure
|
||||
```
|
||||
|
||||
### 工作原理
|
||||
|
||||
```
|
||||
攻击者尝试登录:
|
||||
├─ 第1次失败 → 记录
|
||||
├─ 第2次失败 → 记录
|
||||
├─ 第3次失败 → 记录
|
||||
├─ 第4次失败 → 记录
|
||||
├─ 第5次失败 → 🚫 立即封禁!
|
||||
└─ 10分钟后 → 🔓 自动解封
|
||||
|
||||
如果再次攻击:
|
||||
└─ 再次被封禁 → 循环往复
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 管理命令速查
|
||||
|
||||
### 日常使用
|
||||
|
||||
```bash
|
||||
# 查看状态
|
||||
fail2ban-client status sshd
|
||||
|
||||
# 手动封禁
|
||||
fail2ban-client set sshd banip 1.2.3.4
|
||||
|
||||
# 手动解封
|
||||
fail2ban-client set sshd unbanip 1.2.3.4
|
||||
|
||||
# 查看日志
|
||||
tail -50 /var/log/fail2ban.log
|
||||
|
||||
# 运行监控脚本
|
||||
/root/fail2ban-monitor.sh
|
||||
|
||||
# 重启服务
|
||||
systemctl restart fail2ban
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 预期效果
|
||||
|
||||
### 安装前 vs 安装后
|
||||
|
||||
#### 攻击处理速度
|
||||
|
||||
```
|
||||
安装前:
|
||||
攻击者开始攻击 → 持续1小时 → 被我们的脚本封禁
|
||||
|
||||
安装后:
|
||||
攻击者开始攻击 → 持续1分钟 → 被Fail2ban封禁 ✅
|
||||
速度提升:60倍 🚀
|
||||
```
|
||||
|
||||
#### 服务器负载
|
||||
|
||||
```
|
||||
安装前:
|
||||
每天处理 ~22,000 次攻击请求
|
||||
日志文件增长 ~22,000 条记录
|
||||
|
||||
安装后(预计):
|
||||
每天处理 ~1,000 次攻击请求(减少95%)✅
|
||||
日志文件增长 ~1,000 条记录(减少95%)✅
|
||||
```
|
||||
|
||||
#### 资源消耗
|
||||
|
||||
| 资源 | 变化 | 说明 |
|
||||
|-----|------|------|
|
||||
| CPU | 降低 5-10% | 减少密码验证 |
|
||||
| 内存 | +10MB | Fail2ban 本身占用 |
|
||||
| 磁盘 I/O | 降低 90%+ | 日志减少 |
|
||||
| 网络 | 略微降低 | 拒绝攻击流量 |
|
||||
|
||||
**总体:性能提升,负载降低 ✅**
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 三重防护体系
|
||||
|
||||
### 您现在拥有的安全措施
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 第一层:Fail2ban │
|
||||
│ ──────────────── │
|
||||
│ 响应速度:秒级 │
|
||||
│ 封禁条件:5次失败 │
|
||||
│ 封禁时长:10分钟(自动解封) │
|
||||
│ 特点:快速响应,保护SSH │
|
||||
└─────────────────────────────────┘
|
||||
↓
|
||||
累犯/持续攻击
|
||||
↓
|
||||
┌─────────────────────────────────┐
|
||||
│ 第二层:防火墙脚本 │
|
||||
│ ──────────────── │
|
||||
│ 响应速度:每小时 │
|
||||
│ 封禁条件:100次连接 │
|
||||
│ 封禁时长:永久 │
|
||||
│ 特点:长期防护,保护所有服务 │
|
||||
└─────────────────────────────────┘
|
||||
↓
|
||||
突破两层防护?
|
||||
↓
|
||||
┌─────────────────────────────────┐
|
||||
│ 第三层:强密码 │
|
||||
│ ──────────────── │
|
||||
│ 密码:NEjoy4h8B9Fa │
|
||||
│ 强度:12位随机 │
|
||||
│ 破解时间:几百万年 │
|
||||
│ 特点:最后的堡垒 │
|
||||
└─────────────────────────────────┘
|
||||
↓
|
||||
您的服务器
|
||||
(固若金汤)🏰
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 效果验证
|
||||
|
||||
### 立即可见的效果
|
||||
|
||||
**刚安装就捕获了攻击者**:
|
||||
|
||||
| 指标 | 数据 |
|
||||
|-----|------|
|
||||
| 运行时间 | 2分钟 |
|
||||
| 检测失败登录 | 26次 |
|
||||
| 封禁攻击者 | 2个 |
|
||||
| 拦截效率 | 100% |
|
||||
|
||||
**说明**:Fail2ban 已经在保护您的服务器了!
|
||||
|
||||
### 一周后预期数据
|
||||
|
||||
```
|
||||
预计封禁:200-300 个不同的攻击IP
|
||||
预计拦截:20,000+ 次攻击请求
|
||||
服务器负载:降低 90%+
|
||||
攻击成功率:0%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 重要文件位置
|
||||
|
||||
### 配置和日志
|
||||
|
||||
| 文件 | 路径 | 说明 |
|
||||
|-----|------|------|
|
||||
| **配置文件** | /etc/fail2ban/jail.local | 您的自定义配置 |
|
||||
| **Fail2ban日志** | /var/log/fail2ban.log | 封禁/解封记录 |
|
||||
| **SSH日志** | /var/log/secure | SSH登录日志 |
|
||||
| **监控脚本** | /root/fail2ban-monitor.sh | 查看运行状态 |
|
||||
| **防火墙脚本** | /root/frp-firewall-manager.sh | 长期封禁管理 |
|
||||
|
||||
---
|
||||
|
||||
## 🎓 使用建议
|
||||
|
||||
### 第一周
|
||||
|
||||
```
|
||||
✅ 每天运行一次监控脚本
|
||||
/root/fail2ban-monitor.sh
|
||||
|
||||
✅ 观察封禁情况
|
||||
grep "Ban" /var/log/fail2ban.log | tail -20
|
||||
|
||||
✅ 确认没有误封自己的IP
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### 长期维护
|
||||
|
||||
```
|
||||
✅ 每周查看一次统计
|
||||
/root/fail2ban-monitor.sh
|
||||
|
||||
✅ 每月检查日志大小
|
||||
ls -lh /var/log/fail2ban.log
|
||||
|
||||
✅ 根据需要调整参数
|
||||
vi /etc/fail2ban/jail.local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 常见问题
|
||||
|
||||
### Q1: Fail2ban 会不会误封正常用户?
|
||||
|
||||
**A:** 不会。正常用户:
|
||||
- 不会连续5次输错密码
|
||||
- 您的管理IP已在白名单
|
||||
- 即使误封,10分钟后自动解封
|
||||
|
||||
### Q2: Fail2ban 和防火墙脚本会冲突吗?
|
||||
|
||||
**A:** 不会冲突,反而互补:
|
||||
- Fail2ban:短期封禁(10分钟)
|
||||
- 防火墙脚本:永久封禁(累犯)
|
||||
- 两者使用不同的 iptables 链
|
||||
|
||||
### Q3: 如果我的IP被封禁了怎么办?
|
||||
|
||||
**A:** 两种方法:
|
||||
```bash
|
||||
# 方法1:等10分钟自动解封
|
||||
# 方法2:从其他机器登录服务器解封
|
||||
fail2ban-client set sshd unbanip 您的IP
|
||||
```
|
||||
|
||||
### Q4: Fail2ban 占用多少资源?
|
||||
|
||||
**A:** 非常少:
|
||||
- 内存:约 10MB
|
||||
- CPU:几乎为 0(空闲时)
|
||||
- 磁盘:日志文件约几MB
|
||||
|
||||
---
|
||||
|
||||
## 🔍 验证清单
|
||||
|
||||
### 安装验证 ✅
|
||||
|
||||
- [x] Fail2ban 服务运行中
|
||||
- [x] SSH 保护已启用
|
||||
- [x] 配置文件已创建
|
||||
- [x] 监控脚本可用
|
||||
- [x] 已捕获攻击者
|
||||
- [x] iptables 规则正常
|
||||
- [x] 日志记录正常
|
||||
- [x] 自动启动已配置
|
||||
|
||||
**全部通过 ✅**
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
### 已创建的文档
|
||||
|
||||
1. **`Fail2ban使用指南.md`** ⭐⭐⭐
|
||||
- 完整的使用说明
|
||||
- 所有管理命令
|
||||
- 常见场景处理
|
||||
|
||||
2. **`Fail2ban安装完成报告.md`**(本文档)
|
||||
- 安装摘要
|
||||
- 运行状态
|
||||
|
||||
3. **`防火墙定时任务配置说明.md`**
|
||||
- 防火墙脚本说明
|
||||
|
||||
4. **`SSH安全评估报告.md`**
|
||||
- SSH安全分析
|
||||
|
||||
5. **`文件描述符消耗分析报告.md`**
|
||||
- 根本原因分析
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步行动
|
||||
|
||||
### 建议的后续步骤
|
||||
|
||||
#### 短期(本周)
|
||||
|
||||
1. ✅ 每天运行监控脚本
|
||||
2. ✅ 观察封禁效果
|
||||
3. ✅ 确认没有误封
|
||||
|
||||
#### 中期(本月)
|
||||
|
||||
1. 💡 考虑调整封禁参数(如需要)
|
||||
2. 💡 分析攻击来源统计
|
||||
3. 💡 评估是否需要更严格保护
|
||||
|
||||
#### 长期(未来)
|
||||
|
||||
1. 💡 考虑修改 SSH 端口
|
||||
2. 💡 考虑启用密钥认证
|
||||
3. 💡 定期更新系统和 Fail2ban
|
||||
|
||||
---
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
### 完成的工作
|
||||
|
||||
✅ **Fail2ban 已成功安装并运行**
|
||||
✅ **立即捕获了 2 个攻击者**
|
||||
✅ **服务器安全等级大幅提升**
|
||||
✅ **监控和管理工具已部署**
|
||||
✅ **完整的使用文档已创建**
|
||||
|
||||
### 当前防护级别
|
||||
|
||||
```
|
||||
防护等级:⭐⭐⭐⭐⭐ (5/5)
|
||||
|
||||
安全措施:
|
||||
├─ Fail2ban 实时保护 ✅
|
||||
├─ 防火墙脚本长期防护 ✅
|
||||
├─ 强密码 ✅
|
||||
├─ IP黑名单 ✅
|
||||
├─ 速率限制 ✅
|
||||
└─ 自动监控 ✅
|
||||
|
||||
结论:企业级安全防护!
|
||||
```
|
||||
|
||||
### 效果对比
|
||||
|
||||
```
|
||||
安装前:
|
||||
- 攻击持续时间:1小时
|
||||
- 服务器负载:高
|
||||
- 成功拦截率:95%
|
||||
|
||||
安装后:
|
||||
- 攻击持续时间:1分钟 ✅
|
||||
- 服务器负载:低 ✅
|
||||
- 成功拦截率:99.9%+ ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 恭喜!
|
||||
|
||||
**您的服务器现在拥有:**
|
||||
|
||||
- 🛡️ 三重安全防护
|
||||
- ⚡ 秒级响应速度
|
||||
- 🤖 全自动化防御
|
||||
- 📊 完整的监控体系
|
||||
- 📚 详细的使用文档
|
||||
|
||||
**您的 3D 共享平台(youyidian-3d)现在非常安全!** 🏰
|
||||
|
||||
---
|
||||
|
||||
**安装完成时间**:2025年11月5日 14:22
|
||||
**操作人员**:AI 助手
|
||||
**状态**:✅ 安装成功,运行正常
|
||||
**下次检查建议**:明天同一时间
|
||||
|
||||
**感谢使用!如有任何问题,随时查看使用指南或联系支持。** 🙏
|
||||
|
||||
@ -1,172 +0,0 @@
|
||||
# Harbor 镜像环境配置说明
|
||||
|
||||
## 📋 环境区分
|
||||
|
||||
### 测试镜像标签 - 开发团队专用
|
||||
- **Harbor 项目**: `flnxi`(同一个项目)
|
||||
- **镜像标签**: `pro-test`
|
||||
- **镜像路径**: `harbor.flnxi.com/flnxi/flnxi-material:pro-test`
|
||||
- **访问控制**: 标签级别设置为私有,需要登录
|
||||
- **用途**: 内部测试、开发、验证新功能
|
||||
- **特点**:
|
||||
- 只有开发团队可以访问(通过标签权限控制)
|
||||
- 可能包含未完全稳定的功能
|
||||
- 用于内部测试和功能验证
|
||||
- **需要 Harbor 账号密码登录**
|
||||
|
||||
### 正式镜像标签 - 客户使用
|
||||
- **Harbor 项目**: `flnxi`(同一个项目)
|
||||
- **镜像标签**: `pro-latest`
|
||||
- **镜像路径**: `harbor.flnxi.com/flnxi/flnxi-material:pro-latest`
|
||||
- **访问控制**: 标签级别设置为公开,无需登录
|
||||
- **用途**: 生产环境、客户正式部署
|
||||
- **特点**:
|
||||
- 公开访问,客户可以直接拉取
|
||||
- 稳定版本,经过充分测试
|
||||
- 用于客户正式使用
|
||||
- **无需登录,直接拉取**
|
||||
|
||||
## 🚀 安装时选择环境
|
||||
|
||||
运行 `install.sh` 时,脚本会询问:
|
||||
|
||||
```
|
||||
选择部署环境:
|
||||
1) 测试环境 (test) - 使用测试镜像标签(需要登录)
|
||||
2) 正式环境 (prod) - 使用正式镜像标签(公开,无需登录)
|
||||
请选择 [1/2,默认: 2]:
|
||||
```
|
||||
|
||||
- **选择 `1`**:使用测试镜像标签 (`pro-test`)
|
||||
- 需要输入 Harbor 用户名和密码
|
||||
- 只有开发团队可以使用
|
||||
- 镜像路径:`harbor.flnxi.com/flnxi/flnxi-material:pro-test`
|
||||
- **选择 `2` 或直接回车**:使用正式镜像标签 (`pro-latest`)
|
||||
- 无需登录,直接拉取
|
||||
- 客户使用此选项
|
||||
- 镜像路径:`harbor.flnxi.com/flnxi/flnxi-material:pro-latest`
|
||||
|
||||
## 📝 配置文件
|
||||
|
||||
环境选择会保存在 `.env.production` 文件中:
|
||||
|
||||
```bash
|
||||
# 镜像环境配置
|
||||
IMAGE_ENV=test # 或 prod
|
||||
IMAGE_TAG=test # 或 latest
|
||||
DOCKER_REGISTRY=harbor.flnxi.com/flnxi
|
||||
```
|
||||
|
||||
## 🔄 切换环境
|
||||
|
||||
如果需要切换环境,可以:
|
||||
|
||||
1. **修改配置文件**:
|
||||
```bash
|
||||
# 编辑 .env.production
|
||||
IMAGE_TAG=latest # 改为 latest 使用正式环境
|
||||
# 或
|
||||
IMAGE_TAG=test # 改为 test 使用测试环境
|
||||
```
|
||||
|
||||
2. **重新拉取镜像并启动**:
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
3. **或者重新运行安装脚本**(会询问是否覆盖配置)
|
||||
|
||||
## 📦 Harbor 项目配置
|
||||
|
||||
### 项目结构(单一项目,通过标签区分)
|
||||
|
||||
**Harbor 项目**: `flnxi`(公开项目)
|
||||
|
||||
```
|
||||
harbor.flnxi.com/flnxi/
|
||||
├── runtime/ # 基础运行环境镜像(公开)
|
||||
│ ├── postgres:15-alpine
|
||||
│ ├── minio:RELEASE.2025-04-22T22-12-26Z
|
||||
│ └── minio-mc:latest
|
||||
└── flnxi-material/ # 应用镜像
|
||||
├── pro-test # 测试环境镜像(标签权限:私有)
|
||||
└── pro-latest # 正式环境镜像(标签权限:公开)
|
||||
```
|
||||
|
||||
### 访问控制说明
|
||||
|
||||
- **项目级别**: `flnxi` 项目设置为公开(Public)
|
||||
- **标签级别权限**:
|
||||
- `pro-test` 标签:设置为私有,需要登录才能拉取
|
||||
- `pro-latest` 标签:设置为公开,无需登录即可拉取
|
||||
|
||||
### 镜像推送流程
|
||||
|
||||
1. **开发阶段**:
|
||||
```bash
|
||||
# 构建测试镜像
|
||||
docker build -t harbor.flnxi.com/flnxi/flnxi-material:pro-test .
|
||||
|
||||
# 登录 Harbor(需要开发团队账号)
|
||||
docker login harbor.flnxi.com
|
||||
|
||||
# 推送到测试标签(标签权限:私有)
|
||||
docker push harbor.flnxi.com/flnxi/flnxi-material:pro-test
|
||||
```
|
||||
|
||||
2. **测试通过后**:
|
||||
```bash
|
||||
# 从测试标签拉取已验证的镜像
|
||||
docker pull harbor.flnxi.com/flnxi/flnxi-material:pro-test
|
||||
|
||||
# 重新标记为正式版本
|
||||
docker tag harbor.flnxi.com/flnxi/flnxi-material:pro-test \
|
||||
harbor.flnxi.com/flnxi/flnxi-material:pro-latest
|
||||
|
||||
# 推送到正式标签(标签权限:公开)
|
||||
docker push harbor.flnxi.com/flnxi/flnxi-material:pro-latest
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **测试镜像标签** (`pro-test`):
|
||||
- **标签权限设置为私有**:只有开发团队可以访问
|
||||
- 可能频繁更新
|
||||
- 用于验证新功能
|
||||
- **严禁客户使用**
|
||||
|
||||
2. **正式镜像标签** (`pro-latest`):
|
||||
- **标签权限设置为公开**:客户可以直接拉取
|
||||
- 稳定版本,经过充分测试
|
||||
- 适合客户部署
|
||||
- **客户安装时默认使用此标签**
|
||||
|
||||
3. **镜像推送流程**:
|
||||
- 开发 → 推送到 `pro-test` 标签(标签权限:私有)
|
||||
- 测试验证通过 → 推送到 `pro-latest` 标签(标签权限:公开)
|
||||
- 确保测试环境验证通过后,再推送到正式标签
|
||||
- 建议使用版本标签(如 `pro-v1.0.0`)而不是 `latest`
|
||||
|
||||
4. **权限管理**:
|
||||
- 在 Harbor 中设置 `pro-test` 标签的访问权限为私有
|
||||
- 在 Harbor 中设置 `pro-latest` 标签的访问权限为公开
|
||||
- 项目 `flnxi` 本身可以设置为公开,通过标签权限控制访问
|
||||
|
||||
## 🔧 手动指定镜像
|
||||
|
||||
如果需要使用特定版本的镜像,可以修改 `.env.production`:
|
||||
|
||||
```bash
|
||||
# 使用特定版本
|
||||
APP_IMAGE=harbor.flnxi.com/flnxi/flnxi-material:pro-v1.0.0
|
||||
|
||||
# 或使用测试版本
|
||||
APP_IMAGE=harbor.flnxi.com/flnxi/flnxi-material:pro-test
|
||||
```
|
||||
|
||||
然后重新启动:
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
@ -1,429 +0,0 @@
|
||||
# HTTPS 配置指南 - 3D.YYDD.AI
|
||||
|
||||
## 概述
|
||||
|
||||
为域名 `3D.YYDD.AI` 配置免费的 HTTPS 证书(Let's Encrypt)
|
||||
|
||||
**服务器信息:**
|
||||
- 公网IP:180.97.221.140
|
||||
- 域名:3D.YYDD.AI
|
||||
- 当前端口:2320 (HTTP)
|
||||
- 目标:配置 443 端口 (HTTPS)
|
||||
- 架构:云服务器 → FRP穿透 → NAS服务器
|
||||
|
||||
## 方案选择
|
||||
|
||||
### 方案一:Nginx + Certbot(推荐)
|
||||
|
||||
适用于云服务器上直接配置反向代理
|
||||
|
||||
### 方案二:Cloudflare SSL(最简单)
|
||||
|
||||
适用于使用 Cloudflare 作为 DNS 解析
|
||||
|
||||
## 方案一:Nginx + Let's Encrypt(免费证书)
|
||||
|
||||
### 1. 在云服务器上安装 Nginx 和 Certbot
|
||||
|
||||
```bash
|
||||
# SSH 登录到云服务器 180.97.221.140
|
||||
ssh root@180.97.221.140
|
||||
|
||||
# CentOS/RHEL
|
||||
sudo yum install -y nginx certbot python3-certbot-nginx
|
||||
|
||||
# Ubuntu/Debian
|
||||
sudo apt update
|
||||
sudo apt install -y nginx certbot python3-certbot-nginx
|
||||
```
|
||||
|
||||
### 2. 配置 Nginx 反向代理
|
||||
|
||||
创建配置文件:`/etc/nginx/conf.d/3d-yydd-ai.conf`
|
||||
|
||||
```nginx
|
||||
# HTTP 配置(用于 Let's Encrypt 验证和重定向)
|
||||
server {
|
||||
listen 80;
|
||||
server_name 3d.yydd.ai www.3d.yydd.ai;
|
||||
|
||||
# Let's Encrypt 验证目录
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name 3d.yydd.ai www.3d.yydd.ai;
|
||||
|
||||
# SSL 证书路径(稍后由 certbot 自动填充)
|
||||
ssl_certificate /etc/letsencrypt/live/3d.yydd.ai/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/3d.yydd.ai/privkey.pem;
|
||||
|
||||
# SSL 优化配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头部
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
# 反向代理到 NAS 服务器(通过 FRP 穿透)
|
||||
location / {
|
||||
# 代理到 FRP 映射的本地端口
|
||||
proxy_pass http://127.0.0.1:2320;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
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_cache_bypass $http_upgrade;
|
||||
|
||||
# 大文件上传支持
|
||||
client_max_body_size 10240M;
|
||||
proxy_request_buffering off;
|
||||
|
||||
# 超时设置
|
||||
proxy_connect_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 创建证书验证目录
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/certbot
|
||||
sudo chown -R nginx:nginx /var/www/certbot
|
||||
```
|
||||
|
||||
### 4. 测试 Nginx 配置
|
||||
|
||||
```bash
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
### 5. 启动 Nginx
|
||||
|
||||
```bash
|
||||
sudo systemctl start nginx
|
||||
sudo systemctl enable nginx
|
||||
```
|
||||
|
||||
### 6. 获取 Let's Encrypt 证书
|
||||
|
||||
```bash
|
||||
# 确保域名已经解析到 180.97.221.140
|
||||
# 可以用 ping 或 nslookup 验证
|
||||
ping 3d.yydd.ai
|
||||
|
||||
# 获取证书
|
||||
sudo certbot --nginx -d 3d.yydd.ai -d www.3d.yydd.ai
|
||||
|
||||
# 按照提示操作:
|
||||
# 1. 输入邮箱(用于证书过期提醒)
|
||||
# 2. 同意服务条款
|
||||
# 3. 选择是否重定向 HTTP 到 HTTPS(选择 2 - 强制 HTTPS)
|
||||
```
|
||||
|
||||
### 7. 验证证书
|
||||
|
||||
```bash
|
||||
# 查看证书信息
|
||||
sudo certbot certificates
|
||||
|
||||
# 测试自动续期
|
||||
sudo certbot renew --dry-run
|
||||
```
|
||||
|
||||
### 8. 设置自动续期
|
||||
|
||||
Let's Encrypt 证书有效期为 90 天,需要定期续期:
|
||||
|
||||
```bash
|
||||
# Certbot 会自动创建 cron 任务
|
||||
# 查看续期任务
|
||||
sudo systemctl status certbot-renew.timer
|
||||
|
||||
# 手动续期(测试)
|
||||
sudo certbot renew
|
||||
```
|
||||
|
||||
## 方案二:Cloudflare SSL(零配置)
|
||||
|
||||
如果域名使用 Cloudflare 作为 DNS:
|
||||
|
||||
### 1. 登录 Cloudflare
|
||||
|
||||
访问:https://dash.cloudflare.com
|
||||
|
||||
### 2. 添加域名(如果还没有)
|
||||
|
||||
- 添加站点:YYDD.AI
|
||||
- 按照提示修改域名服务器
|
||||
|
||||
### 3. 配置 DNS 记录
|
||||
|
||||
```
|
||||
类型 名称 内容 代理状态
|
||||
A 3d 180.97.221.140 已代理(橙色云朵)
|
||||
```
|
||||
|
||||
### 4. 配置 SSL/TLS
|
||||
|
||||
在 Cloudflare 面板:
|
||||
|
||||
1. **SSL/TLS** → **概述**
|
||||
- 选择:**灵活(Flexible)** 或 **完全(Full)**
|
||||
- 灵活:Cloudflare到用户HTTPS,Cloudflare到服务器HTTP
|
||||
- 完全:两端都使用HTTPS(推荐,但需要服务器有证书)
|
||||
|
||||
2. **SSL/TLS** → **边缘证书**
|
||||
- 启用:始终使用 HTTPS
|
||||
- 自动 HTTPS 重写:开启
|
||||
- 最低 TLS 版本:TLS 1.2
|
||||
|
||||
### 5. 修改 Nginx 配置(使用 Cloudflare)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name 3d.yydd.ai;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:2320;
|
||||
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;
|
||||
|
||||
client_max_body_size 10240M;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
优势:
|
||||
- ✓ 零配置,Cloudflare 自动提供 SSL
|
||||
- ✓ 免费 CDN 加速
|
||||
- ✓ DDoS 防护
|
||||
- ✓ 自动续期
|
||||
|
||||
## 方案三:acme.sh(轻量级)
|
||||
|
||||
### 1. 安装 acme.sh
|
||||
|
||||
```bash
|
||||
curl https://get.acme.sh | sh
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### 2. 获取证书(DNS验证)
|
||||
|
||||
```bash
|
||||
# 使用 DNS API(推荐)
|
||||
export CF_Token="你的Cloudflare_API_Token"
|
||||
export CF_Account_ID="你的Cloudflare_Account_ID"
|
||||
|
||||
acme.sh --issue --dns dns_cf -d 3d.yydd.ai -d www.3d.yydd.ai
|
||||
```
|
||||
|
||||
### 3. 安装证书到 Nginx
|
||||
|
||||
```bash
|
||||
acme.sh --install-cert -d 3d.yydd.ai \
|
||||
--key-file /etc/nginx/ssl/3d.yydd.ai.key \
|
||||
--fullchain-file /etc/nginx/ssl/3d.yydd.ai.crt \
|
||||
--reloadcmd "systemctl reload nginx"
|
||||
```
|
||||
|
||||
## FRP 配置调整
|
||||
|
||||
如果需要在 NAS 服务器上配置 HTTPS,需要修改 FRP 配置:
|
||||
|
||||
### 在云服务器(frps.toml)
|
||||
|
||||
```toml
|
||||
bindPort = 7000
|
||||
vhostHTTPPort = 2320 # HTTP
|
||||
vhostHTTPSPort = 2443 # HTTPS(新增)
|
||||
```
|
||||
|
||||
### 在 NAS 服务器(frpc.toml)
|
||||
|
||||
```toml
|
||||
serverAddr = "180.97.221.140"
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "youyidian-3d-http"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 4320
|
||||
customDomains = ["3d.yydd.ai"]
|
||||
|
||||
[[proxies]]
|
||||
name = "youyidian-3d-https"
|
||||
type = "https"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 4320
|
||||
customDomains = ["3d.yydd.ai"]
|
||||
```
|
||||
|
||||
## 验证 HTTPS
|
||||
|
||||
### 1. 浏览器访问
|
||||
|
||||
```
|
||||
https://3d.yydd.ai
|
||||
```
|
||||
|
||||
### 2. 命令行测试
|
||||
|
||||
```bash
|
||||
# 测试 SSL 证书
|
||||
curl -I https://3d.yydd.ai
|
||||
|
||||
# 详细 SSL 信息
|
||||
openssl s_client -connect 3d.yydd.ai:443 -servername 3d.yydd.ai
|
||||
```
|
||||
|
||||
### 3. 在线工具
|
||||
|
||||
- SSL Labs测试:https://www.ssllabs.com/ssltest/
|
||||
- 输入:3d.yydd.ai
|
||||
- 目标评分:A 或 A+
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 证书获取失败
|
||||
|
||||
```bash
|
||||
# 检查域名解析
|
||||
nslookup 3d.yydd.ai
|
||||
dig 3d.yydd.ai
|
||||
|
||||
# 检查80端口是否开放
|
||||
sudo firewall-cmd --list-all
|
||||
sudo firewall-cmd --add-service=http --permanent
|
||||
sudo firewall-cmd --add-service=https --permanent
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
### 2. Nginx 启动失败
|
||||
|
||||
```bash
|
||||
# 查看错误日志
|
||||
sudo journalctl -u nginx -f
|
||||
|
||||
# 检查配置语法
|
||||
sudo nginx -t
|
||||
|
||||
# 查看端口占用
|
||||
sudo netstat -tlnp | grep :80
|
||||
sudo netstat -tlnp | grep :443
|
||||
```
|
||||
|
||||
### 3. 证书过期
|
||||
|
||||
```bash
|
||||
# 手动续期
|
||||
sudo certbot renew --force-renewal
|
||||
|
||||
# 查看证书有效期
|
||||
sudo certbot certificates
|
||||
```
|
||||
|
||||
### 4. 混合内容警告
|
||||
|
||||
如果页面有 HTTP 资源:
|
||||
|
||||
```nginx
|
||||
# 在 Nginx 配置中添加
|
||||
add_header Content-Security-Policy "upgrade-insecure-requests";
|
||||
```
|
||||
|
||||
## 推荐方案对比
|
||||
|
||||
| 方案 | 难度 | 成本 | 性能 | 维护 |
|
||||
|-----|------|------|------|------|
|
||||
| Nginx + Certbot | ⭐⭐⭐ | 免费 | 高 | 自动续期 |
|
||||
| Cloudflare | ⭐ | 免费 | 高(CDN) | 零维护 |
|
||||
| acme.sh | ⭐⭐ | 免费 | 高 | 自动续期 |
|
||||
|
||||
## 建议
|
||||
|
||||
**如果域名已在 Cloudflare:**
|
||||
→ 使用 **方案二(Cloudflare SSL)**
|
||||
- 最简单,零配置
|
||||
- 免费 CDN 加速
|
||||
- 自动 HTTPS
|
||||
|
||||
**如果需要完全控制:**
|
||||
→ 使用 **方案一(Nginx + Certbot)**
|
||||
- 完全掌控
|
||||
- 性能最佳
|
||||
- 适合企业环境
|
||||
|
||||
## 部署步骤总结
|
||||
|
||||
### 快速部署(Cloudflare)
|
||||
|
||||
```bash
|
||||
1. Cloudflare 添加 DNS 记录
|
||||
A 3d 180.97.221.140 代理(橙色)
|
||||
|
||||
2. 启用 SSL/TLS(灵活模式)
|
||||
|
||||
3. 完成!访问 https://3d.yydd.ai
|
||||
```
|
||||
|
||||
### 完整部署(Nginx + Let's Encrypt)
|
||||
|
||||
```bash
|
||||
1. 安装 Nginx 和 Certbot
|
||||
sudo yum install -y nginx certbot python3-certbot-nginx
|
||||
|
||||
2. 配置 Nginx
|
||||
sudo vi /etc/nginx/conf.d/3d-yydd-ai.conf
|
||||
|
||||
3. 启动 Nginx
|
||||
sudo systemctl start nginx
|
||||
|
||||
4. 获取证书
|
||||
sudo certbot --nginx -d 3d.yydd.ai
|
||||
|
||||
5. 完成!访问 https://3d.yydd.ai
|
||||
```
|
||||
|
||||
## 下一步
|
||||
|
||||
1. ✅ 修复 TypeScript 编译错误
|
||||
2. ✅ 重新部署到 Docker
|
||||
3. ⏳ 配置域名 DNS 解析
|
||||
4. ⏳ 选择并实施 HTTPS 方案
|
||||
5. ⏳ 测试 HTTPS 访问
|
||||
|
||||
---
|
||||
|
||||
**配置时间:** 2025-10-26
|
||||
**域名:** 3D.YYDD.AI
|
||||
**服务器:** 180.97.221.140
|
||||
**状态:** 待配置
|
||||
|
||||
@ -1,283 +0,0 @@
|
||||
# 增量部署方案
|
||||
|
||||
## 概述
|
||||
|
||||
增量部署适用于频繁的小代码变更,只上传变更的源代码文件,在服务器上重新构建,避免每次都要构建和上传完整镜像。
|
||||
|
||||
## 方案对比
|
||||
|
||||
### 当前方案(完整镜像)
|
||||
```
|
||||
本地构建 → docker save → scp 上传 (120MB) → 服务器 docker load → 部署
|
||||
耗时:5-10 分钟
|
||||
上传:120MB
|
||||
```
|
||||
|
||||
### 增量部署方案
|
||||
```
|
||||
rsync 同步源代码 → 服务器构建 → 部署
|
||||
耗时:2-4 分钟(取决于变更大小)
|
||||
上传:通常 < 10MB(只上传变更文件)
|
||||
```
|
||||
|
||||
## 适用场景
|
||||
|
||||
### ✅ 适合使用增量部署
|
||||
- 频繁的小代码变更(修复 bug、UI 调整)
|
||||
- 源代码变更,但依赖未变化
|
||||
- 服务器性能较好(有足够资源构建)
|
||||
- 需要快速迭代
|
||||
|
||||
### ❌ 不适合使用增量部署
|
||||
- 依赖变化(package.json 变更)
|
||||
- 服务器资源紧张
|
||||
- 需要完全一致的构建环境
|
||||
- 首次部署
|
||||
|
||||
## 实施方案
|
||||
|
||||
### 方案 1:rsync + 服务器构建(推荐)
|
||||
|
||||
#### 1. 创建增量部署脚本
|
||||
|
||||
创建 `scripts/deploy/incremental-deploy.ps1`:
|
||||
|
||||
```powershell
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Server,
|
||||
|
||||
[string]$Username = "root",
|
||||
[string]$Password,
|
||||
|
||||
[string]$RemotePath = "/root/deploy",
|
||||
[string]$SourcePath = ".",
|
||||
[string]$ComposeFile = "docker-compose.prod.yml"
|
||||
)
|
||||
|
||||
# 获取认证信息
|
||||
$AuthInfo = Get-AuthMethod -Server $Server -Username $Username -Password $Password
|
||||
|
||||
$projectRoot = Resolve-Path "$PSScriptRoot/../.."
|
||||
Push-Location $projectRoot
|
||||
try {
|
||||
Write-Host "===> 检查依赖是否变化" -ForegroundColor Cyan
|
||||
|
||||
# 检查 package.json 是否变化
|
||||
$packageHashFile = Join-Path $projectRoot ".last-package-hash"
|
||||
$currentPackageHash = ""
|
||||
if (Test-Path "package.json") {
|
||||
$currentPackageHash = (Get-FileHash "package.json" -Algorithm SHA256).Hash
|
||||
}
|
||||
|
||||
$remotePackageHash = ""
|
||||
$checkHashCmd = "cat ${RemotePath}/source/.last-package-hash 2>/dev/null || echo ''"
|
||||
$remoteHashResult = Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command $checkHashCmd -AuthInfo $AuthInfo
|
||||
if ($remoteHashResult -eq 0) {
|
||||
# 从输出中提取哈希值(需要解析 SSH 输出)
|
||||
# 这里简化处理,实际需要解析命令输出
|
||||
}
|
||||
|
||||
if ($currentPackageHash -ne $remotePackageHash) {
|
||||
Write-Host "⚠ 检测到依赖变化,建议使用完整部署" -ForegroundColor Yellow
|
||||
$continue = Read-Host "是否继续增量部署?(y/N)"
|
||||
if ($continue -ne "y" -and $continue -ne "Y") {
|
||||
Write-Host "已取消部署" -ForegroundColor Yellow
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "===> 同步源代码到服务器" -ForegroundColor Cyan
|
||||
|
||||
# 使用 rsync 同步(排除不需要的文件)
|
||||
$excludePatterns = @(
|
||||
"node_modules",
|
||||
".next",
|
||||
".git",
|
||||
"uploads",
|
||||
"deploy-artifacts",
|
||||
"*.log",
|
||||
".env*",
|
||||
"docs",
|
||||
"scripts"
|
||||
)
|
||||
|
||||
$excludeArgs = $excludePatterns | ForEach-Object { "--exclude=$_" }
|
||||
|
||||
# 构建 rsync 命令
|
||||
$rsyncArgs = @(
|
||||
"-avz",
|
||||
"--delete", # 删除服务器上不存在的文件
|
||||
"--progress"
|
||||
) + $excludeArgs + @(
|
||||
"${SourcePath}/",
|
||||
"${Username}@${Server}:${RemotePath}/source/"
|
||||
)
|
||||
|
||||
if (Get-Command rsync -ErrorAction SilentlyContinue) {
|
||||
Write-Host "使用 rsync 同步..." -ForegroundColor Gray
|
||||
& rsync $rsyncArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "rsync 同步失败"
|
||||
}
|
||||
} else {
|
||||
Write-Host "rsync 未安装,使用 scp 同步..." -ForegroundColor Yellow
|
||||
# 使用 scp 的替代方案(需要先打包)
|
||||
$tempArchive = "incremental-deploy-$(Get-Date -Format 'yyyyMMddHHmmss').tar.gz"
|
||||
|
||||
# 创建临时归档(排除不需要的文件)
|
||||
Write-Host "创建临时归档..." -ForegroundColor Gray
|
||||
& tar -czf $tempArchive --exclude='node_modules' --exclude='.next' --exclude='.git' --exclude='uploads' .
|
||||
|
||||
# 上传归档
|
||||
Write-Host "上传归档..." -ForegroundColor Gray
|
||||
Invoke-SCPUpload -LocalPath $tempArchive -Server $Server -Username $Username -RemotePath $RemotePath -Password $Password -AuthInfo $AuthInfo
|
||||
|
||||
# 在服务器上解压
|
||||
Write-Host "在服务器上解压..." -ForegroundColor Gray
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command "cd ${RemotePath} && mkdir -p source && tar -xzf ${tempArchive} -C source && rm ${tempArchive}" -AuthInfo $AuthInfo
|
||||
|
||||
# 清理本地归档
|
||||
Remove-Item $tempArchive -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# 保存 package.json 哈希
|
||||
if ($currentPackageHash) {
|
||||
$currentPackageHash | Out-File $packageHashFile -NoNewline
|
||||
}
|
||||
|
||||
Write-Host "===> 在服务器上构建镜像" -ForegroundColor Cyan
|
||||
|
||||
# 在服务器上构建
|
||||
$buildCmd = @"
|
||||
cd ${RemotePath}/source && \
|
||||
docker build \
|
||||
--build-arg DATABASE_URL=postgresql://placeholder:placeholder@localhost:5432/placeholder \
|
||||
--build-arg NEXTAUTH_URL=http://45.249.208.118:4320 \
|
||||
--build-arg APP_PUBLIC_URL=http://45.249.208.118:4320 \
|
||||
-t flnxi-material:incremental \
|
||||
-f Dockerfile .
|
||||
"@
|
||||
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command $buildCmd -AuthInfo $AuthInfo
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "服务器构建失败"
|
||||
}
|
||||
|
||||
Write-Host "===> 部署应用" -ForegroundColor Cyan
|
||||
|
||||
# 更新 docker-compose.yml
|
||||
$updateImageCmd = "cd ${RemotePath} && sed -i 's|image:.*flnxi-material.*|image: flnxi-material:incremental|g' docker-compose.yml"
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command $updateImageCmd -AuthInfo $AuthInfo
|
||||
|
||||
# 部署
|
||||
Invoke-SSHCommand -Server $Server -Username $Username -Password $Password -Command "cd ${RemotePath} && docker compose up -d" -AuthInfo $AuthInfo
|
||||
|
||||
Write-Host "✓ 增量部署完成" -ForegroundColor Green
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 服务器端准备
|
||||
|
||||
确保服务器上有:
|
||||
- Docker 和 Docker Compose
|
||||
- 足够的磁盘空间(用于构建)
|
||||
- 足够的 CPU 和内存(构建需要)
|
||||
|
||||
#### 3. 使用方式
|
||||
|
||||
```powershell
|
||||
.\scripts\deploy\incremental-deploy.ps1 `
|
||||
-Server 45.249.208.118 `
|
||||
-Username root `
|
||||
-Password "your-password"
|
||||
```
|
||||
|
||||
### 方案 2:Git + 服务器构建
|
||||
|
||||
如果代码在 Git 仓库中,可以在服务器上直接 pull 并构建:
|
||||
|
||||
```powershell
|
||||
# 在服务器上
|
||||
cd /root/deploy/source
|
||||
git pull origin main
|
||||
docker build -t flnxi-material:latest .
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 利用 Git 的增量更新
|
||||
- ✅ 可以回滚到任意版本
|
||||
- ✅ 不需要上传文件
|
||||
|
||||
**缺点**:
|
||||
- ❌ 需要服务器能访问 Git 仓库
|
||||
- ❌ 需要配置 Git 认证
|
||||
|
||||
## 性能对比
|
||||
|
||||
### 完整部署
|
||||
- 上传:120MB
|
||||
- 构建:本地 3-5 分钟
|
||||
- 总耗时:5-10 分钟
|
||||
|
||||
### 增量部署(小变更)
|
||||
- 上传:< 10MB(只上传变更文件)
|
||||
- 构建:服务器 2-3 分钟
|
||||
- 总耗时:2-4 分钟
|
||||
|
||||
### 增量部署(大变更)
|
||||
- 上传:20-50MB
|
||||
- 构建:服务器 3-5 分钟
|
||||
- 总耗时:4-7 分钟
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 服务器资源
|
||||
- 确保服务器有足够资源构建(建议至少 4GB RAM)
|
||||
- 构建时会占用 CPU 和内存
|
||||
|
||||
### 2. 构建环境一致性
|
||||
- 服务器上的 Docker 版本应与本地一致
|
||||
- 确保服务器能访问 npm 镜像源
|
||||
|
||||
### 3. 依赖管理
|
||||
- 如果 package.json 变化,建议使用完整部署
|
||||
- 或者先在服务器上运行 `npm install`
|
||||
|
||||
### 4. 回滚方案
|
||||
- 保留之前的镜像作为备份
|
||||
- 可以快速回滚到之前的版本
|
||||
|
||||
## 推荐使用场景
|
||||
|
||||
### 使用增量部署
|
||||
- ✅ 修复 bug(代码变更 < 10 个文件)
|
||||
- ✅ UI 调整(只修改组件文件)
|
||||
- ✅ 配置调整(只修改配置文件)
|
||||
|
||||
### 使用完整部署
|
||||
- ✅ 首次部署
|
||||
- ✅ 依赖变化(package.json 变更)
|
||||
- ✅ 重大更新(大量文件变更)
|
||||
- ✅ 需要完全一致的构建环境
|
||||
|
||||
## 混合方案
|
||||
|
||||
可以同时支持两种方式:
|
||||
|
||||
```powershell
|
||||
# 完整部署(默认)
|
||||
.\scripts\deploy\local-build-and-upload.ps1 -Server ...
|
||||
|
||||
# 增量部署(快速迭代)
|
||||
.\scripts\deploy\incremental-deploy.ps1 -Server ...
|
||||
```
|
||||
|
||||
根据实际情况选择使用哪种方式。
|
||||
|
||||
|
||||
@ -1,177 +0,0 @@
|
||||
# NAS 设备一键安装指南
|
||||
|
||||
## 📋 前置要求
|
||||
|
||||
1. **NAS 设备已安装 Docker 和 Docker Compose**
|
||||
```bash
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
2. **NAS 可以访问互联网**(用于拉取 Harbor 镜像)
|
||||
|
||||
3. **NAS 可以通过 SSH 访问**(或直接在 NAS 上操作)
|
||||
|
||||
## 🚀 安装步骤
|
||||
|
||||
### 方式一:直接下载脚本(推荐)
|
||||
|
||||
如果 NAS 可以访问互联网,可以直接下载并运行:
|
||||
|
||||
```bash
|
||||
# 1. 创建安装目录
|
||||
mkdir -p ~/flnxi-install
|
||||
cd ~/flnxi-install
|
||||
|
||||
# 2. 下载安装脚本(需要从 Git 仓库或文件服务器获取)
|
||||
# 方式 A: 如果 NAS 有 Git
|
||||
git clone <your-repo-url> .
|
||||
# 或只下载必要文件:
|
||||
wget <url-to-install.sh>
|
||||
wget <url-to-docker-compose.prod.yml>
|
||||
|
||||
# 方式 B: 从本地传输(见方式二)
|
||||
|
||||
# 3. 给脚本执行权限
|
||||
chmod +x install.sh
|
||||
|
||||
# 4. 运行安装脚本
|
||||
./install.sh
|
||||
```
|
||||
|
||||
### 方式二:从本地传输到 NAS
|
||||
|
||||
#### 步骤 1: 准备文件
|
||||
|
||||
在本地 Windows 机器上,确保以下文件已准备好:
|
||||
- `scripts/tools/install.sh`
|
||||
- `docker-compose.prod.yml`
|
||||
|
||||
#### 步骤 2: 传输文件到 NAS
|
||||
|
||||
**选项 A: 使用 SCP(如果 NAS 支持 SSH)**
|
||||
|
||||
```powershell
|
||||
# 在 PowerShell 中执行
|
||||
$NAS_IP = "192.168.x.x" # 替换为你的 NAS IP
|
||||
$NAS_USER = "admin" # 替换为你的 NAS 用户名
|
||||
$NAS_PATH = "/volume1/docker/flnxi" # 替换为你的 NAS 路径
|
||||
|
||||
# 传输文件
|
||||
scp scripts/tools/install.sh ${NAS_USER}@${NAS_IP}:${NAS_PATH}/
|
||||
scp docker-compose.prod.yml ${NAS_USER}@${NAS_IP}:${NAS_PATH}/
|
||||
```
|
||||
|
||||
**选项 B: 使用 NAS 的 Web 界面**
|
||||
|
||||
1. 通过浏览器访问 NAS 管理界面
|
||||
2. 使用文件管理器上传 `install.sh` 和 `docker-compose.prod.yml` 到指定目录
|
||||
3. 通过 SSH 登录 NAS,给脚本添加执行权限
|
||||
|
||||
#### 步骤 3: 在 NAS 上运行
|
||||
|
||||
```bash
|
||||
# SSH 登录 NAS
|
||||
ssh admin@192.168.x.x
|
||||
|
||||
# 进入安装目录
|
||||
cd /volume1/docker/flnxi # 或你上传的目录
|
||||
|
||||
# 给脚本执行权限
|
||||
chmod +x install.sh
|
||||
|
||||
# 运行安装脚本
|
||||
./install.sh
|
||||
```
|
||||
|
||||
## 📝 安装过程说明
|
||||
|
||||
安装脚本会引导你完成以下步骤:
|
||||
|
||||
1. **检查 Docker 环境** - 自动检测 Docker 和 Docker Compose
|
||||
2. **配置环境变量** - 交互式输入:
|
||||
- 服务器 IP 地址(会自动检测,可确认或修改)
|
||||
- 应用端口(默认 4320)
|
||||
- MinIO 端口(默认 9000/9001)
|
||||
- 初始管理员账号和密码
|
||||
- Harbor 登录信息(如果需要)
|
||||
3. **创建数据目录** - 自动创建必要的目录
|
||||
4. **登录 Harbor** - 如果需要访问私有镜像仓库
|
||||
5. **清理旧容器** - 清理可能存在的旧容器
|
||||
6. **拉取镜像** - 从 Harbor 拉取所有需要的镜像
|
||||
7. **启动服务** - 启动所有 Docker 容器
|
||||
8. **初始化数据库** - 自动运行数据库迁移
|
||||
|
||||
## ✅ 安装完成后
|
||||
|
||||
安装脚本会显示:
|
||||
|
||||
- **应用访问地址**: `http://<NAS_IP>:4320`
|
||||
- **MinIO 控制台**: `http://<NAS_IP>:9001`
|
||||
- **初始管理员账号**: 邮箱和密码(安装时设置)
|
||||
- **数据库信息**: 用户名和密码
|
||||
|
||||
## 🔧 常用命令
|
||||
|
||||
```bash
|
||||
# 查看服务状态
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
|
||||
# 查看日志
|
||||
docker compose -f docker-compose.prod.yml logs -f
|
||||
|
||||
# 重启服务
|
||||
docker compose -f docker-compose.prod.yml restart
|
||||
|
||||
# 停止服务
|
||||
docker compose -f docker-compose.prod.yml down
|
||||
|
||||
# 启动服务
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **端口冲突**: 确保 4320、9000、9001 端口未被占用
|
||||
2. **防火墙**: 确保 NAS 防火墙允许这些端口
|
||||
3. **存储空间**: 确保有足够的存储空间(建议至少 50GB)
|
||||
4. **Harbor 访问**: 如果 Harbor 需要登录,确保 NAS 可以访问 `harbor.flnxi.com`
|
||||
5. **配置文件**: 所有配置保存在 `.env.production` 文件中,请妥善保管
|
||||
|
||||
## 🐛 故障排查
|
||||
|
||||
### Docker 未安装
|
||||
```bash
|
||||
# 群晖 DSM
|
||||
# 通过套件中心安装 Docker
|
||||
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get update
|
||||
sudo apt-get install docker.io docker-compose
|
||||
```
|
||||
|
||||
### Harbor 连接失败
|
||||
```bash
|
||||
# 测试 Harbor 连接
|
||||
ping harbor.flnxi.com
|
||||
|
||||
# 手动登录 Harbor
|
||||
docker login harbor.flnxi.com
|
||||
```
|
||||
|
||||
### 端口被占用
|
||||
```bash
|
||||
# 查看端口占用
|
||||
netstat -tuln | grep 4320
|
||||
netstat -tuln | grep 9000
|
||||
|
||||
# 修改 docker-compose.prod.yml 中的端口配置
|
||||
```
|
||||
|
||||
## 📞 需要帮助?
|
||||
|
||||
如果遇到问题,请检查:
|
||||
1. Docker 和 Docker Compose 版本是否符合要求
|
||||
2. NAS 系统日志
|
||||
3. Docker 容器日志:`docker compose -f docker-compose.prod.yml logs`
|
||||
|
||||
@ -1,215 +0,0 @@
|
||||
# Docker 优化实施总结
|
||||
|
||||
## 已完成的优化
|
||||
|
||||
### 1. Dockerfile 多阶段构建优化 ✅
|
||||
|
||||
#### 优化点:
|
||||
- **使用 npm 缓存挂载**:加速 npm install,使用 `--mount=type=cache,target=/root/.npm`
|
||||
- **优化层顺序**:package.json 在前,源代码在后,提高缓存命中率
|
||||
- **只在构建阶段设置 npm registry**:运行镜像不保留 npm 配置
|
||||
- **清理构建依赖**:使用虚拟包 `.build-deps`,构建完成后清理
|
||||
- **处理可选依赖**:Canvas 作为可选依赖,如果未安装创建占位符,不导致构建失败
|
||||
- **最小化运行镜像**:只安装运行时必需的库
|
||||
|
||||
#### 关键改进:
|
||||
```dockerfile
|
||||
# 使用缓存挂载加速 npm install
|
||||
RUN --mount=type=cache,target=/root/.npm \
|
||||
npm ci --prefer-offline --no-audit
|
||||
|
||||
# 只在构建阶段配置 npm registry
|
||||
RUN npm config set registry https://registry.npmmirror.com/
|
||||
# ... 构建后清理
|
||||
RUN npm config delete registry
|
||||
```
|
||||
|
||||
### 2. 部署脚本优化 ✅
|
||||
|
||||
#### 优化点:
|
||||
- **使用 `docker compose up -d --build`**:让 Compose 智能判断缓存与重建
|
||||
- **避免默认使用 `--no-cache`**:只在需要时使用
|
||||
- **更好的错误处理**:检查 Docker 和 Docker Compose 是否安装
|
||||
- **优雅停止容器**:使用 `--timeout` 参数
|
||||
|
||||
#### 关键改进:
|
||||
```bash
|
||||
# 使用智能构建缓存
|
||||
docker compose up -d --build app
|
||||
|
||||
# 而不是
|
||||
docker compose build --no-cache app
|
||||
docker compose up -d app
|
||||
```
|
||||
|
||||
### 3. 环境变量优化 ✅
|
||||
|
||||
#### 优化点:
|
||||
- **生产环境变量**:`NODE_ENV=production`, `NEXT_TELEMETRY_DISABLED=1`
|
||||
- **清理 npm 配置**:运行镜像不保留构建时的 npm registry 配置
|
||||
|
||||
## 待实施的优化
|
||||
|
||||
### 1. 依赖升级 ⚠️
|
||||
|
||||
#### 安全的小版本升级:
|
||||
```json
|
||||
{
|
||||
"@aws-sdk/client-s3": "^3.928.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.928.0",
|
||||
"sharp": "^0.34.5",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"next-auth": "^4.24.13"
|
||||
}
|
||||
```
|
||||
|
||||
#### 主要版本升级(需要谨慎):
|
||||
- **Next.js**: 14.2 → 16.0(需要测试)
|
||||
- **React**: 18.3 → 19.2(需要测试)
|
||||
- **Prisma**: 5.22 → 6.19(需要迁移)
|
||||
- **ESLint**: 8.57 → 9.39(需要迁移到 flat config)
|
||||
- **Tailwind CSS**: 3.4 → 4.1(需要迁移)
|
||||
|
||||
#### 建议:
|
||||
1. **先升级小版本**:安全且不会破坏兼容性
|
||||
2. **分阶段升级主要版本**:先在开发环境测试
|
||||
3. **ESLint 升级**:如果升级到 9,需要迁移到 flat config 格式
|
||||
|
||||
### 2. SSH 密钥认证 🔐
|
||||
|
||||
#### 当前状态:
|
||||
- 使用明文密码(不安全)
|
||||
- 密码可能出现在日志中
|
||||
|
||||
#### 改进方案:
|
||||
1. **使用 SSH 密钥**:
|
||||
```powershell
|
||||
# 生成 SSH 密钥对
|
||||
ssh-keygen -t rsa -b 4096 -C "deploy@youyidian"
|
||||
|
||||
# 将公钥复制到服务器
|
||||
ssh-copy-id -i ~/.ssh/id_rsa.pub root@45.249.208.118
|
||||
```
|
||||
|
||||
2. **修改部署脚本**:
|
||||
```powershell
|
||||
# 使用密钥文件而不是密码
|
||||
$plinkPath -ssh -i "$env:USERPROFILE\.ssh\id_rsa" "${User}@${Server}" "command"
|
||||
```
|
||||
|
||||
3. **使用环境变量**:
|
||||
```powershell
|
||||
# 从环境变量读取密钥路径
|
||||
$sshKeyPath = $env:SSH_KEY_PATH
|
||||
```
|
||||
|
||||
### 3. 文件同步优化 📦
|
||||
|
||||
#### 当前状态:
|
||||
- 使用 pscp 逐个上传文件
|
||||
- 可能遗漏文件导致构建不一致
|
||||
|
||||
#### 改进方案:
|
||||
1. **使用 Git**:
|
||||
```bash
|
||||
# 在服务器上直接 git pull
|
||||
git pull origin main
|
||||
docker compose up -d --build app
|
||||
```
|
||||
|
||||
2. **使用 tar 打包**:
|
||||
```bash
|
||||
# 打包所有文件(排除 node_modules, .next 等)
|
||||
tar -czf deploy.tar.gz --exclude=node_modules --exclude=.next .
|
||||
# 上传并解压
|
||||
```
|
||||
|
||||
3. **使用 rsync**:
|
||||
```bash
|
||||
# 同步文件(只传输变化的文件)
|
||||
rsync -avz --exclude=node_modules --exclude=.next . root@server:/path
|
||||
```
|
||||
|
||||
### 4. CI/CD 集成 🚀
|
||||
|
||||
#### 建议方案:
|
||||
1. **GitHub Actions**:
|
||||
- 自动构建和测试
|
||||
- 自动部署到服务器
|
||||
- 使用 Secrets 管理敏感信息
|
||||
|
||||
2. **GitLab CI**:
|
||||
- 类似的 CI/CD 流程
|
||||
- 集成 Docker Registry
|
||||
|
||||
## 性能优化效果
|
||||
|
||||
### 构建时间:
|
||||
- **优化前**:~15-20 分钟(不使用缓存)
|
||||
- **优化后**:~3-5 分钟(使用缓存,只有依赖变化时才重新安装)
|
||||
|
||||
### 镜像大小:
|
||||
- **优化前**:~800MB+(包含所有构建工具)
|
||||
- **优化后**:~400-500MB(只包含运行时库,减少约 40%)
|
||||
|
||||
### 部署速度:
|
||||
- **优化前**:每次完全重新构建
|
||||
- **优化后**:利用缓存,只构建变化的部分
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 构建镜像:
|
||||
```bash
|
||||
# 使用缓存(推荐)
|
||||
docker build -t flnxi-material .
|
||||
|
||||
# 不使用缓存(完全重新构建)
|
||||
docker build --no-cache -t flnxi-material .
|
||||
```
|
||||
|
||||
### 部署:
|
||||
```powershell
|
||||
# 使用缓存部署(推荐)
|
||||
.\scripts\deploy\deploy-optimized.ps1
|
||||
|
||||
# 不使用缓存部署
|
||||
.\scripts\deploy\deploy-optimized.ps1 -NoCache
|
||||
```
|
||||
|
||||
### 启用 BuildKit(推荐):
|
||||
```bash
|
||||
# Linux/Mac
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
# Windows PowerShell
|
||||
$env:DOCKER_BUILDKIT=1
|
||||
|
||||
# 或者在 docker-compose.yml 中设置
|
||||
export DOCKER_BUILDKIT=1
|
||||
docker compose build
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **BuildKit 要求**:npm 缓存挂载需要 Docker BuildKit,确保启用
|
||||
2. **Canvas 可选依赖**:如果 canvas 安装失败,应用会使用 ag-psd 的 fallback 方法
|
||||
3. **npm registry**:构建阶段使用国内镜像源加速,运行镜像不保留配置
|
||||
4. **缓存失效**:如果 package.json 或 package-lock.json 变化,缓存会失效
|
||||
|
||||
## 下一步计划
|
||||
|
||||
1. ✅ 完成 Dockerfile 优化
|
||||
2. ✅ 完成部署脚本优化
|
||||
3. ⏳ 升级小版本依赖
|
||||
4. ⏳ 实施 SSH 密钥认证
|
||||
5. ⏳ 改进文件同步方式
|
||||
6. ⏳ 考虑 CI/CD 集成
|
||||
|
||||
## 参考文档
|
||||
|
||||
- [Docker 多阶段构建](https://docs.docker.com/build/building/multi-stage/)
|
||||
- [Docker BuildKit 缓存挂载](https://docs.docker.com/build/cache/mounts/)
|
||||
- [Next.js Docker 部署](https://nextjs.org/docs/deployment#docker-image)
|
||||
- [ESLint 9 迁移指南](https://eslint.org/docs/latest/use/migrate-to-9.0.0)
|
||||
|
||||
|
||||
@ -1,444 +0,0 @@
|
||||
# FLNXI素材库 - 私有镜像打包方案总结
|
||||
|
||||
## ✅ 已完成的工作
|
||||
|
||||
### 1. Docker 配置优化
|
||||
|
||||
#### `docker-compose.yml`(开发/自建)
|
||||
包含完整的服务栈:
|
||||
- ✅ PostgreSQL 数据库(带健康检查)
|
||||
- ✅ MinIO 对象存储(带健康检查)
|
||||
- ✅ MinIO 自动初始化(创建 bucket)
|
||||
- ✅ 应用服务(依赖数据库和存储)
|
||||
- ✅ 数据持久化(Docker volumes)
|
||||
- ✅ 自动化启动顺序
|
||||
- ✅ 环境变量配置
|
||||
|
||||
#### `docker-compose.prod.yml`(客户生产环境)
|
||||
- ✅ 使用私有镜像(而非构建)
|
||||
- ✅ 支持镜像仓库配置
|
||||
- ✅ 支持版本标签
|
||||
|
||||
#### `.dockerignore`
|
||||
- ✅ 优化构建上下文
|
||||
- ✅ 排除不必要文件
|
||||
- ✅ 减少镜像体积
|
||||
|
||||
### 2. 环境配置
|
||||
|
||||
#### `.env.template`
|
||||
- ✅ 环境变量模板
|
||||
- ✅ 详细的配置说明
|
||||
- ✅ 默认值设置
|
||||
- ✅ 安全提示
|
||||
|
||||
### 3. 自动化脚本
|
||||
|
||||
#### `install.sh` - 一键安装脚本
|
||||
功能:
|
||||
- ✅ 检查 Docker 环境
|
||||
- ✅ 自动检测服务器 IP
|
||||
- ✅ 生成随机安全密码
|
||||
- ✅ 创建 .env 配置
|
||||
- ✅ 构建/拉取镜像
|
||||
- ✅ 启动所有服务
|
||||
- ✅ 初始化数据库
|
||||
- ✅ 配置 MinIO
|
||||
- ✅ 显示访问信息
|
||||
- ✅ 友好的进度提示
|
||||
|
||||
#### `quick-start.sh` - 快速启动脚本
|
||||
功能:
|
||||
- ✅ 检查配置文件
|
||||
- ✅ 快速启动服务
|
||||
- ✅ 显示服务状态
|
||||
- ✅ 显示访问地址
|
||||
|
||||
#### `healthcheck.sh` - 健康检查脚本
|
||||
功能:
|
||||
- ✅ 检查所有服务状态
|
||||
- ✅ 验证数据库连接
|
||||
- ✅ 验证 MinIO 连接
|
||||
- ✅ 检查应用健康
|
||||
- ✅ 检查磁盘空间
|
||||
- ✅ 显示资源使用
|
||||
- ✅ 容器资源统计
|
||||
|
||||
#### `test-deployment.sh` - 部署测试脚本
|
||||
功能:
|
||||
- ✅ 全面的自动化测试
|
||||
- ✅ Docker 环境测试
|
||||
- ✅ 容器状态测试
|
||||
- ✅ 服务健康测试
|
||||
- ✅ 数据库连接测试
|
||||
- ✅ MinIO 功能测试
|
||||
- ✅ 网络连接测试
|
||||
- ✅ 端口开放测试
|
||||
- ✅ 文件权限测试
|
||||
- ✅ 资源使用统计
|
||||
- ✅ 测试结果报告
|
||||
|
||||
#### `uninstall.sh` - 卸载脚本
|
||||
功能:
|
||||
- ✅ 安全确认机制
|
||||
- ✅ 停止并删除容器
|
||||
- ✅ 可选删除数据卷
|
||||
- ✅ 可选删除本地文件
|
||||
- ✅ 可选删除镜像
|
||||
- ✅ 友好的提示信息
|
||||
|
||||
#### `build-and-push.sh` - 构建和发布脚本
|
||||
功能:
|
||||
- ✅ 版本号管理
|
||||
- ✅ 构建 Docker 镜像
|
||||
- ✅ 测试镜像有效性
|
||||
- ✅ 推送到私有仓库
|
||||
- ✅ 自动生成发布包
|
||||
- ✅ 创建客户文档
|
||||
- ✅ 打包发布
|
||||
|
||||
### 4. 文档体系
|
||||
|
||||
#### `DEPLOYMENT_GUIDE.md` - 开发者部署指南
|
||||
内容:
|
||||
- ✅ 架构说明
|
||||
- ✅ 构建流程
|
||||
- ✅ 私有仓库配置
|
||||
- ✅ 镜像管理
|
||||
- ✅ MinIO 配置
|
||||
- ✅ 安全措施
|
||||
- ✅ License 方案
|
||||
- ✅ 定价策略
|
||||
- ✅ 技术支持
|
||||
- ✅ 常见问题
|
||||
|
||||
#### `CUSTOMER_README.md` - 客户使用手册
|
||||
内容:
|
||||
- ✅ 系统要求
|
||||
- ✅ 快速安装步骤
|
||||
- ✅ 首次使用指南
|
||||
- ✅ 常用操作命令
|
||||
- ✅ 数据备份恢复
|
||||
- ✅ 系统更新
|
||||
- ✅ 防火墙配置
|
||||
- ✅ 故障排查
|
||||
- ✅ 性能优化
|
||||
- ✅ 安全建议
|
||||
- ✅ 常见问题 FAQ
|
||||
|
||||
#### `README_PACKAGE.md` - 打包指南
|
||||
内容:
|
||||
- ✅ 打包流程总览
|
||||
- ✅ 架构图说明
|
||||
- ✅ 准备工作
|
||||
- ✅ 打包步骤
|
||||
- ✅ 镜像访问控制
|
||||
- ✅ 交付方式
|
||||
- ✅ 版本更新流程
|
||||
- ✅ 测试验证
|
||||
- ✅ 最佳实践
|
||||
|
||||
#### `SALES_PACKAGE.md` - 销售指南
|
||||
内容:
|
||||
- ✅ 产品定位
|
||||
- ✅ 核心优势
|
||||
- ✅ 定价方案
|
||||
- ✅ 增值服务
|
||||
- ✅ 配置要求
|
||||
- ✅ 客户案例
|
||||
- ✅ 销售话术
|
||||
- ✅ 异议处理
|
||||
- ✅ 促单技巧
|
||||
- ✅ 销售工具包
|
||||
- ✅ 目标客户分析
|
||||
- ✅ 合同模板要点
|
||||
- ✅ 持续盈利策略
|
||||
|
||||
#### `VERSION.txt` - 版本信息
|
||||
内容:
|
||||
- ✅ 版本历史
|
||||
- ✅ 功能清单
|
||||
- ✅ 技术栈说明
|
||||
- ✅ 未来计划
|
||||
|
||||
#### `PACKAGE_SUMMARY.md` - 本文档
|
||||
内容:
|
||||
- ✅ 完整工作总结
|
||||
- ✅ 文件清单
|
||||
- ✅ 使用流程
|
||||
|
||||
## 📁 完整文件清单
|
||||
|
||||
### Docker 相关
|
||||
```
|
||||
docker-compose.yml # 开发环境配置(包含构建)
|
||||
docker-compose.prod.yml # 生产环境配置(使用镜像)
|
||||
Dockerfile # 应用镜像构建文件
|
||||
.dockerignore # Docker 构建排除文件
|
||||
```
|
||||
|
||||
### 配置文件
|
||||
```
|
||||
.env.template # 环境变量模板
|
||||
next.config.js # Next.js 配置
|
||||
prisma/schema.prisma # 数据库模型
|
||||
```
|
||||
|
||||
### 自动化脚本
|
||||
```
|
||||
install.sh # 一键安装脚本
|
||||
quick-start.sh # 快速启动脚本
|
||||
healthcheck.sh # 健康检查脚本
|
||||
test-deployment.sh # 部署测试脚本
|
||||
uninstall.sh # 卸载脚本
|
||||
build-and-push.sh # 构建和发布脚本
|
||||
```
|
||||
|
||||
### 文档
|
||||
```
|
||||
DEPLOYMENT_GUIDE.md # 开发者部署指南(详细)
|
||||
README_PACKAGE.md # 打包指南
|
||||
CUSTOMER_README.md # 客户使用手册
|
||||
SALES_PACKAGE.md # 销售指南
|
||||
VERSION.txt # 版本信息
|
||||
PACKAGE_SUMMARY.md # 本文档
|
||||
```
|
||||
|
||||
## 🚀 使用流程
|
||||
|
||||
### 作为开发者(首次发布)
|
||||
|
||||
```bash
|
||||
# 1. 配置私有镜像仓库
|
||||
export DOCKER_REGISTRY=your-registry.com
|
||||
|
||||
# 2. 修改构建脚本(如需要)
|
||||
vim build-and-push.sh
|
||||
|
||||
# 3. 构建并发布
|
||||
./build-and-push.sh
|
||||
# 输入版本号:1.0.0
|
||||
# 选择生成发布包:y
|
||||
|
||||
# 4. 获得发布包
|
||||
# release-v1.0.0.tar.gz
|
||||
|
||||
# 5. 测试(可选但推荐)
|
||||
tar -xzf release-v1.0.0.tar.gz
|
||||
cd release-v1.0.0
|
||||
./install.sh
|
||||
./test-deployment.sh
|
||||
```
|
||||
|
||||
### 作为销售人员
|
||||
|
||||
```bash
|
||||
# 1. 获取发布包
|
||||
release-v1.0.0.tar.gz
|
||||
|
||||
# 2. 准备销售材料
|
||||
# - SALES_PACKAGE.md(销售指南)
|
||||
# - 演示环境
|
||||
# - PPT/视频
|
||||
|
||||
# 3. 向客户交付
|
||||
# - 发布包
|
||||
# - 镜像访问凭证(如需要)
|
||||
# - License Key(如实施)
|
||||
# - CUSTOMER_README.md
|
||||
```
|
||||
|
||||
### 作为客户(安装)
|
||||
|
||||
```bash
|
||||
# 1. 解压
|
||||
tar -xzf flnxi-material-v1.0.0.tar.gz
|
||||
cd flnxi-material-v1.0.0
|
||||
|
||||
# 2. 阅读文档
|
||||
cat README.md
|
||||
|
||||
# 3. 配置
|
||||
vim .env
|
||||
# 修改 SERVER_IP
|
||||
|
||||
# 4. 安装(Linux)
|
||||
chmod +x *.sh
|
||||
./install.sh
|
||||
|
||||
# 5. 访问
|
||||
# http://YOUR_IP:4320
|
||||
```
|
||||
|
||||
### 作为客户(日常使用)
|
||||
|
||||
```bash
|
||||
# 启动
|
||||
./quick-start.sh
|
||||
|
||||
# 停止
|
||||
docker compose down
|
||||
|
||||
# 重启
|
||||
docker compose restart
|
||||
|
||||
# 健康检查
|
||||
./healthcheck.sh
|
||||
|
||||
# 查看日志
|
||||
docker compose logs -f
|
||||
|
||||
# 备份
|
||||
docker compose exec postgres pg_dump -U postgres flnxi > backup.sql
|
||||
tar -czf uploads_backup.tar.gz uploads/
|
||||
```
|
||||
|
||||
## 🎯 核心优势总结
|
||||
|
||||
### 对开发者
|
||||
1. ✅ **完整的自动化流程** - 一键构建发布
|
||||
2. ✅ **标准化交付** - 统一的发布包格式
|
||||
3. ✅ **详细文档** - 减少支持成本
|
||||
4. ✅ **灵活定价** - 多种商业模式
|
||||
|
||||
### 对销售人员
|
||||
1. ✅ **简单演示** - 10分钟完成安装
|
||||
2. ✅ **销售工具** - 话术、案例、对比
|
||||
3. ✅ **价值明确** - 清晰的ROI计算
|
||||
4. ✅ **支持完善** - 降低售后压力
|
||||
|
||||
### 对客户
|
||||
1. ✅ **安装简单** - 真正的一键安装
|
||||
2. ✅ **数据私有** - 完全掌控
|
||||
3. ✅ **零依赖** - 包含所有组件
|
||||
4. ✅ **成本可控** - 一次购买,长期使用
|
||||
|
||||
## 📊 技术特性
|
||||
|
||||
### 私有化部署优势
|
||||
- ✅ **数据安全**: 所有数据在客户服务器
|
||||
- ✅ **无需联网**: 运行不依赖外部服务
|
||||
- ✅ **性能可控**: 资源完全自主
|
||||
- ✅ **定制灵活**: 可根据需求调整
|
||||
|
||||
### MinIO 集成优势
|
||||
- ✅ **S3 兼容**: 标准的对象存储协议
|
||||
- ✅ **高性能**: 支持大文件高速传输
|
||||
- ✅ **易管理**: Web 控制台
|
||||
- ✅ **开源免费**: 无额外授权费用
|
||||
|
||||
### Docker 化优势
|
||||
- ✅ **环境一致**: 开发、测试、生产环境统一
|
||||
- ✅ **快速部署**: 分钟级完成部署
|
||||
- ✅ **易于迁移**: 随时迁移到新服务器
|
||||
- ✅ **资源隔离**: 不影响其他应用
|
||||
|
||||
## 🔧 可选增强功能
|
||||
|
||||
以下功能可根据需要添加:
|
||||
|
||||
### 1. License 系统
|
||||
```typescript
|
||||
// lib/license.ts
|
||||
// 实施授权验证
|
||||
// - MAC 地址绑定
|
||||
// - 过期时间检查
|
||||
// - 在线验证(可选)
|
||||
```
|
||||
|
||||
### 2. 使用统计
|
||||
```typescript
|
||||
// lib/analytics.ts
|
||||
// 收集匿名使用数据
|
||||
// - 活跃用户数
|
||||
// - 功能使用频率
|
||||
// - 系统性能指标
|
||||
```
|
||||
|
||||
### 3. 自动更新
|
||||
```bash
|
||||
# update.sh
|
||||
# 检查新版本
|
||||
# 自动备份
|
||||
# 更新镜像
|
||||
# 重启服务
|
||||
```
|
||||
|
||||
### 4. 监控告警
|
||||
```yaml
|
||||
# prometheus + grafana
|
||||
# 系统监控
|
||||
# 性能指标
|
||||
# 告警通知
|
||||
```
|
||||
|
||||
## 📞 后续支持
|
||||
|
||||
### 技术文档
|
||||
- ✅ 已提供完整的开发者文档
|
||||
- ✅ 已提供完整的客户文档
|
||||
- ✅ 已提供销售指南
|
||||
|
||||
### 培训材料
|
||||
- 📝 可制作视频教程
|
||||
- 📝 可制作 PPT 演示
|
||||
- 📝 可组织线上培训
|
||||
|
||||
### 持续迭代
|
||||
- 📝 收集客户反馈
|
||||
- 📝 优化功能和性能
|
||||
- 📝 发布新版本
|
||||
|
||||
## ✨ 总结
|
||||
|
||||
通过本次改造,项目已经具备:
|
||||
|
||||
1. **完整的私有化部署方案** ✅
|
||||
- 包含数据库、对象存储、应用
|
||||
- 一键安装,自动配置
|
||||
- 零外部依赖
|
||||
|
||||
2. **标准化的交付流程** ✅
|
||||
- 自动化构建
|
||||
- 统一发布包
|
||||
- 完整文档
|
||||
|
||||
3. **商业化能力** ✅
|
||||
- 多种定价方案
|
||||
- 灵活的授权方式
|
||||
- 完善的销售支持
|
||||
|
||||
4. **优秀的用户体验** ✅
|
||||
- 安装简单(10分钟)
|
||||
- 使用方便
|
||||
- 故障排查容易
|
||||
|
||||
## 🎉 下一步
|
||||
|
||||
现在您可以:
|
||||
|
||||
1. **测试完整流程**
|
||||
```bash
|
||||
./build-and-push.sh
|
||||
```
|
||||
|
||||
2. **准备销售材料**
|
||||
- 参考 SALES_PACKAGE.md
|
||||
- 制作演示视频
|
||||
- 准备案例
|
||||
|
||||
3. **开始销售**
|
||||
- 寻找目标客户
|
||||
- 演示产品
|
||||
- 签约交付
|
||||
|
||||
祝生意兴隆!🚀
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: 1.0.0
|
||||
**更新日期**: 2024-10-26
|
||||
**维护者**: 开发团队
|
||||
|
||||
|
||||
|
||||
@ -1,268 +0,0 @@
|
||||
# FLNXI素材库 - 项目说明
|
||||
|
||||
## 📋 项目概述
|
||||
|
||||
这是一个现代化的3D素材分享平台,用户可以上传和下载3D素材文件。项目严格遵循苹果官方UI设计规范,提供优雅的用户体验。
|
||||
|
||||
## 🎯 核心功能
|
||||
|
||||
### 用户功能
|
||||
- ✅ **用户认证**: 注册、登录、JWT会话管理
|
||||
- ✅ **素材上传**:
|
||||
- 上传预览图(250x175px固定尺寸显示)
|
||||
- 输入商品型号、场景名称、作者花名
|
||||
- 上传压缩包文件
|
||||
- ✅ **素材浏览**: 网格布局展示所有素材
|
||||
- ✅ **素材下载**: 一键下载压缩包
|
||||
|
||||
### 管理员功能
|
||||
- ✅ **系统设置入口**: 顶部导航栏可见
|
||||
- ✅ **素材管理**: 查看、编辑、删除素材
|
||||
- ✅ **用户管理**: 查看、删除用户
|
||||
|
||||
## 🏗️ 技术架构
|
||||
|
||||
### 前端技术栈
|
||||
- **框架**: Next.js 14 (App Router)
|
||||
- **UI库**: React 18 + TypeScript
|
||||
- **样式**: TailwindCSS (苹果设计规范)
|
||||
- **认证**: NextAuth.js
|
||||
- **日期处理**: date-fns
|
||||
|
||||
### 后端技术栈
|
||||
- **运行时**: Node.js
|
||||
- **API**: Next.js API Routes
|
||||
- **数据库**: PostgreSQL
|
||||
- **ORM**: Prisma
|
||||
- **文件上传**: Formidable
|
||||
|
||||
### 存储方案
|
||||
- **预览图**: MinIO S3 云存储
|
||||
- Endpoint: https://s3.1080p.hk
|
||||
- Bucket: jsxiangche
|
||||
- Path: 由壹点AI/
|
||||
- **压缩包**: 服务器本地存储 (./uploads目录)
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
gongxiangp2/
|
||||
├── app/ # Next.js App Router
|
||||
│ ├── api/ # API路由
|
||||
│ │ ├── auth/ # 认证API
|
||||
│ │ │ ├── [...nextauth]/route.ts # NextAuth配置
|
||||
│ │ │ └── register/route.ts # 注册API
|
||||
│ │ ├── materials/ # 素材API
|
||||
│ │ │ ├── route.ts # 获取/删除素材
|
||||
│ │ │ ├── upload/route.ts # 上传素材
|
||||
│ │ │ └── download/route.ts # 下载素材
|
||||
│ │ └── admin/ # 管理员API
|
||||
│ │ └── users/route.ts # 用户管理
|
||||
│ ├── login/ # 登录页面
|
||||
│ │ └── page.tsx
|
||||
│ ├── register/ # 注册页面
|
||||
│ │ └── page.tsx
|
||||
│ ├── admin/ # 管理员页面
|
||||
│ │ └── page.tsx
|
||||
│ ├── page.tsx # 首页
|
||||
│ ├── layout.tsx # 根布局
|
||||
│ ├── providers.tsx # Context Providers
|
||||
│ └── globals.css # 全局样式
|
||||
├── components/ # React组件
|
||||
│ ├── Header.tsx # 头部导航
|
||||
│ └── UploadModal.tsx # 上传模态框
|
||||
├── lib/ # 工具库
|
||||
│ ├── prisma.ts # Prisma客户端
|
||||
│ ├── auth.ts # NextAuth配置
|
||||
│ └── s3.ts # S3存储服务
|
||||
├── prisma/ # Prisma配置
|
||||
│ └── schema.prisma # 数据库模式
|
||||
├── types/ # TypeScript类型定义
|
||||
│ └── next-auth.d.ts # NextAuth类型扩展
|
||||
├── uploads/ # 本地上传文件
|
||||
├── .env.local # 环境变量(已配置)
|
||||
├── package.json # 项目依赖
|
||||
├── tsconfig.json # TypeScript配置
|
||||
├── tailwind.config.ts # TailwindCSS配置
|
||||
├── next.config.js # Next.js配置
|
||||
├── middleware.ts # Next.js中间件(路由保护)
|
||||
├── install.sh # 一键安装脚本
|
||||
├── start.sh # 启动脚本
|
||||
├── README.md # 项目说明
|
||||
├── SETUP.md # 部署指南
|
||||
└── PROJECT_INFO.md # 项目信息(本文件)
|
||||
```
|
||||
|
||||
## 🗄️ 数据库设计
|
||||
|
||||
### User 表(用户)
|
||||
```prisma
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
username String @unique
|
||||
password String // bcrypt加密
|
||||
role Role @default(USER) // USER | ADMIN
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
materials Material[]
|
||||
}
|
||||
```
|
||||
|
||||
### Material 表(素材)
|
||||
```prisma
|
||||
model Material {
|
||||
id String @id @default(uuid())
|
||||
previewUrl String // S3 URL
|
||||
productModel String // 商品型号
|
||||
sceneName String // 场景名称
|
||||
authorName String // 作者花名
|
||||
zipFileName String // 压缩包文件名
|
||||
zipFilePath String // 本地路径
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
userId String
|
||||
user User @relation(...)
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 UI设计规范(苹果风格)
|
||||
|
||||
### 设计原则
|
||||
- **简洁**: 清晰的布局,充足的留白
|
||||
- **一致性**: 统一的圆角、阴影、间距
|
||||
- **响应式**: 适配各种屏幕尺寸
|
||||
- **流畅动画**: 所有交互都有平滑过渡
|
||||
|
||||
### 设计元素
|
||||
- **圆角**: 12px (border-radius: 12px)
|
||||
- **阴影**:
|
||||
- 小阴影: 0 4px 12px rgba(0,0,0,0.08)
|
||||
- 大阴影: 0 8px 24px rgba(0,0,0,0.12)
|
||||
- **主色调**: 蓝色系 (#0ea5e9)
|
||||
- **字体**: Inter (Google Fonts)
|
||||
- **过渡**: 200ms ease
|
||||
|
||||
### 组件样式
|
||||
- `.btn-apple`: 苹果风格按钮基础类
|
||||
- `.btn-primary`: 主要操作按钮
|
||||
- `.btn-secondary`: 次要操作按钮
|
||||
- `.input-apple`: 苹果风格输入框
|
||||
- `.card-apple`: 苹果风格卡片
|
||||
|
||||
## 🔐 安全特性
|
||||
|
||||
1. **密码加密**: bcryptjs (10 rounds)
|
||||
2. **JWT认证**: NextAuth.js会话管理
|
||||
3. **路由保护**: Next.js middleware
|
||||
4. **CSRF保护**: NextAuth内置
|
||||
5. **SQL注入防护**: Prisma ORM参数化查询
|
||||
6. **XSS防护**: React自动转义
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 方式1: 使用安装脚本(推荐)
|
||||
```bash
|
||||
./install.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### 方式2: 手动安装
|
||||
```bash
|
||||
# 1. 安装依赖
|
||||
npm install
|
||||
|
||||
# 2. 生成Prisma Client
|
||||
npm run prisma:generate
|
||||
|
||||
# 3. 推送数据库模式
|
||||
npm run prisma:push
|
||||
|
||||
# 4. 启动开发服务器
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 创建管理员账号
|
||||
1. 访问 http://localhost:3000/register 注册账号
|
||||
2. 运行 `npm run prisma:studio`
|
||||
3. 在Prisma Studio中将用户的role改为ADMIN
|
||||
|
||||
## 📝 环境变量说明
|
||||
|
||||
| 变量名 | 说明 | 示例 |
|
||||
|--------|------|------|
|
||||
| DATABASE_URL | PostgreSQL连接字符串 | postgresql://... |
|
||||
| NEXTAUTH_SECRET | NextAuth密钥 | 随机字符串 |
|
||||
| NEXTAUTH_URL | 网站URL | http://localhost:3000 |
|
||||
| S3_ENDPOINT | S3服务端点 | https://s3.1080p.hk |
|
||||
| S3_BUCKET | S3存储桶 | jsxiangche |
|
||||
| S3_ACCESS_KEY_ID | S3访问密钥 | AEvT... |
|
||||
| S3_SECRET_ACCESS_KEY | S3密钥 | u8nz... |
|
||||
| S3_PUBLIC_DOMAIN | S3公共域名 | https://s3.1080p.hk/... |
|
||||
| S3_FILE_PATH | S3文件路径前缀 | 由壹点AI/ |
|
||||
| UPLOAD_DIR | 本地上传目录 | ./uploads |
|
||||
|
||||
## 🔧 常用命令
|
||||
|
||||
```bash
|
||||
# 开发
|
||||
npm run dev # 启动开发服务器
|
||||
npm run build # 构建生产版本
|
||||
npm start # 启动生产服务器
|
||||
|
||||
# 数据库
|
||||
npm run prisma:generate # 生成Prisma Client
|
||||
npm run prisma:push # 推送数据库模式
|
||||
npm run prisma:studio # 打开Prisma Studio
|
||||
|
||||
# 代码质量
|
||||
npm run lint # ESLint检查
|
||||
```
|
||||
|
||||
## 📊 性能优化
|
||||
|
||||
1. **图片优化**: Next.js Image组件自动优化
|
||||
2. **代码分割**: Next.js自动代码分割
|
||||
3. **缓存策略**:
|
||||
- 静态资源: 长期缓存
|
||||
- API响应: 无缓存(实时数据)
|
||||
4. **懒加载**: 组件按需加载
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### Q: 数据库连接失败
|
||||
A: 检查DATABASE_URL是否正确,确保PostgreSQL服务运行中
|
||||
|
||||
### Q: S3上传失败
|
||||
A: 检查S3配置,确保MinIO服务可访问
|
||||
|
||||
### Q: 图片显示404
|
||||
A: 检查next.config.js中的images配置,确保域名在remotePatterns中
|
||||
|
||||
### Q: 上传大文件失败
|
||||
A: 已在next.config.js中配置100MB限制,可根据需要调整
|
||||
|
||||
## 📈 未来扩展
|
||||
|
||||
可能的功能扩展:
|
||||
- [ ] 素材分类和标签
|
||||
- [ ] 搜索和筛选功能
|
||||
- [ ] 用户收藏功能
|
||||
- [ ] 评论和评分系统
|
||||
- [ ] 统计和分析面板
|
||||
- [ ] 批量上传功能
|
||||
- [ ] 在线预览3D模型
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如有技术问题,请检查:
|
||||
1. Node.js版本 >= 18
|
||||
2. 环境变量配置正确
|
||||
3. 数据库连接正常
|
||||
4. S3服务可访问
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user