docs(tabs): 更新标签页视图组件设计与交互细节

- 采用固定内边距策略,解决标签页悬停时的布局抖动问题
- 统一关闭按钮尺寸为14x14px,提升视觉一致性
- 优化活动标签和悬停状态的层级管理,确保视觉层次正确
- 增强视觉反馈,添加内嵌阴影效果改善用户体验
- 添加平滑颜色过渡动画(0.2秒),提升交互流畅度
- 支持玻璃拟态设计系统,实现现代化视觉风格
- 完善工具箱图标支持,确保标签页工具功能准确显示
This commit is contained in:
eafonyang
2026-07-15 09:58:56 +08:00
parent 003b44e2bd
commit f48f1cce70
27 changed files with 670 additions and 212 deletions
@@ -1,6 +1,6 @@
schema_version: 1
module_path: ""
title: 音频设备 CMS 管理系统(前后端全栈
title: Luxsin CMS Dashboard(前后端单体仓库
scope: []
source_files: []
depends_on: []
@@ -0,0 +1 @@
前端:Vue 3 + Vite + Element Plus + vue-router + axios;后端:Express + Sequelize(ORM) + MySQL2 + JSON Web Token + Multer(文件上传) + Axios(上游曲线服务) + Winston(日志) + ioredis + @aws-sdk/client-s3;构建产物由 Nginx 静态托管,开发期使用 nodemon 热重载。
@@ -0,0 +1,5 @@
仓库为前后端同仓部署结构:
- `backend/src` 采用经典 Express 分层:`app.js` 入口挂载 CORS、JSON/bodyLimit 中间件后按目录遍历加载 `routes/*``models/*` 通过 Sequelize 定义 ORM 实体并由 `config/database.js` 初始化连接;`services/*` 封装外部依赖(S3/OtaStorage/MeasurementStorage/CurveClient/Squiglink),`middleware/auth.js` 负责 JWT 校验,`validators/*` 使用 Zod 做入参校验。
- `frontend/src``layout/index.vue` 作为全局壳,内部用 `el-container` 组合侧栏(`sidebar-glass`)、顶栏(`lux-header`)、主内容区(`TabsView` 多标签页)和底栏;路由由 `router/index.js` 驱动,API 调用集中在 `api/*.js`,鉴权状态在 `utils/auth.js`
- 样式体系完全收敛到 `frontend/src/styles/lux-theme.css`,通过 `:root` CSS 变量统一色板、模糊半径、阴影层级,并以 `.lux-shell` 命名空间覆盖 Element Plus 组件默认样式,实现 Glassmorphism + Dimensional Layering + Dark Mode OLED 三层次视觉。
- 部署层由根级 `docker-compose.yml` 编排:后端容器暴露 8083,前端以 nginx:alpine 静态托管 `frontend/dist`,并通过 `nginx.conf` 反向代理 `/api` 到后端;`autoeq/` 下存放测量 CSV 数据供服务读取。
@@ -0,0 +1 @@
基于 Vue3 + Element Plus 与 Express/Sequelize 的耳机品牌型号 OTA 管理平台,提供玻璃拟态暗色主题的管理后台。
@@ -0,0 +1 @@
本地启动需先复制 `.env.example``.env` 并填写数据库/Redis/S3 等环境变量;运行 `docker compose up -d` 即可同时拉起后端与前端(端口 8082/8083)。前端单独开发:`cd frontend && pnpm dev`;后端单独开发:`cd backend && pnpm dev`
@@ -0,0 +1,6 @@
- CSS 主题通过 `:root` 下的 `--lux-*` 变量集中管理颜色、模糊、阴影、圆角,组件样式一律引用这些变量而非硬编码色值。
- 所有覆盖 Element Plus 的全局样式都包裹在 `.lux-shell` 命名空间下,避免污染登录页等非 shell 页面。
- 后端路由文件只导出 `{ path, handler }` 对象数组,由 `routes/index.js` 统一遍历注册,新增接口只需新建文件并在 index 中引入。
- 模型定义遵循 `src/models/<Name>.js` 单文件一个 Sequelize Model 的模式,并通过 `src/models/index.js` 统一关联与导出。
- 业务逻辑从路由中下沉到 `src/services/*`,路由仅做参数校验与响应包装,保持控制器薄而纯。
- 前端 API 模块按资源维度拆分为 `api/*.js`,每个模块暴露同名函数,组件通过 `import { listModels } from '@/api/model'` 直接调用。
@@ -0,0 +1,38 @@
---
kind: frontend_style
name: Luxsin CMS 玻璃拟态暗色主题系统
category: frontend_style
scope:
- '**'
source_files:
- frontend/src/styles/lux-theme.css
- frontend/src/main.js
- frontend/vite.config.js
- frontend/package.json
---
## 体系概述
基于 Element Plus 的 CSS Variables 覆盖 + 全局 `lux-theme.css` 自定义样式,实现「深色宇宙底 + 玻璃拟态(Glassmorphism)+ 维度分层」的统一视觉风格。整个前端无 Sass/Less/Tailwind,纯 CSS 变量驱动设计令牌。
## 核心文件与包
- `frontend/src/styles/lux-theme.css` — 全站主题定义与设计令牌(CSS 变量、组件覆盖、布局壳层)
- `frontend/src/main.js` — 引入 Element Plus dark css-vars、中文语言包及 lux-theme.css
- `frontend/vite.config.js` — 别名 `@/src`、chunk 拆分(vendor-vue / vendor-element-plus / vendor-element-icons
- `frontend/package.json` — 依赖:Vue3 + Vue Router + Element Plus + @element-plus/icons-vue + axios + vue-json-pretty
## 架构与约定
1. **设计令牌集中管理**:所有颜色、圆角、阴影、模糊级别均以 `--lux-*` 前缀的 CSS 变量声明在 `:root`,如 `--lux-cyan``--lux-glass-bg-card``--lux-blur-lg``--lux-shadow-card` 等,组件样式通过引用这些变量保持一致性。
2. **Element Plus 深度定制**:通过 `lux-shell` 作用域选择器覆盖 EP 默认样式(el-card、el-table、el-button、el-input、el-dialog、el-tabs、el-pagination 等),并启用 `element-plus/theme-chalk/dark/css-vars.css` 以利用其 CSS 变量机制。
3. **三层维度分层模型**:L1(内容卡片/表格)、L2(浮动侧栏/顶栏/底栏)、L3(对话框/下拉/消息弹窗),每层使用不同的 `backdrop-filter` 模糊等级与透明度组合,形成空间纵深感。
4. **背景氛围**`.lux-shell` 容器内放置 `.lux-orb-1/2/3` 三个大尺寸高斯模糊彩色光球,配合径向渐变页面底色,营造「深色宇宙」氛围。
5. **全局壳层模式**:应用根节点需包裹 `.lux-shell` 类,所有 EP 组件样式均在该作用域下生效,避免污染全局。
6. **响应式策略**:未采用媒体查询断点,主要依靠 Flexbox 弹性布局与 `min()` 函数控制光球尺寸;侧栏支持折叠(`.is-collapsed`)切换宽度。
7. **构建期优化**Vite 将 Element Plus 独立拆分为 chunk,减少首屏体积;忽略第三方库 `INVALID_ANNOTATION` 警告。
## 开发者规范
- 新增 UI 元素时优先复用 `--lux-*` 设计令牌,不要硬编码颜色或圆角值。
- 需要浮层效果的容器统一使用 `backdrop-filter` 配合 `rgba(30,41,59, X)` 半透明背景,保持玻璃质感一致。
- 按钮主色使用 `#38bdf8`cyan),hover 变亮至 `#22d3ee`,禁用渐变,改用纯色 + box-shadow 发光。
- 表格行 hover 使用品牌青低透明度高亮 `rgba(56,189,248,0.06)`,斑马纹使用极淡白透 `rgba(255,255,255,0.025)`
- 对话框/下拉/消息弹窗统一使用 `blur(24px) saturate(1.5)` 强模糊 + 顶部青色辉光装饰线。
- 若需扩展新组件样式,继续在 `lux-theme.css` 中按 L1/L2/L3 分区追加,遵循现有命名与注释结构。
+3 -3
View File
@@ -3,11 +3,11 @@ schema_version: 1
locale: zh-CN
branch: main
nodes_managed: true
exported_at: "2026-07-10T09:19:18Z"
exported_at: "2026-07-15T01:54:51Z"
modules:
"":
dir_name: 音频设备 CMS 管理系统(前后端全栈
title: 音频设备 CMS 管理系统(前后端全栈
dir_name: Luxsin CMS Dashboard(前后端单体仓库
title: Luxsin CMS Dashboard(前后端单体仓库
scope: []
source_files: []
children: []
@@ -0,0 +1,39 @@
---
kind: dependency_management
name: pnpm 单仓依赖管理(前后端双 package.json
category: dependency_management
scope:
- '**'
source_files:
- backend/package.json
- frontend/package.json
- backend/pnpm-lock.yaml
- frontend/pnpm-lock.yaml
- frontend/pnpm-workspace.yaml
- .pnpm-store/v11/index.db
---
## 系统概览
本仓库采用 pnpm 作为统一的包管理器,前后端各自维护独立的 `package.json`,通过根级 `.pnpm-store/v11/` 共享全局缓存,实现跨子项目的依赖去重与快速安装。未使用 pnpm workspace 的 monorepo 模式,而是以“多 package.json + 共享 store”的方式组织。
## 关键文件与位置
- 后端依赖声明:`backend/package.json`Express、Sequelize、MySQL2、JWT、Axios、Zod、AWS S3 SDK、Winston、ioredis 等)
- 前端依赖声明:`frontend/package.json`Vue3、Element Plus、Vite、Axios、vue-router 等)
- 锁文件:`backend/pnpm-lock.yaml`lockfileVersion 9.0,记录精确版本与 integrity);`frontend/pnpm-lock.yaml`(存在但未在工具输出中展开)
- 前端工作区配置:`frontend/pnpm-workspace.yaml`(仅启用 esbuild 构建)
- 全局缓存目录:`.pnpm-store/v11/`(按哈希分片存储已安装包)
- 部署文档约束:`DEPLOY.md` 明确固定 Node 镜像为 `node:22-alpine`,并强调 `packageManager` 字段锁定 pnpm 版本
## 架构与约定
- 包管理器锁定:两个 `package.json` 均声明 `packageManager: "pnpm@11.5.2"`(前端附带 sha512 校验),确保 CI/本地安装使用相同 pnpm 版本。
- 版本策略:依赖统一使用 `^` 语义化范围(如 `express ^4.21``axios ^1.7``element-plus ^2.3.14`),由 pnpm-lock.yaml 固化实际解析到的具体版本与完整性校验值。
- 依赖隔离:前后端 node_modules 独立安装,不共享同一份 `node_modules`,避免运行时冲突;但通过 `.pnpm-store` 共享磁盘缓存,减少重复下载。
- 无私有源/代理配置:未发现 `.npmrc``.pnpmrc` 或 registry 相关设置,默认使用 npm 官方源。
- 无 vendoring:未将第三方包直接提交到仓库,依赖完全通过 pnpm 从远端拉取。
## 开发者应遵循的规则
1. **新增/升级依赖**:仅在对应子项目(`backend/``frontend/`)的 `package.json` 中修改,然后运行 `pnpm install` 生成/更新该子项目的 `pnpm-lock.yaml`
2. **不要手动编辑 lock 文件**:所有版本变更通过 pnpm 命令触发,保证 integrity 与依赖树一致性。
3. **保持 packageManager 字段同步**:若升级 pnpm 主版本,需同时更新前后端 `package.json` 中的 `packageManager` 值。
4. **CI/容器环境**:基于 `node:22-alpine` 镜像,pnpm 版本由 `packageManager` 字段自动锁定,无需额外指定。
5. **不使用 workspace 聚合脚本**:当前未定义根级 scripts 来并行安装前后端,建议在各自目录内执行 `pnpm install`
@@ -0,0 +1,57 @@
---
kind: error_handling
name: 前后端错误处理约定:统一响应体 + HTTP 状态码 + 拦截器
category: error_handling
scope:
- '**'
source_files:
- backend/src/utils/response.js
- backend/src/middleware/auth.js
- backend/src/middleware/bodyLimit.js
- backend/src/app.js
- frontend/src/utils/request.js
- backend/src/routes/auth.js
- backend/src/routes/blacklist.js
- backend/src/routes/models.js
---
## 1. 系统/方法概述
- 后端采用「业务错误走 JSON 响应体、网络/鉴权错误走 HTTP 状态码」的双轨模式,没有统一的 Error 类或全局错误中间件。
- 前端通过 axios 请求拦截器集中处理 401/403 与业务 code=0 的错误提示和跳转。
## 2. 关键文件与位置
- 后端统一响应封装:`backend/src/utils/response.js`
- 认证与权限中间件(HTTP 错误):`backend/src/middleware/auth.js``backend/src/middleware/bodyLimit.js`
- 应用入口(无全局错误捕获):`backend/src/app.js`
- 前端请求与错误拦截:`frontend/src/utils/request.js`
- 典型路由示例(混用两种错误形式):`backend/src/routes/auth.js``backend/src/routes/blacklist.js``backend/src/routes/models.js`
## 3. 架构与约定
### 3.1 后端 — 两类错误出口
- 业务错误:通过 `ApiResponse.error(msg, code)` 返回 `{ code, msg, data }`,HTTP 状态码默认 200。调用方按 `code === 0` 判定失败。
- 网络/鉴权错误:直接 `res.status(4xx).json({ detail })`,如 401 未登录/凭证无效、403 无权限、413 上传过大等。
- 异常兜底:未在路由中 try/catch 的未捕获异常会由 Express 默认处理器输出堆栈;`app.js` 仅在启动阶段对数据库同步做 try/catch 并记录日志,不改变进程退出行为。
- 自定义错误类型:未发现专用 Error 子类,常见做法是 `throw new Error(...)` 或在外部服务调用失败时抛错让上层 catch。
### 3.2 前端 — 统一拦截与用户反馈
- 请求拦截:自动注入 Authorization 头,非登录接口携带 token。
- 响应拦截:
- 业务错误:当 `response.data.code === 0` 时,除非 `skipErrorToast` 标记,否则弹出 ElMessage 并 reject Promise,同时把原始响应挂载到 `err.responseData` 供上层读取。
- 401:清除本地认证信息,根据 `error.response.data.detail` 提示,若不在 `/login` 则重定向至登录页并带 redirect 参数。
- 403:提示“无权限”后 reject。
- 其他网络错误:打印 console 并 ElMessage.error 提示。
- 组件层:需要自行 `.catch` 处理业务错误分支逻辑(例如弹窗内显示具体错误详情)。
## 4. 开发者应遵循的规则
- 后端路由/服务层
- 校验失败、业务异常优先使用 `ApiResponse.error(msg, code)` 返回,保持 200 状态码,由前端统一提示。
- 仅对网络层/协议层问题使用 `res.status(4xx).json({ detail })`,如鉴权失败、资源不存在、参数超限等。
- 对外部依赖(数据库、Squiglink 等)调用需包裹 try/catch,将底层错误转换为 `ApiResponse.error` 或合适的 HTTP 错误。
- 避免在路由中直接 throw 未捕获异常;如需抛出,应在外层统一捕获并转为标准响应。
- 前端 API 调用
- 依赖 `request.js` 的响应拦截进行通用错误提示;对不需要全局 toast 的场景,设置 `config.skipErrorToast = true` 并在调用处自行提示。
- 通过 `err.responseData` 获取后端返回的完整业务错误对象,用于展示更详细的错误信息。
- 不要重复实现 401/403 的重定向与提示逻辑,统一交由拦截器处理。
- 一致性建议
- 逐步收敛 `res.json(ApiResponse.error(...))``res.status(4xx).json({ detail })` 的使用边界,减少同一接口混合两种返回形式的情况。
- 考虑引入全局错误中间件,将未捕获异常统一记录并返回友好响应,提升可观测性与健壮性。
@@ -0,0 +1,39 @@
---
kind: logging_system
name: 后端日志系统(Winston 单文件输出)
category: logging_system
scope:
- '**'
source_files:
- backend/src/config/logger.js
- backend/src/app.js
- backend/src/routes/auth.js
- backend/src/config/database.js
- backend/package.json
---
## 1. 使用的系统与框架
- 后端采用 **Winston v3** 作为统一日志框架,通过 `backend/src/config/logger.js` 集中创建并导出实例。
- 前端未引入独立日志库,调试依赖浏览器控制台,无结构化前端日志方案。
## 2. 核心文件与位置
- 日志配置与实例:`backend/src/config/logger.js`
- 应用入口挂载:`backend/src/app.js``require('./config/logger')` 后在启动/错误路径调用)
- 各路由模块按需 `require('../config/logger')` 使用(如 `routes/auth.js``routes/blacklist.js` 等)
- 数据库 SQL 日志由 Sequelize 单独控制:`backend/src/config/database.js``logging: isDevelopment ? console.log : false`
- 日志持久化目录:`backend/logs/app.log`(由 Dockerfile 中的 `/app/logs` 映射到宿主机)
## 3. 架构与约定
- **单例模式**:全局唯一 logger 实例,所有模块共享同一配置,避免重复初始化。
- **输出目标**:同时写入 Console 与本地文件 `logs/app.log`,编码 UTF-8;未启用按天/大小轮转。
- **日志级别**:默认 `info`,业务代码中使用 `logger.info / warn / error` 三种级别,未见 `debug` 使用。
- **格式规范**:时间戳 `YYYY-MM-DD HH:mm:ss` + 级别 + 消息体;额外字段以 JSON 字符串拼接在末尾(`...meta` 展开为 `JSON.stringify(meta)`),非结构化 JSON 行。
- **环境变量**:日志级别未暴露为环境变量,硬编码为 `'info'`
- **Sequelize SQL 日志**:开发环境通过 `console.log` 输出,生产关闭,不进入 Winston。
## 4. 开发者应遵循的规则
- 统一从 `../config/logger` 导入并使用,禁止直接 `console.log` 输出业务日志。
- 关键事件使用对应级别:成功/常规信息用 `info`,可恢复异常或降级用 `warn`,不可恢复错误用 `error`
- 如需附加上下文,通过第三个参数对象传入(会被序列化为 JSON 拼接到消息后),例如 `logger.info('msg', { userId })`
- 不要在日志中记录敏感信息(密码、token、完整请求体等)。
- 当前未实现日志轮转,生产部署时应配合外部 logrotate 或容器日志收集策略。
@@ -10,43 +10,35 @@ source_files:
- backend/src/config/database.js
- backend/src/config/redis.js
- backend/src/config/logger.js
- .env.example
- backend/src/app.js
- .env.example
- docker-compose.yml
---
## 系统概
项目的配置系统基于 Node.js 的 `dotenv` + 进程环境变量(`process.env`)实现,采用「根目录 `.env` 文件 + Docker compose env_file 注入」的双源模式,通过统一的入口在应用启动时完成加载。
## 系统概
仓库采用「根目录 `.env` + 启动时 `dotenv` 注入 + 各模块按需读取 `process.env`」的轻量级配置方案,未引入集中式配置中心或类型化配置库。所有运行时参数均通过环境变量提供,由 Node.js 进程在启动阶段加载。
## 核心机制
- **统一入口**`backend/src/app.js` 首行 `require('./config/loadEnv')` 触发配置加载,确保所有后续模块都能读到环境变量
- **本地开发**`loadEnv.js` 解析项目根目录 `dashboard/.env`(相对 `__dirname` 向上四层),使用 `dotenv.config({ path })` 注入到 `process.env`;若文件不存在则静默跳过
- **Docker 部署**:容器内无 `.env` 文件,由 `docker-compose.yml``env_file` 直接注入环境变量,`fs.existsSync` 判断避免覆盖已有值
- **环境判断**`config/env.js` 暴露 `APP_ENV``isDevelopment``isProduction` 三个常量,供各模块按环境切换行为(如 Sequelize SQL 日志开关)
## 关键文件与职责
- `backend/src/config/loadEnv.js`:应用入口最先执行,从仓库根目录解析并加载 `.env`(仅本地开发存在;Docker 部署时由 compose `env_file` 注入,容器内无该文件则跳过)
- `backend/src/config/env.js`:统一环境判断工具,基于 `APP_ENV` 导出 `isDevelopment` / `isProduction` 布尔值供其他模块使用
- `backend/src/app.js`Express 入口,先 `require('./config/loadEnv')` 再初始化数据库、路由、监听端口等
- `backend/src/config/database.js`Sequelize 实例,连接 MySQL,默认值 `audio/root/root123`,开发模式开启 SQL 日志。
- `backend/src/config/redis.js`ioredis 单例工厂 `getEqCacheRedis()`,按 `REDIS_EQ_DB` 选择独立 DB,带错误回调与超时重试。
- `backend/src/config/logger.js`winston 双通道(Console + File),固定输出到 `backend/logs/app.log`
- `.env.example`:完整的环境变量清单与注释,作为部署模板。
- `docker-compose.yml`:通过 `env_file: .env``environment` 覆盖 `PORT=8000``APP_ENV=${APP_ENV:-production}`,并将宿主机 `/data/projects/source` 挂载进容器。
## 配置项组织
所有可配置项集中在根级 `.env.example`,按功能域分组注释:
- 数据库:`DATABASE_HOST/PORT/NAME/USER/PASSWORD`
- 应用:`APP_NAME``APP_ENV``PORT`
- JWT 与初始管理员:`JWT_SECRET``DASHBOARD_ADMIN_USERNAME/PASSWORD`
- Meilisearch`MEILISEARCH_URL/API_KEY/INDEX`
- AWS S3`AWS_REGION/ACCESS_KEY_ID/SECRET_ACCESS_KEY/S3_OTA_BUCKET/S3_MEASUREMENT_BUCKET`
- OTA URL 与上传目录:`OTA_X8_PUBLIC_BASE``OTA_X9_URL_BASE``OTA_UPLOAD_DIR`
- Curve API`CURVE_API_BASE_URL`
- Redis EQ 缓存:`REDIS_HOST/PORT/PASSWORD/EQ_DB`
## 架构与约定
1. **加载顺序**`app.js``loadEnv`dotenv)→ 各 `config/*` 模块读 `process.env` → 业务层直接使用。
2. **环境区分**:仅依赖 `APP_ENV` 两个取值(development / production),没有多环境配置文件分支。
3. **默认值策略**:每个配置项在读取处给出合理默认值(如数据库 `localhost:3306/audio/root/root123`、Redis `127.0.0.1:6379/db=1`、端口 `8083`),保证本地开箱即用。
4. **密钥与敏感信息**JWT secret、数据库密码、AWS 凭据、Meilisearch API Key 全部走环境变量,`.env``.gitignore` 排除。
5. **外部服务解耦**MySQL、Redis、S3、Meilisearch、Curve API 等均以独立环境变量命名空间暴露,新增依赖只需在 `.env.example` 补充条目并在对应 config 中读取。
6. **前端侧**:前端为纯静态产物,不直接读取 `.env`;构建期由 Vite 注入 `import.meta.env.*`,但当前代码未见前端配置模块,前端通过相对路径请求后端 API。
## 配置消费方式
各模块直接通过 `process.env.XXX || '默认值'` 读取,形成「分散式消费」模式:
- `config/database.js` → MySQL 连接参数
- `config/redis.js` → ioredis 客户端(含密码可选、错误监听)
- `config/logger.js` → winston 输出级别与文件路径
- `services/otaStorage.js` → OTA 包上传目录与 S3 配置
- `services/measurementStorage.js` → 频响测量文件 S3 存储
- `routes/models.js` → Meilisearch 搜索索引
- `services/curveClient.js` → 曲线查询外部 API 基地址
## 设计约定与约束
1. **禁止硬编码敏感信息**:所有密钥、密码、URL 必须来自环境变量,`.env` 已在 `.gitignore` 中排除。
2. **默认值兜底**:每个 `process.env` 读取都提供合理默认值,保证本地开箱即用。
3. **按域拆分配置文件**:数据库、Redis、日志等基础设施各自独立文件,便于扩展新依赖。
4. **Docker 优先**:生产环境推荐通过 compose `env_file` 注入而非挂载 `.env` 文件。
5. **新增配置项流程**:先在 `.env.example` 添加注释说明,再在各消费处补充 `process.env.XXX || default`
## 开发者应遵循的规则
- 新增配置项必须在 `.env.example` 中声明并附带注释说明用途与默认值。
- 所有配置读取必须放在 `backend/src/config/*` 子模块中,禁止在业务路由或服务里直接散落 `process.env` 调用。
- 对数值型配置使用 `parseInt(..., 10)` 显式转换,避免字符串拼接导致类型错误。
- 生产环境一律通过 Docker Compose `env_file` 或编排平台注入环境变量,不得将真实 `.env` 提交至仓库。
- 如需新增外部依赖(新数据库、消息队列等),仿照 `database.js` / `redis.js` 的模式创建独立配置模块并导出单例。
@@ -0,0 +1,52 @@
---
kind: configuration_system
name: 后端环境变量与配置加载体系
category: configuration_system
scope:
- '**'
source_files:
- backend/src/config/loadEnv.js
- backend/src/config/env.js
- backend/src/config/database.js
- backend/src/config/redis.js
- backend/src/config/logger.js
- .env.example
- backend/src/app.js
---
## 系统概述
本项目的配置系统基于 Node.js 的 `dotenv` + 进程环境变量(`process.env`)实现,采用「根目录 `.env` 文件 + Docker compose env_file 注入」的双源模式,通过统一的入口在应用启动时完成加载。
## 核心机制
- **统一入口**`backend/src/app.js` 首行 `require('./config/loadEnv')` 触发配置加载,确保所有后续模块都能读到环境变量。
- **本地开发**`loadEnv.js` 解析项目根目录 `dashboard/.env`(相对 `__dirname` 向上四层),使用 `dotenv.config({ path })` 注入到 `process.env`;若文件不存在则静默跳过。
- **Docker 部署**:容器内无 `.env` 文件,由 `docker-compose.yml``env_file` 直接注入环境变量,`fs.existsSync` 判断避免覆盖已有值。
- **环境判断**`config/env.js` 暴露 `APP_ENV``isDevelopment``isProduction` 三个常量,供各模块按环境切换行为(如 Sequelize SQL 日志开关)。
## 配置项组织
所有可配置项集中在根级 `.env.example`,按功能域分组注释:
- 数据库:`DATABASE_HOST/PORT/NAME/USER/PASSWORD`
- 应用:`APP_NAME``APP_ENV``PORT`
- JWT 与初始管理员:`JWT_SECRET``DASHBOARD_ADMIN_USERNAME/PASSWORD`
- Meilisearch`MEILISEARCH_URL/API_KEY/INDEX`
- AWS S3`AWS_REGION/ACCESS_KEY_ID/SECRET_ACCESS_KEY/S3_OTA_BUCKET/S3_MEASUREMENT_BUCKET`
- OTA URL 与上传目录:`OTA_X8_PUBLIC_BASE``OTA_X9_URL_BASE``OTA_UPLOAD_DIR`
- Curve API`CURVE_API_BASE_URL`
- Redis EQ 缓存:`REDIS_HOST/PORT/PASSWORD/EQ_DB`
## 配置消费方式
各模块直接通过 `process.env.XXX || '默认值'` 读取,形成「分散式消费」模式:
- `config/database.js` → MySQL 连接参数
- `config/redis.js` → ioredis 客户端(含密码可选、错误监听)
- `config/logger.js` → winston 输出级别与文件路径
- `services/otaStorage.js` → OTA 包上传目录与 S3 配置
- `services/measurementStorage.js` → 频响测量文件 S3 存储
- `routes/models.js` → Meilisearch 搜索索引
- `services/curveClient.js` → 曲线查询外部 API 基地址
## 设计约定与约束
1. **禁止硬编码敏感信息**:所有密钥、密码、URL 必须来自环境变量,`.env` 已在 `.gitignore` 中排除。
2. **默认值兜底**:每个 `process.env` 读取都提供合理默认值,保证本地开箱即用。
3. **按域拆分配置文件**:数据库、Redis、日志等基础设施各自独立文件,便于扩展新依赖。
4. **Docker 优先**:生产环境推荐通过 compose `env_file` 注入而非挂载 `.env` 文件。
5. **新增配置项流程**:先在 `.env.example` 添加注释说明,再在各消费处补充 `process.env.XXX || default`
@@ -0,0 +1,53 @@
---
kind: build_system
name: 构建与部署体系(pnpm + Docker Compose + rsync
category: build_system
scope:
- '**'
source_files:
- docker-compose.yml
- backend/Dockerfile
- frontend/Dockerfile
- frontend/vite.config.js
- backend/package.json
- frontend/package.json
- scripts/upload.sh
- DEPLOY.md
- backend/start.sh
---
## 1. 使用的系统与方法
- **包管理**:前后端统一使用 pnpm(`packageManager: pnpm@11.5.2`),通过 `pnpm-lock.yaml` 锁定依赖。
- **前端构建**Vite`vite build`),产物输出到 `frontend/dist/`,并通过手动 chunk 拆分 `vue/vue-router``element-plus``@element-plus/icons-vue` 提升缓存命中率。
- **后端运行**Express + Sequelize,入口 `backend/src/app.js`,开发用 nodemon,生产直接 `node src/app.js`
- **容器化**:Docker 多阶段构建——前端 builder 基于 `node:18-alpine`,最终镜像为 `nginx:alpine`;后端基于 `node:22-alpine`,仅安装 `--prod` 依赖。
- **编排**:根目录 `docker-compose.yml` 定义 `backend`Node API,端口 8000)和 `frontend`(Nginx 静态站点,端口 80),通过自定义 bridge 网络 `audio-network` 通信。
- **发布上传**`scripts/upload.sh` 基于 rsync+ssh 将指定文件增量同步到 EC2(`ubuntu@ec2-...:/data/project/dashboard/`),支持 `frontend|backend|compose|all` 预设目标及 `-n` dry-run。
- **本地开发**:前端 Vite dev server 3000 端口,通过 proxy 将 `/api` 转发到 `http://localhost:8083`;后端默认 8083 端口,由 `.env``PORT` 控制。
## 2. 关键文件与位置
- `docker-compose.yml` — 服务编排、端口映射、日志轮转、数据卷挂载
- `backend/Dockerfile` — Node 22 Alpine 镜像,只装 prod 依赖,启动 `src/app.js`
- `frontend/Dockerfile` — 多阶段:builder 构建 distnginx:alpine 托管
- `frontend/vite.config.js` — alias `@`、dev proxy、chunk 拆分、警告过滤
- `backend/package.json` / `frontend/package.json` — 脚本与依赖声明,固定 pnpm 版本
- `scripts/upload.sh` — rsync 上传脚本,含 preset 路径集合与帮助文档
- `DEPLOY.md` — 完整部署指南(首次部署、更新、环境变量、常见问题)
- `backend/start.sh` / `restart.sh` / `stop.sh` — 裸机运行时辅助脚本
- `frontend/nginx.conf` — Nginx 反向代理配置(将 `/api` 指向 backend:8000
## 3. 架构与约定
- **环境隔离**`.env` 放在项目根目录,Compose 通过 `env_file` 注入容器;正式环境 S3 走 EC2 IAM 角色,不传 AK/SK。
- **端口约定**:容器内后端固定 8000,宿主机映射 8083;前端 Nginx 容器内 80,宿主机映射 8082。本地开发时前端 3000 代理到 8083。
- **构建产物位置**:前端 `dist/` 在服务器 `/data/project/dashboard/frontend/dist` 以只读方式挂载;后端源码 `backend/src/` 随镜像重建。
- **OTA 存储**:宿主机 `/data/projects/source` 挂载到后端容器,用于存放 X9 OTA 升级包。
- **日志策略**Compose 中每个服务启用 json-file 驱动,单文件 10MB、最多 5 个文件。
- **版本策略**:无 CI/CD 流水线,版本号停留在 `1.0.0`,发布靠人工执行 `upload.sh` + `docker compose up -d`
## 4. 开发者应遵循的规则
- **依赖变更**:修改 `package.json` 后必须重新生成 `pnpm-lock.yaml`,否则 Docker 构建会失败。
- **前端构建**:发布前先在本地执行 `cd frontend && pnpm install && pnpm build`,确保 `dist/` 存在后再上传。
- **上传范围**:优先使用 `./scripts/upload.sh <target>`,不要手动遗漏 `pnpm-lock.yaml``Dockerfile`;需要改 nginx 配置时显式传入 `./frontend/nginx.conf`
- **环境变量**`.env` 只在服务器维护,不会被 upload.sh 同步;修改后需 `docker compose up -d --force-recreate backend` 重启生效。
- **端口一致性**:不要在代码里硬编码端口,统一通过 `.env``PORT` 或 Compose `environment` 覆盖。
- **健康检查**:部署后用 `curl http://服务器IP:8083/health` 验证后端可用,再访问 `:8082` 确认前端页面。
@@ -1 +0,0 @@
前端:Vue 3.3 + Vite 6 + Element Plus 2.3(暗黑 CSS 变量 + 中文 locale+ vue-router 4 + axios;后端:Express 4 + Sequelize 6 + mysql2 + JWT + Zod 校验 + AWS S3 SDK + Winston 日志 + ioredis;构建与编排:pnpm@11、Docker Compose、Nginx Alpine。
@@ -1,5 +0,0 @@
仓库为典型的前后端分离单仓结构:
- `backend/src` 采用 Express 应用层 + Sequelize ORM 的数据访问层划分:`config/` 集中加载 `.env`、数据库、Redis、日志;`middleware/` 放置全局中间件(鉴权、bodyLimit);`models/` 通过 `index.js` 统一 `sync()` 并导出;`routes/` 按资源拆分路由文件并在 `routes/index.js` 聚合挂载;`services/` 封装外部依赖(S3、Curve、EQ 缓存、OTA 存储、用户引导);`validators/` 使用 zod 做入参校验;入口 `app.js` 在启动时执行 `sequelize.sync()` 与超级管理员引导。
- `frontend/src` 基于 Vite + Vue3 + Element Plus`main.js` 引入 Element Plus 暗黑 CSS 变量与自定义 `lux-theme.css``router/index.js` 声明式路由,`api/` 目录按资源一一对应后端 REST 接口,`views/` 按功能域组织页面组件,`components/` 存放跨页面复用组件。
- 部署由根级 `docker-compose.yml` 编排:`backend` 容器暴露 8083`frontend` 使用 nginx:alpine 静态托管 `dist` 并通过 `nginx.conf` 反向代理到后端,共享 `audio-network` 网络。
- 顶层 `autoeq/` 存放测量数据 CSV/TXT 样本,`scripts/upload.sh` 用于上传脚本,`DEPLOY.md` / `DESIGN.md` 记录部署与设计说明。
@@ -1 +0,0 @@
基于 Vue3 + Element Plus 与 Express + Sequelize 的耳机品牌/型号、OTA 升级、黑名单与用户管理后台,提供 Glassmorphism 深色主题 UI。
@@ -1 +0,0 @@
开发:`cd frontend && pnpm dev` 启动 Vite 热更新;`cd backend && pnpm start``pnpm dev`nodemon)。生产:`docker compose up -d --build`,需先复制 `.env.example``.env` 填写 DB/Redis/S3/JWT 等环境变量;前端构建产物位于 `frontend/dist`,由 nginx 容器以只读方式挂载。
@@ -1,5 +0,0 @@
- 前端样式集中在 `src/styles/lux-theme.css`,通过 `:root` CSS 变量定义色板、模糊半径、阴影层级,所有组件覆盖均以 `.lux-shell .el-*` 选择器限定作用域,不修改业务逻辑代码。
- 后端路由按资源拆分为独立文件(如 `brands.js``models.js``ota.js`),在 `routes/index.js` 中统一遍历注册,新资源只需新增路由文件并加入数组即可生效。
- 模型定义遵循 Sequelize 标准模式,并在 `models/index.js` 中集中 `sync({ force: false })`,避免在各路由中重复初始化连接。
- 请求参数校验统一放在 `validators/` 目录下对应模块,使用 zod schema 描述后在路由 handler 中调用,保持路由层仅负责编排。
- 前端 API 调用集中在 `src/api/` 下与后端资源一一对应的 JS 文件中,组件侧仅 import 函数而不直接写 axios 请求。