fix: restore post-v1.1.14 features while preserving asset updates
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
Undo the full-tree rollback in 6ea1b3d and restore the pre-rollback v1.2.0 code. Preserve the new asset flow rules, abnormal inventory separation, range drilldowns and date picker; adapt the regression test to the restored routes layout.
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,165 @@
|
||||
# 架构与分层约定
|
||||
|
||||
本文是这个仓库的结构契约。**6 条硬规则由 `src/architecture.test.ts` 断言**,破坏即测试失败。
|
||||
|
||||
## 1. 分层与依赖方向
|
||||
|
||||
```
|
||||
app/ 应用外壳(路由、导航注册)
|
||||
↓
|
||||
modules/ 前端业务域 —— 只依赖 shared/
|
||||
↓
|
||||
shared/ 叶子层:角色常量、跨端 DTO、纯工具(不得依赖 modules/ 或 server/)
|
||||
|
||||
server/ 后端 —— 只依赖 shared/,不得依赖 modules/
|
||||
```
|
||||
|
||||
**依赖只能向下**。前端通过 HTTP 调用后端,不通过 import。
|
||||
|
||||
### 6 条硬规则
|
||||
|
||||
| # | 规则 | 违反后果 |
|
||||
| --- | --- | --- |
|
||||
| 1 | `server/**` 不得 import `modules/**` | 服务端被前端类型/组件绑架,无法独立部署与测试 |
|
||||
| 2 | `modules/**`、`components/**`、`auth/**`、`app/**` 不得 import `server/**` | 打包会拖入服务端代码与密钥 |
|
||||
| 3 | `shared/**` 不得 import `modules/**` / `server/**` / `components/**` | 叶子层反向依赖会让依赖图成环 |
|
||||
| 4 | 不得出现 `vendor/` 引用 | 生产代码必须位于业务域目录,不能藏在"第三方/参考"目录里 |
|
||||
| 5 | `model.ts` / `model.tsx` 不得 import `react` | 纯模型必须能在 Node 里直接单测,不依赖 DOM |
|
||||
| 6 | `@ts-nocheck` 只允许出现在已登记的原型快照文件 | 类型门禁对最大文件失效 |
|
||||
|
||||
### 关于第 6 条的豁免清单
|
||||
|
||||
这三个文件是**逐字节保留**的 8113 验收原型(UI / CSS 冻结,改动需重新做桌面与移动端截图验收):
|
||||
|
||||
```
|
||||
modules/energy/hydrogen/board/EnergyBiBoardApp.tsx
|
||||
modules/energy/hydrogen/station-daily/StationDailyApp.tsx
|
||||
modules/energy/hydrogen/station-daily/StationDailyDetailView.tsx
|
||||
```
|
||||
|
||||
豁免清单是**只减不增**的:新增 `@ts-nocheck` 会被测试拦下,请改为修正类型。
|
||||
|
||||
## 2. 前端业务域的形状
|
||||
|
||||
每个域尽量保持同一形状,便于"进一个目录就知道从哪读起":
|
||||
|
||||
```
|
||||
modules/<domain>/
|
||||
index.tsx 域入口组件(导出给 app/modules.ts 注册)
|
||||
api.ts 只做 HTTP,不做业务判断
|
||||
types.ts DTO
|
||||
model.ts 纯函数:筛选 / 排序 / 聚合 / 口径(可被 node:test 直接测)
|
||||
components/ 无状态展示组件
|
||||
*.css 域内样式
|
||||
```
|
||||
|
||||
复杂域可以再分子域,例如氢能:
|
||||
|
||||
```
|
||||
modules/energy/hydrogen/
|
||||
index.tsx 导航入口
|
||||
api.ts types.ts 数据访问与 DTO
|
||||
model/ 纯逻辑(格式化、承担方标签、按日汇总格式)
|
||||
board/ 经营看板 UI
|
||||
station-daily/ 单站日报 UI
|
||||
drill/ 下钻弹层 UI
|
||||
common/ 域内共享工具与组件
|
||||
fonts/ 看板专用字体
|
||||
```
|
||||
|
||||
判定标准很简单:**能在 Node 里直接测的放 `model/`;需要浏览器/DOM 的放 UI 子目录。**
|
||||
|
||||
## 3. 后端结构
|
||||
|
||||
```
|
||||
server/
|
||||
config.ts 环境变量集中读取 + assertRuntimeConfig() 启动期校验
|
||||
app.ts createApp():装配中间件、挂载路由、静态托管(无副作用,可测试)
|
||||
index.ts 进程启动:config 校验 → app → bootstrap → listen
|
||||
local.ts 只读预览启动(不建表、不定时任务)
|
||||
bootstrap.ts 启动副作用总入口
|
||||
db/ mysql / hydrogen / heatmap 连接
|
||||
db/schema/ 运行时建表/改表的唯一位置(只读模式下整体跳过)
|
||||
middleware/ auth(JWT → 注入 user)、read-only
|
||||
auth/ 登录换票、固定密码、权限过滤与脱敏
|
||||
routes/<domain>/ 按业务域的 HTTP 路由
|
||||
```
|
||||
|
||||
### 后端业务域的形状(`server/routes/<domain>/`)
|
||||
|
||||
命名约定:`index.ts` 是**聚合器**(把子路由挂到前缀上),`routes.ts` 是**叶子路由**。
|
||||
|
||||
| 域 | 文件 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `vehicles/` | `routes.ts` `repository.ts` `model.ts` `utils.ts` `types.ts` | ✅ 完整分层;SQL 有契约测试 |
|
||||
| `ele/` | `routes.ts` `repository.ts` `model.ts`(+ `model.test.ts` `routes.test.ts`) | ✅ 完整分层;改造前后 SQL/参数/响应体已做等价性验证 |
|
||||
| `feedback/` | `routes.ts` `repository.ts` `oss.ts`(+ `routes.test.ts`) | ✅ 完整分层;同上 |
|
||||
| `mileage/` | `index.ts`(聚合)+ `monitoring.ts` `targets.ts` `trend.ts` `daily-report.ts` `vehicle-recent.ts` + `repository.ts` + `*-model.ts` + `cache.ts` `oneos-api.ts` `daily-report-{service,store,scheduler}.ts` | ✅ 完整分层;车辆关联信息 SQL 与 18 个查询集中在此 |
|
||||
| `energy/` | `index.ts`(聚合)+ `electric.ts` `etc.ts` `hydrogen-station-board.ts` `hydrogen-bi-v2.ts` + `repository.ts` + `query-model.ts` `cache.ts` `constants.ts` | ✅ 完整分层;WHERE 片段组装(`buildFilterClauses` / `buildGroupSelect` / `buildScopedWhere` / `resolvedWhere`)与共享 SQL 片段也都在 repository / constants |
|
||||
| `scheduling/` | `index.ts`(聚合)+ `suggestions.ts` `notify.ts` `repository.ts` + `algorithm.ts` `notification-model.ts` | ✅ 完整分层;跨域数据源(里程车辆信息 / OneOS)显式注入 |
|
||||
| `hydrogen-heatmap/` | `routes.ts` `repository.ts` `model.ts`(+ `routes.test.ts`) | ✅ 完整分层;`buildWhere` 片段与参数顺序已锁定 |
|
||||
| `vehicle-heatmap/` | `routes.ts` `repository.ts` `model.ts`(+ `routes.test.ts`) | ✅ 完整分层;同时覆盖 MySQL(考核批次)与 PG(定位点)两个库 |
|
||||
|
||||
**全部八个域**(`vehicles` / `ele` / `feedback` / `vehicle-heatmap` / `hydrogen-heatmap` / `scheduling` / `energy` / `mileage`)
|
||||
由架构测试守护:必须存在 `repository.ts`,且该域**其他任何非测试文件都不得含 SQL**。
|
||||
其余域(`energy` / `mileage`)尚未拆出 repository——拆分时**不要改变 SQL 与参数顺序**,
|
||||
请按 `ele/routes.test.ts` 的配方先补契约测试,并用"改造前后同一批请求对比落库 SQL 与响应体"做等价性验证。
|
||||
|
||||
|
||||
### 中间件顺序(在 `app.ts` 中显式体现)
|
||||
|
||||
```
|
||||
cors → read-only → /api/auth(公开) → authMiddleware → 各业务域路由
|
||||
→ /api/health → /api/* 未匹配返回 JSON 404 → 静态托管 + SPA 回退
|
||||
```
|
||||
|
||||
两个已经踩过的坑,改动时不要退回去:
|
||||
|
||||
- **未匹配的 `/api/*` 必须返回 JSON 404**。若落到 SPA 回退,会返回 `200 text/html`,
|
||||
前端 `res.ok` 为真后在 `res.json()` 抛解析错误,把"路由不存在"伪装成数据问题。
|
||||
- **模块守卫必须 fail-closed**,写法是 `if (!canAccessX(user?.roles))`,
|
||||
不能写成 `if (user && !canAccessX(user.roles))`——后者在 `user` 缺失时静默放行。
|
||||
|
||||
### 角色与数据权限
|
||||
|
||||
- 角色常量唯一真源:`src/shared/auth/roles.ts`(前后端共用)。
|
||||
- 数据范围过滤与客户名脱敏:`src/server/auth/permissions.ts`。
|
||||
- `permissionLevel` 只表示**数据范围**,不等于模块权限;能源/调度/反馈各自另有白名单。
|
||||
|
||||
## 4. 新增一个业务模块的步骤
|
||||
|
||||
1. `src/modules/<domain>/`:建 `api.ts`(HTTP)、`model.ts`(纯逻辑)、`index.tsx`(入口)、`components/`。
|
||||
2. `src/app/modules.ts`:注册导航项,必要时用 `roles.ts` 的判断控制可见性。
|
||||
3. `src/server/routes/<domain>.ts`(或 `routes/<domain>/`):实现接口;需要模块级权限就加 **fail-closed** 守卫。
|
||||
4. 数据权限:读取后调用 `filterByPermission` + `maskCustomerNames`,或写等价的 SQL 过滤。
|
||||
5. 测试:纯逻辑进 `model.test.ts`;接口口径用**固定夹具**锁 SQL 与参数(参考 `server/routes/energy/routes.test.ts`)。
|
||||
6. 跑 `npm run lint && npm test && npm run build`。
|
||||
|
||||
## 5. 已知未尽事项
|
||||
|
||||
诚实记录,避免后来者以为已经做完:
|
||||
|
||||
- **后端八个域都已拆出 repository**,SQL 只允许出现在各域的 `repository.ts`(以及共享片段模块
|
||||
`energy/constants.ts`)中,由架构测试守护。新增查询请沿用同一形状:路由只做校验与组装,
|
||||
SQL 进 `repository.ts`,纯逻辑进 `model.ts`,并用 mock pool 的契约测试锁定 SQL 与参数。
|
||||
守卫用的是"语句形状"正则(如 `update <table> set`)而不是裸关键字,避免把
|
||||
`UpdateNotification` 或日志里的 "update error" 误判为 SQL;小写 SQL 同样能被抓到。
|
||||
- **运行时建表已集中到 `server/db/schema/`**,并在 `DB_READ_ONLY=1` 时整体跳过(由架构测试守护,
|
||||
已实测不触碰数据库)。它仍由业务接口在首次调用时触发,而不是只由 `bootstrap.ts` 调用——
|
||||
要彻底改成显式迁移,需要先建立数据库变更脚本流程,避免"代码里偷偷建表"。
|
||||
- **`src/lib/cn.ts` 刻意不引入 `tailwind-merge`**:全仓只有 2 处调用,为此新增一个
|
||||
传递依赖不划算。若将来条件类覆盖变多,再换 `clsx` + `tailwind-merge`。
|
||||
- **前端 `dist` 体积**:氢能看板单个 chunk 约 200 kB(gzip 50 kB),来自原型渲染方式(大量内联样式)。
|
||||
|
||||
### 已收敛的重复实现(不要退回多份)
|
||||
|
||||
| 能力 | 唯一实现 | 由测试守护 |
|
||||
| --- | --- | --- |
|
||||
| Excel 导出(拼装与写出) | `src/shared/xlsx.ts` | 架构测试:`modules/**` 不得再出现 `book_new` / `book_append_sheet` / `writeFile(` |
|
||||
| 高德 JSAPI 加载与底图 | `src/shared/amap.ts` | 架构测试:只有它可 import `@amap/amap-jsapi-loader` |
|
||||
| 运行时 DDL(建表/改表) | `src/server/db/schema/*` | 架构测试:其他 server 文件不得出现 `CREATE TABLE` / `ALTER TABLE`;每个 schema 模块必须检查 `ddlAllowed()` |
|
||||
| 自然日区间(近 N 天/快捷区间) | `src/shared/date-range.ts`、`modules/energy/daily-range/model.ts` | `date-range` 暂无独立测试;改动请补 |
|
||||
| 氢能数值格式化 | `modules/energy/hydrogen/model/display-format.ts` | `display-format.test.ts` |
|
||||
| 角色常量与模块可见性 | `src/shared/auth/roles.ts` | `app/modules.test.ts` |
|
||||
| 环境变量读取与校验 | `src/server/config.ts` | `auth/password.test.ts` 覆盖密码模式;JWT 缺失拒绝启动已实测 |
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# 登录方式与 Portainer 配置
|
||||
|
||||
不设置 `BI_AUTH_MODE` 时默认 `sso`,沿用业务系统 jumpToken 登录。
|
||||
设置 `BI_AUTH_MODE=password` 时只开放固定密码登录,关闭 SSO 换票入口。
|
||||
浏览器从 `/api/auth/config` 获取模式,密码不会打包到前端,不使用 VITE_ 密码变量。
|
||||
|
||||
## Portainer Stack
|
||||
|
||||
在现有 BI 服务的 `environment` 中合并以下配置,保留原镜像、端口、数据库等配置:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
BI_AUTH_MODE: ${BI_AUTH_MODE:-sso}
|
||||
BI_AUTH_PASSWORD: ${BI_AUTH_PASSWORD:-}
|
||||
JWT_SECRET: ${JWT_SECRET:?必须配置独立的随机签名密钥}
|
||||
DEV_BYPASS_AUTH: "0"
|
||||
NODE_ENV: production
|
||||
```
|
||||
|
||||
在 Stack 的 Environment variables 区域录入以下名称和值:
|
||||
|
||||
| 名称 | 固定密码模式 | SSO 模式 |
|
||||
| --- | --- | --- |
|
||||
| BI_AUTH_MODE | password | sso |
|
||||
| BI_AUTH_PASSWORD | 非空密码,允许短密码,推荐独立随机密码 | 留空 |
|
||||
| JWT_SECRET | 独立随机密钥,至少 32 字符 | 保留部署专用密钥 |
|
||||
|
||||
不要把真实密码提交进 Git。Portainer 中填写的 Stack 变量必须通过上面的 environment 映射才能进入容器。
|
||||
更新 Stack 并重新创建容器;单纯 Restart 不会更新容器环境变量。
|
||||
容器方式部署:Duplicate/Edit → Advanced container settings → Env,填写同样变量,再重新部署。
|
||||
需要先构建包含本功能的新镜像;旧镜像不认识这些变量。
|
||||
|
||||
## 权限、安全和验证
|
||||
|
||||
- 固定密码身份是共享只读身份,具有全量看板数据读取范围及能源访问权限,无调度/反馈管理角色;服务端拒绝写入方法。
|
||||
- 会话有效期 8 小时。更换密码、JWT_SECRET 或切换模式后,原密码会话失效。
|
||||
- 每个服务实例 15 分钟最多 20 次密码验证请求;多副本需在网关配置共享限流。共享限流可能被恶意请求耗尽,建议只在内网或受控网络开放。
|
||||
- 外网必须使用 HTTPS,避免明文传输密码和令牌。密码/环境变量对拥有容器管理权限的人可见。
|
||||
- 密码为空或全为空白,或签名密钥少于 32 字符时拒绝登录;无默认访问密码。不要沿用镜像中的旧默认 JWT_SECRET。
|
||||
- 短密码容易被猜中,仅建议在内网或受限访问环境使用;登录限流保持开启。
|
||||
- DB_READ_ONLY=1 时密码登录仍可用,业务写入仍被禁止。
|
||||
- 测试未登录访问、错误密码、正确密码、刷新恢复会话;切回 sso 后检查跳转登录。
|
||||
- 开发免登录仅 SSO 开发模式生效,生产必须保持 DEV_BYPASS_AUTH=0。
|
||||
|
||||
Portainer 官方说明:https://docs.portainer.io/user/docker/containers/advanced
|
||||
以及 https://docs.portainer.io/sts/user/docker/stacks/add
|
||||
@@ -0,0 +1,671 @@
|
||||
# LN-BI 项目移交交接说明
|
||||
|
||||
> 文档版本:1.0
|
||||
> 更新日期:2026-08-21
|
||||
> 对应代码版本:`1.1.15`
|
||||
> 对应分支 / 提交:`main` / `2518d9ee5440c39138427ce76d1f6e0faf8d9f29`
|
||||
> 项目仓库:`https://gitea.lnh2e.com/shishengliang/ln-bi.git`
|
||||
|
||||
## 1. 文档目的与交接边界
|
||||
|
||||
本文用于帮助新的研发、测试、运维和产品人员快速接管 LN-BI 项目,覆盖:
|
||||
|
||||
- 系统定位、代码结构和运行拓扑;
|
||||
- 登录认证、角色鉴权和数据权限;
|
||||
- 本地开发与免登录调试;
|
||||
- 前后端技术栈;
|
||||
- 数据对接方、数据库、外部服务及对应 BI / 报表;
|
||||
- 当前功能、后台任务、接口边界和部署流程;
|
||||
- 已知技术债、安全风险和交接检查清单。
|
||||
|
||||
本文依据当前仓库代码和配置编写,不记录数据库密码、JWT 密钥、API Key、OSS 密钥或 CI 仓库凭据。此类信息应通过密码管理器、Portainer / Docker 环境变量或 CI Secret 单独移交。
|
||||
|
||||
## 2. 项目概览
|
||||
|
||||
### 2.1 系统定位
|
||||
|
||||
LN-BI 是羚牛业务数据的统一 BI Web 应用。目前包含两组主入口:
|
||||
|
||||
| 主入口 | 主要模块 | 默认路由 |
|
||||
| --- | --- | --- |
|
||||
| 资产 BI | 资产管理、里程管理、车辆 / 加氢热力图、智能调度 | `/asset` |
|
||||
| 能源 BI | 氢能、电能、ETC | `/energy` |
|
||||
|
||||
应用还保留两个不在主导航中展示的后台入口:
|
||||
|
||||
| 隐藏入口 | 用途 | 路由 |
|
||||
| --- | --- | --- |
|
||||
| 充电记录导入 | 上传并管理电费 XLSX 数据 | `/ele/import` |
|
||||
| 用户反馈管理 | 查看、回复和推进用户反馈 | `/admin/feedback` |
|
||||
|
||||
页面内部主要使用 Hash 保存模块和子页面状态,例如 `/energy#hydrogen/overview`。入口兼容逻辑位于 `src/app/routing.ts`。
|
||||
|
||||
### 2.2 总体架构
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[羚牛业务系统] -->|一次性 jumpToken| B[LN-BI React 前端]
|
||||
B -->|/api/auth/exchange| C[Hono API]
|
||||
C -->|校验 jumpToken / 获取用户角色| A
|
||||
C -->|签发 8 小时本地 JWT| B
|
||||
B -->|Bearer JWT| C
|
||||
|
||||
C --> D[(主业务 MySQL / 跨 Schema)]
|
||||
C --> E[(氢能 MySQL 连接\n默认复用主库)]
|
||||
C --> F[(车辆位置 PostgreSQL)]
|
||||
C --> G[OneOS 里程 API]
|
||||
C --> H[阿里云 OSS]
|
||||
B --> I[高德地图 JS API]
|
||||
```
|
||||
|
||||
生产环境由一个 Node.js 进程同时提供 REST API 和 Vite 构建后的静态页面。开发环境中,Vite 前端和 Hono 后端分别运行,由 Vite 将 `/api` 代理到后端。
|
||||
|
||||
### 2.3 关键目录
|
||||
|
||||
| 路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `src/App.tsx` | 应用入口、认证门禁、主路由和隐藏页面装配 |
|
||||
| `src/app/modules.ts` | 资产 / 能源导航模块注册与角色可见性 |
|
||||
| `src/auth/` | 前端认证状态、JWT 注入、未授权页面 |
|
||||
| `src/shared/auth/` | 前后端共用角色常量和模块访问判断 |
|
||||
| `src/server/` | Hono 服务、认证中间件、数据库连接、API 路由和后台任务 |
|
||||
| `src/modules/` | 各 BI 页面和交互实现 |
|
||||
| `src/modules/energy/hydrogen-bi-v2/` | 当前氢能 BI 原型迁入版、真实数据适配器和下钻实现 |
|
||||
| `docs/` | 接口、重构和交接文档 |
|
||||
| `Dockerfile` | 前端构建 + Node 运行时镜像 |
|
||||
| `docker-compose.yml` | 当前容器部署参数样例 |
|
||||
| `woodpecker.yml` | Woodpecker CI 构建、测试、镜像推送流程 |
|
||||
|
||||
### 2.4 当前质量基线
|
||||
|
||||
2026-08-21 在当前提交和本地依赖环境完成以下验证:
|
||||
|
||||
| 检查 | 结果 |
|
||||
| --- | --- |
|
||||
| `npm run lint` | 通过 |
|
||||
| `npm test` | 113 项通过,0 项失败 |
|
||||
| `npm run build` | 通过,Vite 成功生成生产构建 |
|
||||
|
||||
测试和构建会输出 Node.js `DEP0205`(`module.register()` 已弃用)警告,目前不阻断运行;后续升级 `tsx` / Node 工具链时应处理。
|
||||
|
||||
## 3. 登录认证方式
|
||||
|
||||
### 3.1 正常登录链路
|
||||
|
||||
项目自身没有用户名 / 密码登录表单,正常入口依赖羚牛业务系统单点跳转:
|
||||
|
||||
1. 用户先登录羚牛业务系统。
|
||||
2. 业务系统跳转到 LN-BI,并在 URL 中携带一次性 `jumpToken`。
|
||||
3. 前端调用 `GET /api/auth/exchange?jumpToken=...`。
|
||||
4. LN-BI 后端调用业务系统的 `issueTokenByJump` 接口校验一次性令牌并取得用户、部门编码和角色。
|
||||
5. 后端根据角色计算数据权限级别,并通过主业务库 `tab_department` 补全部门名称。
|
||||
6. 后端使用 `JWT_SECRET` 签发有效期 8 小时的 LN-BI JWT。
|
||||
7. 前端将 JWT 和用户信息保存到当前标签页的 `sessionStorage`:
|
||||
- `bi_jwt`
|
||||
- `bi_user`
|
||||
8. 前端从地址栏移除 `jumpToken`,之后所有受保护 API 均发送 `Authorization: Bearer <JWT>`。
|
||||
9. API 返回 `401` 时,前端清空本地会话并显示“会话已过期”。
|
||||
|
||||
关键实现:
|
||||
|
||||
- 前端:`src/auth/AuthProvider.tsx`
|
||||
- Token 注入:`src/auth/api-client.ts`
|
||||
- Token 换取:`src/server/auth/login.ts`
|
||||
- API 认证:`src/server/auth/middleware.ts`
|
||||
|
||||
### 3.2 公开接口与受保护接口
|
||||
|
||||
不要求 JWT 的接口:
|
||||
|
||||
- `/api/health`
|
||||
- `/api/auth/*`
|
||||
|
||||
其余 `/api/*` 默认经过统一认证中间件。能源、智能调度、加氢热力图和反馈管理在统一认证之上还有模块级或接口级角色校验。
|
||||
|
||||
### 3.3 会话特征
|
||||
|
||||
| 项目 | 当前实现 |
|
||||
| --- | --- |
|
||||
| Token 类型 | HS256 JWT(`jsonwebtoken` 默认签名算法) |
|
||||
| 有效期 | 8 小时 |
|
||||
| 浏览器存储 | `sessionStorage`,关闭标签页后失效 |
|
||||
| 用户来源 | 羚牛业务系统 jumpToken 换取 |
|
||||
| 部门名称来源 | 主业务库 `tab_department` |
|
||||
| 退出方式 | 当前无独立退出接口;清除标签页会话或收到 401 后清理 |
|
||||
|
||||
## 4. 角色鉴权与数据权限
|
||||
|
||||
权限分为两层,接手时必须分别理解:
|
||||
|
||||
1. **模块访问权限**:决定能否进入能源、智能调度或反馈后台。
|
||||
2. **数据范围权限**:决定用户可查看全量、部门或个人负责的数据。
|
||||
|
||||
### 4.1 数据范围角色
|
||||
|
||||
| 业务系统角色 | JWT `permissionLevel` | 数据范围 |
|
||||
| --- | --- | --- |
|
||||
| `所有权限`、`数智中心`、`BI-Leader` | `full` | 全量数据 |
|
||||
| `BI-Leader-Dep` | `department` | `departmentName / department = user.depName` |
|
||||
| 其他角色或无上述角色 | `personal` | `managerId = user.userId` |
|
||||
|
||||
数据过滤和客户名称脱敏由 `src/server/auth/permissions.ts` 提供。目前明确接入该过滤的主要范围包括:
|
||||
|
||||
- 资产车辆统计与列表;
|
||||
- 里程实时监控;
|
||||
- 里程考核车辆明细;
|
||||
- 智能调度建议。
|
||||
|
||||
注意:数据权限不是数据库行级安全,而是路由读取数据后在服务端过滤。新增 API 时必须主动调用统一过滤逻辑或实现等价的 SQL 过滤,不能只依赖前端隐藏。
|
||||
|
||||
### 4.2 模块访问角色
|
||||
|
||||
| 模块 / 能力 | 允许角色 | 前端控制 | 后端控制 |
|
||||
| --- | --- | --- | --- |
|
||||
| 资产管理、里程管理、车辆热力图 | 已认证用户 | 导航默认展示 | 统一 JWT 中间件 |
|
||||
| 能源 BI(氢能 / 电能 / ETC) | `BI-LEADER-ENERGY` 或 `所有权限` | `/energy` 门禁 | `/api/energy/*` 守卫 |
|
||||
| 加氢热力图 | `BI-LEADER-ENERGY` 或 `所有权限` | 无权限时不出现在热力图子导航 | `/api/hydrogen-heatmap/*` 守卫 |
|
||||
| 智能调度 | `BI-SCHEDULE-OPT` | 无角色时不显示模块 | `/api/scheduling/*` 守卫 |
|
||||
| 反馈管理 | `BI-ADMIN-FEEDBACK` 或任一全量数据角色 | 隐藏页面,需直接访问 | 管理列表与更新接口校验 |
|
||||
| 用户反馈提交 / 我的反馈 | 已认证用户 | 页面反馈入口 | 统一 JWT 中间件 |
|
||||
|
||||
角色常量和判断位于 `src/shared/auth/roles.ts`。
|
||||
|
||||
### 4.3 容易误解的权限边界
|
||||
|
||||
- `full` 只表示数据范围,不代表自动拥有所有模块权限。
|
||||
- `数智中心`、`BI-Leader` 虽会获得全量数据,但当前不会自动进入能源模块;能源仅额外接受 `所有权限`。
|
||||
- 智能调度当前仅接受 `BI-SCHEDULE-OPT`,不会因为拥有 `full` 数据权限自动放行。
|
||||
- 前端隐藏不是安全措施;任何新增受限能力都必须增加后端守卫。
|
||||
|
||||
## 5. 本地免登录调试环境
|
||||
|
||||
### 5.1 环境要求
|
||||
|
||||
- Node.js 22(Docker 和 CI 均使用 Node 22);
|
||||
- npm,依赖版本以 `package-lock.json` 为准;
|
||||
- 能访问所需数据库和外部 API 的网络环境;
|
||||
- 本地配置文件 `.env`。该文件已被 `.gitignore` 忽略,禁止提交。
|
||||
|
||||
### 5.2 最小启动步骤
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
npm run dev
|
||||
```
|
||||
|
||||
启动后:
|
||||
|
||||
| 服务 | 地址 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| Vite 前端 | `http://localhost:3000` | 对局域网开放,`/api` 代理到 3001 |
|
||||
| Hono 后端 | `http://localhost:3001` | `SERVER_PORT` 未设置时的默认端口 |
|
||||
| 健康检查 | `http://localhost:3001/api/health` | 只验证进程可访问,不验证数据库 |
|
||||
|
||||
### 5.3 免登录开关
|
||||
|
||||
本地前后端需要同时开启免登录:
|
||||
|
||||
```dotenv
|
||||
# 仅用于本地开发,生产环境严禁设置为 1
|
||||
VITE_DEV_BYPASS_AUTH=1
|
||||
DEV_BYPASS_AUTH=1
|
||||
```
|
||||
|
||||
- `VITE_DEV_BYPASS_AUTH=1`:前端直接构造“本地开发”全权限用户,仅在 Vite dev 模式生效。
|
||||
- `DEV_BYPASS_AUTH=1`:后端认证中间件注入本地开发用户,否则前端虽然进入页面,API 仍会返回 401。
|
||||
- 本地用户包含 `所有权限`、`BI-SCHEDULE-OPT`、`BI-ADMIN-FEEDBACK`、`BI-LEADER-ENERGY`。
|
||||
- 免登录只绕过认证,不会替代数据库、OneOS API、高德地图或 OSS 配置。
|
||||
|
||||
### 5.4 `.env` 配置模板
|
||||
|
||||
以下仅列变量名和占位符,不得把真实值写入本文或提交到 Git:
|
||||
|
||||
```dotenv
|
||||
# 认证
|
||||
EXTERNAL_API_BASE=https://<业务系统域名>
|
||||
JWT_SECRET=<高强度随机密钥>
|
||||
DEV_BYPASS_AUTH=1
|
||||
VITE_DEV_BYPASS_AUTH=1
|
||||
|
||||
# 主业务 MySQL
|
||||
DB_HOST=<host>
|
||||
DB_PORT=3306
|
||||
DB_USER=<user>
|
||||
DB_PASSWORD=<password>
|
||||
DB_NAME=<database>
|
||||
|
||||
# 氢能 MySQL;不填时复用 DB_*
|
||||
HYDROGEN_DB_HOST=<host>
|
||||
HYDROGEN_DB_PORT=3306
|
||||
HYDROGEN_DB_USER=<user>
|
||||
HYDROGEN_DB_PASSWORD=<password>
|
||||
HYDROGEN_DB_NAME=<database>
|
||||
|
||||
# 历史里程 MySQL 连接配置
|
||||
# 当前 src/server/mileage-db.ts 未被运行时代码引用;不要误认为修改后会影响里程页面
|
||||
MILEAGE_DB_HOST=<host>
|
||||
MILEAGE_DB_PORT=3306
|
||||
MILEAGE_DB_USER=<user>
|
||||
MILEAGE_DB_PASSWORD=<password>
|
||||
MILEAGE_DB_NAME=<database>
|
||||
|
||||
# 车辆位置 PostgreSQL
|
||||
HEATMAP_DB_HOST=<host>
|
||||
HEATMAP_DB_PORT=5432
|
||||
HEATMAP_DB_USER=<read-only-user>
|
||||
HEATMAP_DB_PASSWORD=<password>
|
||||
HEATMAP_DB_NAME=<database>
|
||||
HEATMAP_DB_SSL=false
|
||||
|
||||
# OneOS 里程 API
|
||||
ONEOS_MILEAGE_API_BASE_URL=https://<api-host>
|
||||
ONEOS_MILEAGE_API_KEY=<api-key>
|
||||
ONEOS_MILEAGE_API_TIMEOUT_MS=20000
|
||||
|
||||
# 高德地图
|
||||
AMAP_WEB_KEY=<web-key>
|
||||
AMAP_SECURITY_JS_CODE=<security-code>
|
||||
|
||||
# 用户反馈截图 OSS
|
||||
OSS_REGION=<region>
|
||||
OSS_ENDPOINT=<endpoint>
|
||||
OSS_BUCKET=<bucket>
|
||||
OSS_ACCESS_KEY_ID=<access-key-id>
|
||||
OSS_ACCESS_KEY_SECRET=<access-key-secret>
|
||||
OSS_BASE_DIR=<directory>
|
||||
|
||||
# 运行参数
|
||||
SERVER_PORT=3001
|
||||
MILEAGE_REPORT_AUTO_ARCHIVE=0
|
||||
```
|
||||
|
||||
本地调试建议将 `MILEAGE_REPORT_AUTO_ARCHIVE=0`,避免开发进程在 06:30 自动生成正式日报快照。
|
||||
|
||||
## 6. 技术栈
|
||||
|
||||
### 6.1 前端
|
||||
|
||||
| 分类 | 技术 |
|
||||
| --- | --- |
|
||||
| 语言 | TypeScript、TSX、CSS |
|
||||
| 框架 | React 19 |
|
||||
| 构建工具 | Vite 6 |
|
||||
| 样式 | Tailwind CSS 4 + 模块专用 CSS |
|
||||
| 图表 | Recharts 3;氢能原型包含自定义 DOM / CSS 图表 |
|
||||
| 动效 | Motion for React |
|
||||
| 图标 | Lucide React |
|
||||
| Excel | SheetJS `xlsx` |
|
||||
| 地图 | 高德地图 JS API |
|
||||
| 路由 | 浏览器 Path + Hash 自研轻量路由,无 React Router |
|
||||
|
||||
### 6.2 后端
|
||||
|
||||
| 分类 | 技术 |
|
||||
| --- | --- |
|
||||
| 语言 / 运行时 | TypeScript、Node.js 22、ES Module |
|
||||
| Web 框架 | Hono 4 + `@hono/node-server` |
|
||||
| TypeScript 运行 | `tsx` |
|
||||
| 认证 | `jsonwebtoken` |
|
||||
| MySQL | `mysql2/promise` |
|
||||
| PostgreSQL | `pg` |
|
||||
| 对象存储 | `ali-oss` |
|
||||
| 配置 | `dotenv` / 容器环境变量 |
|
||||
| 测试 | Node.js 内置 Test Runner |
|
||||
|
||||
### 6.3 构建与部署
|
||||
|
||||
- `npm run lint`:TypeScript 静态检查;
|
||||
- `npm test`:运行 `src/**/*.test.ts`;
|
||||
- `npm run build`:生成 Vite `dist`;
|
||||
- `npm run start`:Node 直接通过 `tsx` loader 启动服务;
|
||||
- Docker:Node 22 Alpine 多阶段构建;
|
||||
- CI:Woodpecker 执行安装、静态检查、测试、构建、Docker 镜像构建和 Harbor 推送;
|
||||
- 镜像标签:`<分支名>-<package.json version>`;
|
||||
- 当前 Compose 使用 host 网络,容器服务端口由 `SERVER_PORT` 注入,现有部署样例为 `8111`。
|
||||
|
||||
## 7. 数据对接与数据源映射
|
||||
|
||||
### 7.1 运行时数据源总表
|
||||
|
||||
| 数据源 / 对接方 | 连接方式 | 配置入口 | 主要数据 | 消费模块 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 羚牛业务系统认证服务 | HTTPS API | `EXTERNAL_API_BASE` | jumpToken、用户、部门编码、角色 | 登录认证 |
|
||||
| 主业务 MySQL | MySQL | `DB_*` | 车辆、合同、客户、部门、资产流转、电费、ETC、反馈、调度记录等 | 资产、里程关联信息、调度、电能、ETC、反馈 |
|
||||
| 氢能业务库 | MySQL | `HYDROGEN_DB_*`;未配置则复用 `DB_*` | 加氢流水、加氢站、付款 / 结算、站点余额等 | 氢能 BI、单站、按日、下钻 |
|
||||
| 里程考核数据 | 主业务 MySQL / 跨 Schema 查询 | 当前实际使用 `DB_*` | 考核目标、考核车辆、日报快照 | 里程统计、日报、智能调度 |
|
||||
| OneOS 里程服务 | HTTPS API | `ONEOS_MILEAGE_API_*` | 按日 / 区间车辆里程、来源协议 | 里程监控、日报、调度 |
|
||||
| 车辆位置分析库 | PostgreSQL 只读 | `HEATMAP_DB_*` | 每车每日首个有效定位点 | 车辆热力图 |
|
||||
| 高德地图 | 浏览器 JS API | `AMAP_WEB_KEY`、`AMAP_SECURITY_JS_CODE` | 地图底图、空间展示 | 车辆 / 加氢热力图 |
|
||||
| 阿里云 OSS | OSS SDK | `OSS_*` | 用户反馈截图 | 反馈提交、反馈管理 |
|
||||
| 本地地区映射 | JSON | `src/server/routes/mileage/region-map.json` | 城市到运营区域映射 | 里程筛选和区域聚合 |
|
||||
|
||||
`src/server/mileage-db.ts` 仍定义了一组 `MILEAGE_DB_*` MySQL 连接,但当前没有任何运行时代码导入该连接池。因此它属于历史遗留配置,不计入当前有效运行时数据源;接手人不应通过修改 `MILEAGE_DB_*` 来排查现有里程页面。
|
||||
|
||||
### 7.2 主业务 MySQL 的主要表 / Schema
|
||||
|
||||
当前部署样例中 `DB_NAME` 指向业务库,同时代码存在 `lingniu_prod.*` 跨 Schema 查询。数据库账号必须具备实际所需 Schema 的最小权限。
|
||||
|
||||
| 数据域 | 代表表 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| 车辆主数据 | `vehicle_info`、`vehicle_status`、`vehicle_model` | 资产、车辆归属、车型、运营状态 |
|
||||
| 租赁 / 客户 | `vehicle_lease_order_record`、`vehicle_lease_contract_info`、`customer_info` | 客户、部门、经理、项目 |
|
||||
| 资产流转 | `delivery_vehicle`、`return_vehicle_task`、`vehicle_replacement` | 交车、退车、换车周统计及明细 |
|
||||
| 实时车辆 | `tab_truck_remote_sync_realtime_info` | 当日里程、车辆省份 |
|
||||
| 里程考核 | `lingniu_prod.tab_mileage_assessment_target`、`lingniu_prod.tab_mileage_assessment_vehicle` | 目标、车辆、完成率 |
|
||||
| 里程日报 | `lingniu_prod.tab_mileage_daily_report` | 每日归档快照 |
|
||||
| 电费 | `bi_ele_charge_record` | XLSX 导入后的充电记录 |
|
||||
| ETC | `etc_toll_record`、`energy_etc_bill` | 通行明细、应收和已收 |
|
||||
| 调度 | `tab_scheduling_notifications` | 调度干预、执行状态和历史 |
|
||||
| 用户反馈 | `bi_user_feedback` | 反馈、截图地址、回复和状态 |
|
||||
|
||||
### 7.3 氢能 BI 主要数据表
|
||||
|
||||
| 表 | 用途 |
|
||||
| --- | --- |
|
||||
| `hydrogen_fuel_ledger` | 加氢主流水,统计加氢量、成本、对客金额、车辆、客户、核对状态和数据来源 |
|
||||
| `hydrogen_station` | 历史加氢站主数据 |
|
||||
| `new_hydrogen_site` | 当前业务站点主数据及省市区等属性 |
|
||||
| `hydrogen_station_payment` | 加氢站付款 / 结算记录 |
|
||||
| `new_hydrogen_site_balance_record` | 站点预充值余额记录 |
|
||||
| `tab_outside_hydrogen_site` | 外部站点与内部站点、坐标的映射,用于热力图 |
|
||||
| `common_district` | 行政区划名称映射 |
|
||||
|
||||
氢能 V2 API 统一提供总览、按日树、下钻等视图。旧 `/hydrogen/*` 接口仍保留用于兼容和回滚,但当前氢能页面入口只装配 `hydrogen-bi-v2/PrototypeBoard.tsx`。
|
||||
|
||||
### 7.4 数据口径注意事项
|
||||
|
||||
- 氢能“车辆归属”和“成本承担方”是两个独立维度,不得互相替代。
|
||||
- 氢能成本承担方按业务字段归纳为“我司承担、客户承担、其他”;利润口径是客户承担订单的“对客总价 - 等量订单成本总价”。
|
||||
- 电能导入按 `order_no` 去重,并根据车辆信息匹配内部 / 外部 / 未知车辆。
|
||||
- ETC 金额、应收和已收直接读取台账,不做推算;没有数据时前端展示空状态。
|
||||
- 里程按自然日和上海时区处理;OneOS API 支持单日和区间查询,并按协议优先级归一化来源。
|
||||
- 客户名称会根据数据权限在服务端进行脱敏。
|
||||
|
||||
## 8. 当前功能清单
|
||||
|
||||
### 8.1 资产管理 BI
|
||||
|
||||
| 功能域 | 当前能力 |
|
||||
| --- | --- |
|
||||
| 资产总览 | 总资产、运营、库存、待交付、周交车 / 退车 / 换车 |
|
||||
| 车型分析 | 按车辆类型、车型、批次逐级统计和展开 |
|
||||
| 部门分析 | 按部门 / 经理统计、展开车辆明细 |
|
||||
| 区域分析 | 按大区、省市、客户筛选与统计 |
|
||||
| 客户分析 | 客户多选、品牌、部门、经理、区域筛选 |
|
||||
| 库存分析 | 按区域 / 车型切换,支持多级展开和筛选 |
|
||||
| 资产流转 | 自定义日期范围,交车 / 退车 / 换车趋势与明细下钻 |
|
||||
| 车辆明细 | 车牌、车型、批次、客户、状态、位置等筛选和弹窗 |
|
||||
| 导出 | 资产相关列表和明细 Excel 导出 |
|
||||
| 自动刷新 | 主页面数据每 60 秒刷新 |
|
||||
|
||||
### 8.2 里程管理 BI
|
||||
|
||||
| 子页面 | 当前能力 |
|
||||
| --- | --- |
|
||||
| 实时监控 | 当日 / 指定日 / 区间里程,来源协议优先级、部门、客户、项目、主体、状态、区域、品牌、里程区间等筛选 |
|
||||
| 实时监控 | 当日 / 累计 / 统计时间排序,异常和在线状态识别,车辆详情 |
|
||||
| 统计报表 | 考核目标、目标车辆、累计完成率、当年完成率、日均要求、趋势下钻 |
|
||||
| 每日汇报 | 运营 / 库存车辆、当日里程、环比、车型与地区分组、近 7 日趋势、历史快照 |
|
||||
| 导出 | 监控区间和车辆汇总 Excel 导出 |
|
||||
| 缓存 | 服务启动立即刷新,之后每分钟刷新实时监控缓存 |
|
||||
| 自动归档 | 上海时间每天 06:30 归档上一自然日日报,可通过环境变量关闭 |
|
||||
|
||||
### 8.3 智能调度
|
||||
|
||||
| 功能 | 当前能力 |
|
||||
| --- | --- |
|
||||
| 建议生成 | 根据考核里程、剩余天数、车辆状态和车型生成高低里程调度建议 |
|
||||
| 筛选 | 按建议类型、部门、车型等条件筛选和搜索 |
|
||||
| 详情 | 查看当前车辆、候选车辆及差距 |
|
||||
| 操作 | 单条 / 批量登记调度干预 |
|
||||
| 历史 | 通知 / 干预记录查询、状态更新、取消或完成 |
|
||||
| 导出 | 建议列表 CSV 导出 |
|
||||
|
||||
### 8.4 车辆热力图
|
||||
|
||||
- 按日期范围、车牌 / VIN、考核批次筛选;
|
||||
- 定位活跃度 / 车辆覆盖度切换;
|
||||
- 网格聚合、全国概览、区域下钻和附近车辆;
|
||||
- 地图和详情面板可独立展开,支持全屏;
|
||||
- PostgreSQL 连接配置为只读事务默认值。
|
||||
|
||||
### 8.5 加氢热力图
|
||||
|
||||
- 按日期、站点搜索、承担维度筛选;
|
||||
- 加氢量、加氢频次、车辆覆盖三种热力指标;
|
||||
- 加氢站排名、区域下钻、附近站点和 GPS 覆盖提示;
|
||||
- 仅对有有效坐标的数据进行地图聚合;
|
||||
- 受能源角色控制。
|
||||
|
||||
### 8.6 氢能经营 BI(当前 V2 页面)
|
||||
|
||||
| 页面 / 维度 | 当前能力 |
|
||||
| --- | --- |
|
||||
| 全局总览 | 年份、核对状态、车辆归属筛选;累计加氢量、累计成本、利润、本月、本日 KPI |
|
||||
| 趋势 | 月度加氢量、月度收支、站点 Top5、区域省 / 市占比 |
|
||||
| 汇总 | 加氢站汇总、客户账单汇总、列表折叠、排序和筛选 |
|
||||
| KPI 下钻 | 支持先站点后客户或先客户后站点,再到车辆 / 流水 |
|
||||
| 图表下钻 | 月份、站点、区域、客户等上下文穿透 |
|
||||
| 账单下钻 | 客户 → 日期 → 车辆流水;站点 → 日期 → 客户 / 流水 |
|
||||
| 按日页面 | 日期范围、车辆归属、核对状态;日期 → 站点 → 客户 → 车辆 / 数据源多层展开 |
|
||||
| 单站页面 | 站点日期筛选、日汇总、趋势、流水和导出 |
|
||||
| 导出 | KPI 穿透、客户账单、站点账单、每日明细 Excel |
|
||||
| 接口 | `/api/energy/h2/v2/meta`、`overview`、`daily`、`daily-tree`、`drill` |
|
||||
|
||||
说明:原型源码中仍有演示常量和兼容组件;真实运行数据应以 `prototype-adapter.ts`、`prototype-real-daily.tsx`、`prototype-real-drills.tsx` 和 V2 API 返回为准。修改页面时必须同时检查 Web 与移动端,不能只以测试通过代替原型截图验收。
|
||||
|
||||
### 8.7 电能经营 BI
|
||||
|
||||
| 页面 / 能力 | 当前实现 |
|
||||
| --- | --- |
|
||||
| 按日 | 日充电量、费用、订单、内部 / 外部车辆和趋势 |
|
||||
| 总览 | 汇总 KPI、月份趋势、车辆归属统计 |
|
||||
| 数据导入 | 隐藏入口上传 `.xlsx`,按订单号去重、批次管理、记录搜索和归属汇总 |
|
||||
| 数据表 | `bi_ele_charge_record`,接口首次调用时自动确保建表 |
|
||||
|
||||
### 8.8 ETC 看板
|
||||
|
||||
- 通行明细笔数;
|
||||
- 涉及车辆去重数;
|
||||
- 通行费金额;
|
||||
- ETC 账单数、应收和已收;
|
||||
- 最新通行时间;
|
||||
- 无台账时展示“暂无数据”,不生成模拟数据。
|
||||
|
||||
### 8.9 用户反馈闭环
|
||||
|
||||
- 普通用户提交新维度、Bug、体验或其他反馈;
|
||||
- 支持最多 6 张截图,单张最大 5 MB,上传至 OSS;
|
||||
- 用户查看自己的反馈历史;
|
||||
- 管理员按状态查看、回复和更新为待处理 / 处理中 / 已完成 / 已忽略;
|
||||
- 反馈表由接口首次调用时自动创建并兼容补列。
|
||||
|
||||
## 9. API 路由概览
|
||||
|
||||
| 路由前缀 | 用途 |
|
||||
| --- | --- |
|
||||
| `/api/auth` | jumpToken 换 JWT、查看当前 JWT 用户 |
|
||||
| `/api/vehicles` | 资产总览、车型 / 部门 / 区域 / 客户 / 库存、流转、车辆明细 |
|
||||
| `/api/mileage` | 实时监控、目标、趋势、车辆近期里程、日报及历史 |
|
||||
| `/api/scheduling` | 调度建议、通知和执行记录 |
|
||||
| `/api/energy` | 氢能 V2 / 兼容接口、电能经营、ETC |
|
||||
| `/api/ele` | 电费 XLSX 导入、列表、批次、聚合 |
|
||||
| `/api/vehicle-heatmap` | 车辆位置热力图配置、元数据、点位和附近车辆 |
|
||||
| `/api/hydrogen-heatmap` | 加氢热力图配置、元数据、点位和附近站点 |
|
||||
| `/api/feedback` | 反馈提交、截图上传、我的反馈、管理列表和更新 |
|
||||
|
||||
## 10. 后台任务与自动建表
|
||||
|
||||
### 10.1 服务启动动作
|
||||
|
||||
`src/server/bootstrap.ts` 在正式服务进程启动时执行:
|
||||
|
||||
1. 确保 `tab_scheduling_notifications` 存在;
|
||||
2. 立即刷新里程监控缓存;
|
||||
3. 每 60 秒刷新里程监控缓存;
|
||||
4. 启动里程日报 06:30 自动归档调度器。
|
||||
|
||||
### 10.2 按需自动建表
|
||||
|
||||
以下接口会在首次使用时执行 `CREATE TABLE IF NOT EXISTS`:
|
||||
|
||||
- 电费:`bi_ele_charge_record`;
|
||||
- 用户反馈:`bi_user_feedback`;
|
||||
- 里程日报:`lingniu_prod.tab_mileage_daily_report`;
|
||||
- 智能调度:`tab_scheduling_notifications`(服务启动时)。
|
||||
|
||||
生产数据库账号若严格只读,这些功能会启动或调用失败。交接时应明确“读数据账号”和“应用写表账号”的权限边界。
|
||||
|
||||
## 11. 开发、测试和发布流程
|
||||
|
||||
### 11.1 常用命令
|
||||
|
||||
```bash
|
||||
# 安装锁定依赖
|
||||
npm ci
|
||||
|
||||
# 前后端开发模式
|
||||
npm run dev
|
||||
|
||||
# 仅后端 / 仅前端
|
||||
npm run dev:server
|
||||
npm run dev:client
|
||||
|
||||
# 质量检查
|
||||
npm run lint
|
||||
npm test
|
||||
npm run build
|
||||
|
||||
# 生产式本地启动(需先 build)
|
||||
npm run start
|
||||
```
|
||||
|
||||
### 11.2 发版步骤
|
||||
|
||||
1. 确认工作区只包含本次变更,排除 `.DS_Store`、临时脚本和本地数据文件。
|
||||
2. 执行 `npm run lint && npm test && npm run build`。
|
||||
3. 按语义版本更新 `package.json` 和 `package-lock.json`。
|
||||
4. 提交并推送到目标分支。
|
||||
5. Woodpecker 根据分支和版本生成镜像标签并推送 Harbor。
|
||||
6. 在 Portainer / Compose 中更新镜像版本,保留原镜像标签用于回滚。
|
||||
7. 验证健康检查、登录跳转、关键 API、Web 页面和移动端页面。
|
||||
|
||||
版本只在 Git 中更新不代表已部署;必须分别确认:
|
||||
|
||||
- 代码已推送;
|
||||
- CI 已通过;
|
||||
- 镜像已推送;
|
||||
- 部署已更新;
|
||||
- 页面和数据已验收。
|
||||
|
||||
## 12. 日志与排障入口
|
||||
|
||||
| 现象 | 优先检查 |
|
||||
| --- | --- |
|
||||
| 无法从业务系统登录 | 浏览器 URL 是否有 `jumpToken`;`/api/auth/exchange` 响应;`EXTERNAL_API_BASE`;后端认证日志 |
|
||||
| 页面能进但 API 401 | 前后端免登录是否同时开启;`bi_jwt` 是否存在;JWT_SECRET 是否一致 |
|
||||
| 页面 403 | 业务系统角色、JWT 中 `roles`、模块白名单,不要只看 `permissionLevel` |
|
||||
| 健康检查正常但页面无数据 | `/api/health` 不检查数据库;继续检查具体 API、数据库连接和 SQL 权限 |
|
||||
| 里程无数据 | OneOS API 地址 / Key、网络、traceId、协议兼容降级日志、主库车辆关联信息 |
|
||||
| 热力图无底图 | 高德 Key 和安全码、域名白名单、浏览器控制台 |
|
||||
| 车辆热力图无点位 | PostgreSQL 连接、只读权限、日期范围、`is_heatmap_eligible` |
|
||||
| 氢能数据慢 | V2 API 查询耗时、筛选范围、数据库索引、是否误走兼容接口 |
|
||||
| 反馈图片失败 | OSS 配置、Bucket 权限、文件类型和 5 MB 限制 |
|
||||
| 日报未归档 | `MILEAGE_REPORT_AUTO_ARCHIVE`、容器时区、06:30 日志、日报表写权限 |
|
||||
|
||||
## 13. 已知风险与优先整改项
|
||||
|
||||
### P0:凭据管理
|
||||
|
||||
1. 当前仓库的部署配置和部分数据库连接代码存在硬编码凭据或默认口令。交接后应立即:
|
||||
- 将数据库、JWT、Harbor、OSS、API Key 全部迁移到 CI Secret / Portainer Secret / 环境变量;
|
||||
- 删除代码和配置中的真实 fallback;
|
||||
- 对已经进入 Git 历史的凭据执行轮换;
|
||||
- 不要只删除当前文件内容而忽略历史提交。
|
||||
2. `Dockerfile` 和认证代码存在可预测的 JWT 默认密钥。生产必须显式注入随机 `JWT_SECRET`,并在缺失时让应用拒绝启动。
|
||||
|
||||
### P1:认证与权限
|
||||
|
||||
1. 前端当前用公开的 `/api/health` 判断缓存 JWT 是否有效,因此该请求并未真正校验 Token;过期 Token 会在第一次访问受保护 API 时才被清理。应改为调用 `/api/auth/me`。
|
||||
2. `jumpToken` 通过查询参数传递,可能进入代理访问日志;业务条件允许时应改为 POST body,并限制日志记录。
|
||||
3. `cors()` 当前未限制来源;生产应配置允许域名白名单。
|
||||
4. `full` 数据角色与模块角色不是同一套白名单,容易产生“能看全量数据但进不了模块”的误解。变更角色策略前应由产品 / 管理员确认。
|
||||
5. 隐藏页面不是授权控制。充电导入接口目前仅依赖“已登录”,若属于管理能力,应补独立角色和后端守卫。
|
||||
|
||||
### P1:数据与运行稳定性
|
||||
|
||||
1. 主库、跨 Schema 查询和专用数据库连接并存,部署账号权限和 Schema 默认值需形成正式清单。
|
||||
2. `src/server/mileage-db.ts` 是当前未被引用的历史连接文件,`docker-compose.yml` 仍保留相应变量,容易误导排障;确认无外部依赖后应删除或重新接入。
|
||||
3. 应用启动和部分接口会自动建表;数据库权限收紧前需先迁移为正式数据库变更脚本。
|
||||
4. 氢能兼容接口和 V2 接口同时存在,新增功能应明确修改当前 V2 路径,避免只修旧接口。
|
||||
5. 氢能原型目录仍包含演示常量;新增展示必须确认数据来自真实 API,禁止把演示数据带入经营口径。
|
||||
6. 车辆和加氢热力图的默认日期目前为代码常量,长期运行应改为根据数据水位或当前日期计算。
|
||||
|
||||
### P2:工程治理
|
||||
|
||||
1. 仓库缺少统一根 README,本交接文档可作为后续 README 的基础。
|
||||
2. 本地 `.DS_Store` 容易进入工作区,建议补充全局 / 项目忽略规则。
|
||||
3. 目前使用 Path + Hash 自研路由;新增页面时必须同时验证直达、刷新、前进后退和移动端底栏状态。
|
||||
4. 氢能页面属于严格原型复刻范围,CSS / DOM 调整必须做桌面与移动端截图对比验收。
|
||||
|
||||
## 14. 交接资料与权限清单
|
||||
|
||||
以下内容不应写入 Git,需要由原负责人通过安全渠道单独移交:
|
||||
|
||||
- Gitea 项目成员权限及分支保护规则;
|
||||
- Woodpecker 项目、流水线和 Secret 管理权限;
|
||||
- Harbor 项目及镜像拉取 / 推送账号;
|
||||
- Portainer / ECS / Docker 服务管理权限;
|
||||
- 主业务、氢能、里程和位置数据库的只读 / 读写账号;
|
||||
- OneOS 里程 API 地址、API Key、调用方白名单和联系人;
|
||||
- 羚牛业务系统认证接口联系人和 jumpToken 协议;
|
||||
- 高德地图 Key、安全码及域名白名单;
|
||||
- OSS Bucket、RAM 用户和目录权限;
|
||||
- 生产域名、反向代理、证书和 DNS 管理权限;
|
||||
- 数据口径负责人:资产、里程、调度、氢能、电能、ETC 各一名。
|
||||
|
||||
## 15. 接手验收清单
|
||||
|
||||
### 15.1 代码与环境
|
||||
|
||||
- [ ] 能拉取 `main` 并确认目标版本;
|
||||
- [ ] Node.js 22、`npm ci`、lint、test、build 均通过;
|
||||
- [ ] 已获得不含明文传播的本地 / 测试环境变量;
|
||||
- [ ] 本地前后端和免登录调试正常;
|
||||
- [ ] `.env`、数据库导出、截图和临时文件不会进入 Git。
|
||||
|
||||
### 15.2 认证与权限
|
||||
|
||||
- [ ] 从业务系统 jumpToken 跳转登录成功;
|
||||
- [ ] 普通、部门、全量三类数据权限分别验收;
|
||||
- [ ] 能源、智能调度、反馈管理员角色分别验收;
|
||||
- [ ] 无权限用户前端不可见且后端返回 403;
|
||||
- [ ] Token 过期后能正确退出并重新登录。
|
||||
|
||||
### 15.3 数据与功能
|
||||
|
||||
- [ ] 资产、里程、调度、氢能、电能、ETC 的主要 API 都能访问真实数据;
|
||||
- [ ] BI 汇总值与最小粒度明细守恒;
|
||||
- [ ] 氢能成本承担方、对客金额、成本金额和利润口径已由业务负责人确认;
|
||||
- [ ] 里程 OneOS API 的单日、区间和来源协议正常;
|
||||
- [ ] 车辆 / 加氢热力图可加载地图、点位和详情;
|
||||
- [ ] Excel / CSV 导入导出可用;
|
||||
- [ ] 用户反馈和截图上传可用;
|
||||
- [ ] 06:30 日报归档在测试环境完成一次验证。
|
||||
|
||||
### 15.4 发布与回滚
|
||||
|
||||
- [ ] Woodpecker 流水线通过;
|
||||
- [ ] Harbor 中存在对应版本镜像;
|
||||
- [ ] Portainer / Compose 使用目标镜像标签和正确 Secret;
|
||||
- [ ] 部署后验证健康检查、登录、API、Web 和移动端;
|
||||
- [ ] 记录上一稳定镜像标签、数据库变更和回滚步骤。
|
||||
|
||||
## 16. 维护原则
|
||||
|
||||
1. **先确认真实数据口径,再改页面**:经营指标必须能从汇总下钻到真实流水。
|
||||
2. **前端隐藏不代替后端鉴权**:任何管理或敏感能力都要有 API 守卫。
|
||||
3. **配置与凭据分离**:代码只保留变量名和安全默认行为,不保留真实密码。
|
||||
4. **原型页面以截图验收**:氢能模块必须对照原型验证布局、样式、交互、下钻层级和移动端。
|
||||
5. **变更必须可回滚**:发版保留旧镜像,数据库变更先备份、再迁移、再核对。
|
||||
6. **区分可达与可用**:健康检查正常不代表数据库、外部 API 和经营数据正常。
|
||||
@@ -0,0 +1,13 @@
|
||||
# 本地只读预览
|
||||
|
||||
使用 Node.js 22+ 和 npm,在本项目内执行 `npm ci`,避免依赖其他工作目录的 node_modules 软链接。
|
||||
|
||||
将 `.env.local.example` 复制为 `.env.local` 并填写连接配置,或通过进程环境变量提供配置。使用数据库侧只读账号;不要提交真实凭据。
|
||||
|
||||
执行 `npm run dev:local`,浏览器访问 http://127.0.0.1:8115/energy#hydrogen 。前后端固定绑定本机 8115 / 3001;端口占用时退出,不自动换端口。停止终端进程即可停止服务;此命令不配置开机自启。
|
||||
|
||||
本地入口关闭 mock、启用本地免登录,禁用后台建表与定时归档,并拒绝 API 的 POST / PUT / PATCH / DELETE。请勿把免登录预览暴露到公网。氢能查询启用只读 SQL 检查;这不能替代数据库账号的只读权限。
|
||||
|
||||
`npm run dev` / `npm start` 保留原部署方式;设置 `DB_READ_ONLY=1` 时同样不启动后台任务,且 API 禁止写入。生产登录流程需要写请求,因此不要把本地只读模式当作生产部署配置。
|
||||
|
||||
验证:`npm test`、`npm run lint`、`npm run build`;健康检查为 `GET http://127.0.0.1:3001/api/health`。健康检查成功仅代表进程可用,还需检查氢能 meta / overview 的真实数据响应。
|
||||
Vendored
BIN
Binary file not shown.
@@ -43,7 +43,7 @@ const mileagePool = mysql.createPool({
|
||||
host: '101.133.130.65',
|
||||
port: 3306,
|
||||
user: 'bi_reader_02',
|
||||
password: 'bi_reader_02_Pass',
|
||||
password: process.env.MILEAGE_DB_PASSWORD,
|
||||
database: 'hydrogen_energy',
|
||||
waitForConnections: true,
|
||||
connectionLimit: 5,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
### 数据库 2:hydrogen_energy(新增连接)
|
||||
|
||||
- 连接信息:`101.133.130.65:3306`,用户 `bi_reader_02`,密码 `bi_reader_02_Pass`,库名 `hydrogen_energy`
|
||||
- 连接信息:`101.133.130.65:3306`,用户 `bi_reader_02`,密码 `<见环境变量 MILEAGE_DB_PASSWORD>`,库名 `hydrogen_energy`
|
||||
- `v_vehicle_daily_stats` — 1004 辆车的每日里程明细(plate, vin, stat_date, daily_km, total_km, day_hydrogen, daily_run_secs, source)
|
||||
|
||||
## 架构
|
||||
|
||||
Reference in New Issue
Block a user