# 租户总后台（saasadminui + fs-admin-saas）登录与首页仪表盘 — 问题清单

> **范围**：`saasadminui` 登录链路、路由守卫、`/index` 首页仪表盘（`src/views/index.vue`）  
> **后端**：`fs-admin-saas:8004`（profile=`admin`），业务实现 `fs-service`  
> **排查日期**：2026-05-27  
> **说明**：本文档记录问题点与修复状态。  
> **修复批次**：2026-05-27（登录 + 首页 P0/P1 首批）

---

## 修复状态（2026-05-27）

| 编号 | 状态 |
|------|------|
| P0-2 getFirstLogin | ✅ 已在 `SysLoginController` 实现 |
| P0-3 dealerChart 初始化 | ✅ 已移除无效图表初始化 |
| P0-5 authorizationInfo | ✅ 已修正返回对象 |
| P0-6 trafficLog null | ✅ 已保证返回空 DTO |
| P0-8 登出 tenantCode | ✅ 已清理 Cookie |
| P1-4/5 路由守卫 | ✅ GetInfo/菜单失败跳转登录 |
| P1-6~8 首页展示 | ✅ 短信/流量/较昨日绑定数据 |
| P1-11 收款图表 option | ✅ 已修正 |
| P0-4 统计任务 NPE | ✅ `statis.config` 空 JSON 防护 |
| setPassword API | ✅ 改用 `/system/user/profile/updatePwd` |

---

## 一、链路总览

```mermaid
sequenceDiagram
  participant UI as saasadminui
  participant API as fs-admin-saas:8004
  participant Redis as Redis
  participant DB as 租户库/主库

  UI->>API: POST /login (tenantCode+账号密码)
  API->>DB: 主库查租户 + 切租户库认证
  API-->>UI: token
  UI->>API: GET /getInfo
  UI->>API: GET /getRouters (AdminMenu)
  UI->>UI: 进入 /index
  UI->>API: 并行 N 个 /index/statistics/*
  API->>Redis: 读统计缓存
  API-->>UI: 仪表盘数据
```

| 环节 | 前端 | 后端（fs-admin-saas） |
|------|------|------------------------|
| 登录 | `views/login.vue` → `api/login.js` | `SysLoginController`（@Profile admin） |
| 验证码 | `getCodeImg` | `CaptchaController` |
| 用户信息 | `store/user.js` GetInfo | `GET /getInfo` |
| 动态菜单 | `store/permission.js` | `GET /getRouters` → `IAdminMenuService` |
| 首页 | `views/index.vue` | `com.fs.api.controller.IndexStatisticsController` |
| 红包概览 | `api/company/redPacket.js` | `FsRedPacketController` `/his/redPacket/info` |

---

## 二、问题统计摘要

| 级别 | 数量 | 说明 |
|------|------|------|
| **P0** | 8 | 阻塞登录/首页白屏/核心数据全空 |
| **P1** | 12 | 功能错误、数据不准、易引发线上故障 |
| **P2** | 10 | 体验、规范、技术债 |
| **配置/环境** | 5 | 代理、Redis、定时任务、JDK |

---

## 三、P0 — 必须优先处理

### P0-1 前端代理曾指向错误服务（已局部修正，需验证）

| 项 | 内容 |
|----|------|
| **现象** | 开发环境请求打到 `fs-company:8006`，租户总后台接口 404 或行为与 company 端混淆 |
| **正确** | `saasadminui` → `fs-admin-saas:8004` |
| **相关文件** | `saasadminui/vue.config.js`、`saasadminui/nginx.conf` |
| **状态** | 已改为 8004；需在本地 `npm run dev` + 启动 8004 后回归验证 |
| **建议** | 生产 Nginx 同步检查，勿再指向 8006 |

### P0-2 `GET /getFirstLogin` 在 fs-admin-saas 上不存在（404）

