MilliSim/.trae/documents/about-us-module-and-reset.md

188 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 关于我们模块 + 数据库重置 + COS 清理 实施计划
## Context背景与目标
用户提出三项需求:
1. **数据库重置**:清空所有业务数据,仅保留 admin 账户,重置后保留种子数据(分类/平台/标签/示例项目/轮播图/合作伙伴由 DbSeeder 自动补回)。
2. **COS 资源全删**:清空腾讯云 COS 存储桶内所有应用上传的文件(保留 `backups/` 下的数据库备份)。
3. **关于我们模块**
- 前台 About 页去掉"联系我们"模块
- 后台新增"关于我们"管理模块:控制前台是否显示该页 + 全部内容可编辑(内联所见即所得,编辑样式与前台一致)
当前 About 页 100% 硬编码([About.vue](file:///d:/TraeProject/MilliSim4/frontend/src/views/frontend/About.vue)),无任何后端存储。本计划从零构建内容存储 + 后台内联编辑 + 前台动态渲染。
## 关键设计决策(经用户确认 + 架构验证)
1. **存储方式**:全部 About 内容作为单个 JSON 字符串存入 SystemSettings 表Key=`AboutContent`),与现有 PlatformName/ShowPartners 等配置一致。避免新建表 + 改 seeder 补丁的复杂度。
2. **编辑交互**:使用无边框 `el-input`/`el-textarea`autosize通过 hover/focus 样式提示可编辑性。不使用 `contenteditable`Vue 响应性陷阱:光标跳动)。
3. **数组排序**:上下箭头按钮,不引入拖拽库。
4. **ShowAbout 控制**:隐藏导航链接 + 路由守卫拦截直接访问(重定向到首页)。
5. **重置安全**:要求输入 "RESET" 确认 + 重置前自动创建一次数据库备份 + 操作日志记录。
6. **COS 清理范围**仅删除已知上传前缀avatars/banners/logos/projects/webgl/packages/common/icons/videos/categories/platforms/partners保留 `backups/`
7. **AboutContent 不进 PublicSettingsDto**public-settings 仅加 `showAbout`(小布尔值,供导航/路由用AboutContent 由独立端点加载,仅 About 页消费。
## 实施步骤
### 阶段 A后端基础设施
#### A1. FileStorageService 增加批量删除能力
**文件**`d:\TraeProject\MilliSim4\backend\MilliSim.Api\Services\Infrastructure\FileStorageService.cs`
- 接口 `IFileStorageService` 新增:
- `Task<List<string>> ListAllObjectsAsync()` — 分页列出桶内所有对象 key每页 1000循环至结束
- `Task<int> DeleteObjectsAsync(IEnumerable<string> keys)` — 批量删除(`DeleteMultiObjectRequest`,每批最多 1000
- 实现:复用私有 `GetStorageConfigAsync()` + `GetCosXml(config)`Local 模式则遍历 `wwwroot/uploads` 目录删除文件
- 不暴露到控制器单独使用,仅供 factory-reset 调用
#### A2. AboutContent DTO + 端点
**文件**`d:\TraeProject\MilliSim4\backend\MilliSim.Api\Common\Dtos\Dtos.cs`
新增 `AboutContentDto`
```csharp
public class AboutContentDto
{
public string HeroTag { get; set; } = "关于我们";
public string HeroTitle { get; set; } = "";
public string HeroDesc { get; set; } = "";
public string IntroTitle { get; set; } = "公司简介";
public List<string> IntroParagraphs { get; set; } = new();
public List<AboutStatDto> Stats { get; set; } = new();
public string MissionTitle { get; set; } = "使命与愿景";
public List<AboutMissionCardDto> MissionCards { get; set; } = new();
public string TimelineTitle { get; set; } = "发展历程";
public List<AboutTimelineItemDto> Timeline { get; set; } = new();
}
public class AboutStatDto { public string Number { get; set; } = ""; public string Label { get; set; } = ""; }
public class AboutMissionCardDto { public string Title { get; set; } = ""; public string Icon { get; set; } = "rocket"; public string Desc { get; set; } = ""; }
public class AboutTimelineItemDto { public string Year { get; set; } = ""; public string Title { get; set; } = ""; public string Desc { get; set; } = ""; }
public class AboutSettingsDto
{
public bool ShowAbout { get; set; } = true;
public AboutContentDto Content { get; set; } = new();
}
```
`PublicSettingsDto` 仅新增 `public bool ShowAbout { get; set; } = true;`(不加 AboutContent
#### A3. SystemService 关于我们读写
**文件**`d:\TraeProject\MilliSim4\backend\MilliSim.Api\Services\SystemService.cs`
- `GetAboutSettingsAsync()` → 读 SystemSettings 中 `ShowAbout` + `AboutContent`JSON 反序列化),返回 `AboutSettingsDto`
- `UpdateAboutSettingsAsync(AboutSettingsDto dto)` → 序列化 Content 为 JSON 存入 `AboutContent` key`ShowAbout` 存 bool 字符串
- `GetPublicSettingsAsync()` 中加入 `ShowAbout` 读取
#### A4. SystemController 端点
**文件**`d:\TraeProject\MilliSim4\backend\MilliSim.Api\Controllers\SystemController.cs`
- `GET /api/system/about``[AllowAnonymous]` 返回 `AboutSettingsDto`(前台 About 页消费)
- `PUT /api/system/about``[Authorize(Policy="Admin")]` 保存
#### A5. 工厂重置端点
**文件**`d:\TraeProject\MilliSim4\backend\MilliSim.Api\Services\SystemService.cs` + `SystemController.cs`
- `POST /api/system/factory-reset``[Authorize(Policy="Admin")]`body 含 `ConfirmText`(必须等于 "RESET"
- 逻辑:
1. 校验 `ConfirmText == "RESET"`,否则返回 400
2. 先调 `CreateBackupAsync()` 创建备份(保留安全网)
3. `SET FOREIGN_KEY_CHECKS=0`
4. 按依赖顺序 DELETE FROM 所有 21 张表Users 仅删 `Username != 'admin'`,并清空 admin 的 Avatar/UpdatedAt 字段)
5. `SET FOREIGN_KEY_CHECKS=1`
6.`DbSeeder.SeedAsync(db)` 重新补种子admin 已存在跳过;分类/平台/标签/项目/轮播图/合作伙伴/设置按"为空才补"逻辑重新填入AboutContent/ShowAbout 默认值也会被补入)
7.`_fileStorage.ListAllObjectsAsync()` 列出所有对象,过滤掉 `backups/` 前缀,调 `DeleteObjectsAsync()` 批量删除
8. 记操作日志
9. 返回 `{ TablesCleared, ObjectsDeleted, BackupId }`
#### A6. DbSeeder 补种子
**文件**`d:\TraeProject\MilliSim4\backend\MilliSim.Api\Data\DbSeeder.cs`
-`SeedAsync` 的 SystemSettings 补种子段加入:
- `ShowAbout` = "true"
- `AboutContent` = 默认 JSON当前 About.vue 的硬编码内容序列化)
- 仅在 key 不存在时插入(与现有逻辑一致)
### 阶段 B前端 — 关于我们动态化 + 去掉联系模块
#### B1. 类型 + API + Store
**文件**
- `d:\TraeProject\MilliSim4\frontend\src\types\index.ts`:加 `AboutContent` / `AboutStat` / `AboutMissionCard` / `AboutTimelineItem` 接口;`PublicSettings``showAbout: boolean`
- `d:\TraeProject\MilliSim4\frontend\src\api\index.ts``systemApi.getAbout()` GET `/system/about``systemApi.updateAbout(payload)` PUT`systemApi.factoryReset(confirmText)` POST
- `d:\TraeProject\MilliSim4\frontend\src\stores\system.ts``settings``showAbout`(默认 true`aboutContent` ref + `loadAbout()` 方法About 页 onMounted 调用)
#### B2. About.vue 动态化
**文件**`d:\TraeProject\MilliSim4\frontend\src\views\frontend\About.vue`
- 去掉"联系我们"section原 L101-130+ 相关 CTA
- script`onMounted``systemStore.loadAbout()` 获取内容;用 `computed` 映射 store 中的 aboutContent
- template所有硬编码文本替换为 `{{ about.heroTag }}` 等绑定timeline 用 `v-for="item in about.timeline"`
- 无数据时显示骨架屏/loading
#### B3. 路由 + 导航守卫
**文件**
- `d:\TraeProject\MilliSim4\frontend\src\router\index.ts``/about` 路由 `meta.requiresAbout: true``beforeEach` 守卫中 `if (to.meta.requiresAbout && !systemStore.settings.showAbout) return '/'`,并先 `await systemStore.ensureLoaded()`
- `d:\TraeProject\MilliSim4\frontend\src\layouts\FrontendLayout.vue`nav 菜单"关于我们"项加 `v-if="systemStore.settings.showAbout"`footer 链接同理
### 阶段 C后台 — 关于我们内联编辑页
#### C1. 新建 AboutSettings.vue
**文件**`d:\TraeProject\MilliSim4\frontend\src\views\admin\AboutSettings.vue`(新建)
布局完全复刻前台 About.vue 结构,但所有文字改为可编辑:
- 顶部工具栏:`ShowAbout` el-switch + "保存" el-buttonloading 状态)
- Hero 区3 个无边框 el-inputtag/title/descinherit 前台字体样式
- 公司简介:`el-textarea :autosize` × N 段可增删4 个统计卡片number + label 各一输入),每卡右上角 hover 显示删除按钮 + 上下移按钮
- 使命与愿景3 张卡片title + icon 选择 + desc textarea增删/排序
- 发展历程时间线项year + title + desc增删/排序
- 编辑态样式:`.editable:hover { outline: 1px dashed var(--color-primary); }` `.editable:focus { outline: 2px solid var(--color-primary); background: var(--color-primary-light-9); }`
- 数组操作按钮hover 时浮现"上移/下移/删除""添加"按钮在列表末尾
- `onMounted``systemApi.getAbout()` 加载;保存调 `systemApi.updateAbout()`;保存后 `systemStore.load()` 刷新前台状态
- 图标选择用 el-select 下拉(预设 SvgIcon 列表rocket/globe/heart/target/users/star 等)
#### C2. 路由 + 菜单
**文件**
- `d:\TraeProject\MilliSim4\frontend\src\router\index.ts`:加 `/admin/about-settings` 路由admin auth
- `d:\TraeProject\MilliSim4\frontend\src\layouts\AdminLayout.vue`:侧栏菜单加入"关于我们"项icon: `info``building`
### 阶段 D后台 — 工厂重置入口
#### D1. Settings.vue 危险操作卡片
**文件**`d:\TraeProject\MilliSim4\frontend\src\views\admin\Settings.vue`
- 在页面末尾加"危险操作"卡片:
- 标题 + 警告说明("将清空所有业务数据并删除 COS 资源,仅保留 admin 账户,操作前会自动创建备份"
- el-input 要求输入 "RESET" 才能激活按钮
- el-button danger "恢复出厂设置"disabled 直到输入正确)
- 二次确认弹窗 `showActionConfirm`
-`systemApi.factoryReset(confirmText)`;成功后 `ElMessage.success` + 提示重新登录admin 密码不变)
### 阶段 E编译验证 + 重启
1. 杀掉后端进程PID 13788/22680`dotnet build` → 重启后端
2. 前端 Vite HMR 自动加载,确认无编译错误
3. 浏览器测试场景
## 验证步骤
### 后端验证
1. `dotnet build` 0 错误 0 警告
2. 启动后端,确认 `PendingUploads` + 新增设置 keyAboutContent/ShowAbout建表/补种子成功(查日志)
3. Swagger 确认新端点存在GET/PUT `/api/system/about`、POST `/api/system/factory-reset`
### 前端验证
1. Vite HMR 无编译错误
2. 访问 `/about` 确认内容从后端加载(非硬编码)
3. 关闭 ShowAbout 后导航不显示"关于我们",直接访问 `/about` 重定向到首页
4. 后台 `/admin/about-settings` 编辑任意文字 → 保存 → 刷新前台确认生效
5. 数组增删/排序正常
6. 后台"恢复出厂设置"输入 RESET → 执行 → 确认数据库清空 + COS 清空 + admin 可登录 + 种子数据恢复
## 假设与决策
1. **不删 admin 账户**DbSeeder 对 admin 用户按 username 幂等,重置后保留原密码 `admin123`
2. **保留 backups/ 前缀**:工厂重置不删 `backups/` 下的数据库备份文件,作为最后安全网。
3. **重置前自动备份**:调用现有 `CreateBackupAsync`,即使重置失败也有回滚依据。
4. **AboutContent 默认值**:取当前 About.vue 硬编码内容作为默认 JSON保证未配置时前台显示不变。
5. **不引入拖拽库**:数组排序用上下箭头按钮,与现有 SortOrder 字段约定一致。
6. **PublicSettingsDto 不含 AboutContent**:避免每次应用启动加载大 JSONAboutContent 由 About 页独立加载。
7. **factory-reset 同步删 COS**DB 清空后立即删 COS保证一致性COS 删除失败不回滚 DBDB 已是干净状态COS 残留可手动清理,记录日志)。