| 项 | 内容 |
|----|------|
| **现象** | 登录成功后调用 `getFirstLogin` 失败；`login.vue` 的 `catch` 仍跳转首页，首次改密流程失效 |
| **原因** | `getFirstLogin` 仅在 `CompanyLoginController`（`@Profile("company")`）中实现，且该类在 `com.fs.company.controller.*` 被启动类 **排除扫描** |
| **活跃登录** | `SysLoginController`（admin）**无** `getFirstLogin` |
| **前端** | `api/login.js` → `GET /getFirstLogin`；`login.vue` `checkFirstLogin()` |
| **建议** | 在 `SysLoginController` 或独立 admin Controller 中实现租户总后台版「首次登录改密」（基于 `SysUser` + `his.login` 配置），或前端暂时关闭该流程 |

### P0-3 首页 ECharts 初始化访问已注释的 DOM（可能白屏/控制台报错）

| 项 | 内容 |
|----|------|
| **现象** | `mounted` 中仍调用 `initDealerChart()`、`handleDealerChartData()`，但模板中 `ref="dealerChart"` 的图表区域已被注释 |
| **文件** | `saasadminui/src/views/index.vue`（约 1027–1028、1451–1463 行 vs 412–426 行注释块） |
| **影响** | `echarts.init(undefined)` 抛错，可能中断同 `mounted` 内其它图表初始化 |
| **建议** | 删除或恢复对应 DOM；仅保留 `dealerChartNew` |

### P0-4 仪表盘数据强依赖 Redis 缓存，定时任务存在 NPE

| 项 | 内容 |
|----|------|
| **现象** | 企业数据、资产、分析概览等卡片长期为 0 |
| **后端** | `IndexStatisticsController` 多数接口从 Redis 读 `DATA_OVERVIEW_*` 等 key |
| **写入** | 依赖 `statisticsService.dataOverviewTask()` 等 Quartz 任务 |
| **日志** | `sys-error.log`：`StatisticsServiceImpl.rechargeConsumption` 中 `jsonObject` 为 null → 租户任务失败 |
| **其它** | `No bean named 'qwTask'` 等任务 Bean 缺失，可能影响调度器整体稳定性 |
| **建议** | 修复 `fs-service` 统计任务 NPE；确认 SaaS 租户任务分发器正常；缓存未命中时降级查库或返回明确提示 |

### P0-5 `authorizationInfo` 部门聚合分支返回错误对象（后端逻辑 Bug）

| 项 | 内容 |
|----|------|
| **文件** | `fs-admin-saas/.../api/controller/IndexStatisticsController.java` `authorizationInfo()` |
| **现象** | `else` 分支累加结果写入 `authorizationInfoDTO1`，但 `return` 的是未赋值的 `authorizationInfoDTO` |
| **影响** | 按部门筛选时「平台今日看课人数 / 配额上限」恒为空或 0 |
| **建议** | 返回 `authorizationInfoDTO1` 或统一赋值后返回 |

### P0-6 `trafficLog` 接口可能返回 `null` 响应体

| 项 | 内容 |
|----|------|
| **文件** | 同上 `getTrafficLog()` |
| **现象** | `result == null \|\| sysConfig == null` 时 `return null` |
| **影响** | 前端 `trafficLog().then` 可能异常；且 UI 上「剩余流量」为写死 `100.00GB`（见 P1-8），接口失败用户无感知 |
| **建议** | 统一 `R.ok().put("data", emptyDto)`；前端绑定真实字段 |

### P0-7 `X-Frontend-Type` 与后端 profile 曾不一致（已改 admin，需联调）

| 项 | 内容 |
|----|------|
| **原状** | 前端发 `company`，后端为 `admin` profile |
| **影响** | Token 无 `tenantId` 时，`tenant-code` 回退切库行为与菜单数据源（`AdminMenu` vs `CompanyMenu`）不一致 |
| **现状** | `request.js` 已改为 `admin`；登录必须带 `tenantCode` 以保证 `LoginUser.tenantId` |
| **建议** | 联调确认 `getRouters` 读的是租户库 `admin_menu` 数据 |

### P0-8 登出未清除 `tenantCode` Cookie

| 项 | 内容 |
|----|------|
| **文件** | `store/modules/user.js` `LogOut` / `FedLogOut` |
| **现象** | 仅 `removeToken()`，未 `Cookies.remove('tenantCode')` |
| **影响** | 换租户登录可能携带旧 `tenant-code` 请求头（与 body 不一致） |
| **建议** | 登出时同步清理 `tenantCode` |

---

## 四、P1 — 功能/数据错误

### P1-1 登录文案与校验矛盾

| 文件 | `login.vue` |
|------|-------------|
| **现象** | placeholder「企业编号（选填）」 vs `loginRules.tenantCode` **required** |
| **建议** | 租户总后台应必填；改文案为「请输入企业编号」 |

### P1-2 短信二次验证未实现

| 文件 | `login.vue` `handleLogin` |
|------|---------------------------|
| **现象** | `needSms: true` 时仅提示「验证组件未启用」 |
| **后端** | `checkIsNeedCheck` 在 admin 端存在，但前端无弹窗流程 |

### P1-3 `checkIsNeedCheck` 返回类型与拦截器

| 项 | 内容 |
|----|------|
| **后端** | 返回原始 `boolean` |
| **前端** | `if (!resp)` 依赖拦截器返回体为布尔值；若将来改为 `AjaxResult` 会失效 |
| **建议** | 约定统一 `AjaxResult` 或文档化 |

### P1-4 路由守卫失败后的跳转逻辑

| 文件 | `permission.js` |
|------|-----------------|
| **现象** | `GetInfo` 失败 → `LogOut` → `next({ path: '/' })`，仍可能再次触发 GetInfo |
| **建议** | 失败应 `next('/login')` 并带 redirect |

### P1-5 动态路由 `getRouters` 无错误处理

| 文件 | `store/modules/permission.js` |
|------|-------------------------------|
| **现象** | `getRouters()` 无 `.catch`，菜单接口失败时 Promise 悬挂，页面卡在 NProgress |
| **建议** | 增加 catch + 提示 |

### P1-6 首页短信余额未绑定接口数据

| 文件 | `index.vue` 约 224–226 行 |
|------|---------------------------|
| **现象** | 已调用 `smsBalance()` 赋值 `smsRemainCount`，模板写死 `0` |
| **建议** | 使用 `{{ smsRemainCount }}` |

### P1-7 资产「较昨日」写死

| 文件 | `index.vue` 约 78–79 行 |
|------|---------------------------|
| **现象** | 显示「较昨日 +1」，未使用 `yesterdayComsumption` |
| **建议** | 计算真实环比 |

### P1-8 流量卡片 UI 写死

| 文件 | `index.vue` 约 197–204 行 |
|------|---------------------------|
| **现象** | 「剩余流量 100.00GB」、进度 90% 硬编码；仅「今日/本月消耗」用接口 |
| **建议** | 对接 `trafficLog` 的 `traffic` 等字段 |

### P1-9 看课配额进度条分子分母相同

| 文件 | `index.vue` 约 159–163 行 |
|------|---------------------------|
| **现象** | 配额展示 `todayWatchUserCount / versionLimit`，但分子也绑定了 `todayWatchUserCount`（应为当前使用 vs 上限） |
| **建议** | 核对产品设计后改绑定字段 |

### P1-10 `thisMonthOrderCount` / `thisMonthRecvCount` 返回结构不统一

| 项 | 内容 |
|----|------|
| **后端** | 默认分支：`return redisCache.getCacheObject(...)` 可能为顶层含 `dates` 的 `R`；部门聚合：`R.ok().put("dates",...)` |
| **前端** | 使用 `res.dates`、`res.orderCount`（非 `res.data.dates`） |
| **风险** | Redis 存的对象结构变化会导致图表无数据 |
| **建议** | 统一 `R.ok().put("data", chartDto)`；前端统一读 `res.data` |

### P1-11 `initThisMonthRecvChart` 使用了错误 option

| 文件 | `index.vue` `initThisMonthRecvChart` |
|------|--------------------------------------|
| **现象** | `setOption(thisMonthOrderCountOption)` 应为 `thisMonthRecvCountOption` |
| **影响** | 「本月收款数」图表轴/系列配置错误 |

### P1-12 红包接口遍历全量公司

| 文件 | `FsRedPacketController.info()` |
|------|--------------------------------|
| **现象** | `companyService.selectCompanyList(new Company())` 无租户/权限过滤 |
| **影响** | 租户总后台可能汇总到越权数据或性能差 |
| **建议** | 按当前租户/数据权限过滤；逻辑下沉 `fs-service` |

---

## 五、P2 — 体验与规范

### P2-1 `$runtimeConfig` 初始化时机

| 项 | 内容 |
|----|------|
| **现象** | `index.vue` `data()` 使用 `this.$runtimeConfig.VUE_APP_COURSE_DEFAULT`，登录前为 `{}`，默认会员/企微可能不准 |
| **依赖** | 登录后 `reloadRuntimeConfig()` 读 `his.adminUi.config` |
| **建议** | 进入首页前 await 配置加载，或 GetInfo 后刷新 |

### P2-2 头像 URL 拼接

| 文件 | `store/modules/user.js` |
|------|-------------------------|
| **现象** | `VUE_APP_BASE_API + user.avatar`，若 avatar 已是完整 URL 会重复 |
| **建议** | 判断 `http` 前缀 |

### P2-3 请求超时 20 分钟

| 文件 | `utils/request.js` `timeout: 1200000` |
|------|----------------------------------------|
| **建议** | 登录/统计接口单独合理超时（如 30s） |

### P2-4 环境变量命名误导

| 文件 | `.env.development` 中 `ENV = 'production'` |
|------|---------------------------------------------|

### P2-5 缺少 `.catch` 的仪表盘并行请求

| 文件 | `index.vue` `refresh()` |
|------|-------------------------|
| **现象** | 多个 API 无 catch，单个 500 仅控制台/拦截器弹窗，卡片静默为 0 |
| **建议** | 统一错误态或骨架屏 |

### P2-6 重复/死代码

| 项 | 内容 |
|----|------|
| `initDealerChart` / `handleDealerChartData` | 对应 UI 已注释 |
| `api/tenant.js` vs `api/tenant/tenant.js` | 路径重复（非本页核心） |

### P2-7 后端双份 `IndexStatisticsController`

| 路径 | Profile | 是否加载 |
|------|---------|----------|
| `com.fs.api.controller.IndexStatisticsController` | **admin** | ✅ fs-admin-saas 使用 |
| `com.fs.company.controller.company.IndexStatisticsController` | company | ❌ 排除扫描 |

**建议**：避免误改 company 包下文件；文档标明以 `api` 包为准。

### P2-8 拼写与命名技术债

| 位置 | 说明 |
|------|------|
| `rechargeComsumption` | 应为 Consumption |
| `dealderCount` | 应为 dealer |
| 前后端需保持一致以免改 URL |

### P2-9 注释与 JavaDoc 不足

| 建议 |
|------|
| `IndexStatisticsController` 各接口补充：Redis key、租户范围、依赖定时任务 |
| `SysLoginController` 注明租户总后台登录与 company 端差异 |

### P2-10 JDK 版本

| 项 | 内容 |
|----|------|
| **目标** | JDK 17 |
| **现状** | 父 POM `java.version=1.8` |
| **建议** | 功能稳定后单独立项升级 |

---

## 六、配置与环境依赖

| 编号 | 检查项 | 说明 |
|------|--------|------|
| E-1 | `fs-admin-saas` 端口 **8004** 已启动 | profile `dev,admin` |
| E-2 | Redis 可连 | 统计接口几乎全依赖缓存 |
| E-3 | MySQL 主库 + 租户库 | 登录 `tenantCode` 切库 |
| E-4 | `tenant_info` 中租户 status=1 | 否则「企业已禁用」 |
| E-5 | Quartz / `statisticsService.dataOverviewTask` | 缓存写入；需先修 NPE |

---

## 七、首页 API 对照表（前端 → 后端）

| # | 前端方法 | URL | 后端类 | 主要风险 |
|---|----------|-----|--------|----------|
| 1 | `login` | POST `/login` | `SysLoginController` | 租户码错误 |
| 2 | `getCodeImg` | GET `/captchaImage` | `CaptchaController` | 403 若未放行 |
| 3 | `checkIsNeedCheck` | POST `/checkIsNeedCheck` | `SysLoginController` | 返回格式 |
| 4 | `getFirstLogin` | GET `/getFirstLogin` | **缺失** | **404** |
| 5 | `getInfo` | GET `/getInfo` | `SysLoginController` | 租户库用户/角色 |
| 6 | `getRouters` | GET `/getRouters` | `SysLoginController` | AdminMenu 数据 |
| 7 | `getConfigByKey` | GET `/system/config/getConfigByKey/his.adminUi.config` | SysConfig | 运行时配置 |
| 8 | `rechargeComsumption` | GET `/index/statistics/rechargeComsumption` | IndexStatisticsController | Redis 空 |
| 9 | `redPacketInfo` | GET `/his/redPacket/info` | FsRedPacketController | 全表公司汇总 |
| 10 | `trafficLog` | GET `/index/statistics/trafficLog` | IndexStatisticsController | **可 null** |
| 11 | `dealerAggregated` | GET `/index/statistics/dealerAggregated` | 同上 | Redis 空 |
| 12 | `analysisPreview` | POST `/index/statistics/analysisPreview` | 同上 | Redis 空 |
| 13 | `smsBalance` | GET `/index/statistics/smsBalance` | 同上 | 前端未展示 |
| 14 | `authorizationInfo` | GET `/index/statistics/authorizationInfo` | 同上 | **返回错对象** |
| 15 | `watchEndPlayTrend` | POST `.../watchEndPlayTrend` | 同上 | Redis 空 |
| 16 | `deaMemberTopTen` | POST `.../deaMemberTopTen` | 同上 | ref 已删 |
| 17 | `watchCourseTopTen` | POST `.../watchCourseTopTen` | 同上 | — |
| 18 | `getWatchCourseStatisticsData` | POST `.../getWatchCourseStatisticsData` | 同上 | 实时查库 |
| 19 | `rewardMoneyTopTen` | POST `.../rewardMoneyTopTen` | 同上 | — |
| 20 | `rewardMoneyTrend` | POST `.../rewardMoneyTrend` | 同上 | — |
| 21 | `thisMonthOrderCount` | GET `.../thisMonthOrderCount` | 同上 | 返回结构 |
| 22 | `thisMonthRecvCount` | GET `.../thisMonthRecvCount` | 同上 | option 用错 |

---

## 八、建议修复顺序（供下一阶段开发）

1. **环境**：确认 8004 + Redis + 修复统计定时任务 NPE  
2. **登录**：补 `getFirstLogin`（admin）、登出清 Cookie、路由失败跳登录页  
3. **首页 P0**：修 `dealerChart` 初始化、修 `authorizationInfo` return、修 `trafficLog` null  
4. **首页 P1**：短信/流量/较昨日绑定真实数据、修收款图表 option  
5. **数据**：缓存未命中降级 + 前端 loading/错误态  
6. **规范**：接口注释、统一 R 返回结构、逻辑收拢 `fs-service`  

---

## 九、验证清单（修复后自检）

- [ ] 正确 `tenantCode` + 账号密码可登录，验证码开关开/关均正常  
- [ ] 登录后侧边栏菜单正常，刷新页面不掉线  
- [ ] 首页无控制台 ECharts 报错  
- [ ] 企业数据/资产/经营数据非全 0（或明确提示「统计任务未跑」）  
- [ ] 分析概览切换「今日/昨日/本周」有变化  
- [ ] 本月订单/收款图表有曲线  
- [ ] 换租户登录后数据隔离正确  
- [ ] 登出再登录另一租户，请求头 `tenant-code` 正确  

---

*文档维护：随修复进展更新对应条目的「状态」列。*
