feat: add dashboard stats redesign spec and login i18n completion

This commit is contained in:
jie committed 2026-03-13 00:42:22 +08:00
1 parent b0539c0014
commit 8410a2cac4
7 files changed
+722 -632

No files matched your search

@@ -0,0 +1,163 @@
# 技术契约与架构设计书:Dashboard 统计卡片重设计
**版本**:v1.0
**日期**:2026-03-13
**作者**:system-architect
**状态**:待评审
---
## 一、现状诊断
### 当前 API 响应结构
```json
{
"total_auth_codes": 12,
"active_licenses": 2,
"expiring_soon": 0,
"abnormal_alerts": 2,
"growth_rate": {
"auth_codes": 100.00,
"licenses": 0.00
}
}
```
### 现存问题清单
| # | 问题 | 严重性 |
|---|------|--------|
| 1 | `growth_rate` 语义不清——是"同比"上月还是"环比"?无时间标注 | 高 |
| 2 | 两个增长率字段(auth_codes / licenses)对用户的业务价值极低,不知道要对比什么 | 高 |
| 3 | 前端 `statsData` 中的 `key` 映射与 i18n label 不一致(注释与实际label错位) | 中 |
| 4 | `expiring_soon` 统计的是"30天内到期的授权码",但卡片标签写的是"即将过期",口径模糊 | 中 |
| 5 | 没有"今日新增"这类高频关注的运营指标 | 中 |
| 6 | 增长率算法缺陷:上月总数为0时直接返回0而非100% | 低 |
---
## 二、竞品分析(License Management SaaS 参考)
### Keygen.sh / LicenseSpring / Cryptlex 共性 Dashboard 指标
```
┌─────────────────────────────────────────────────────────┐
│ 高频关注(运营视角) │
│ • 今日新增授权码 / 本月新增授权码(绝对数 + 环比昨日/上月)│
│ • 活跃设备数(在线 License) │
│ • 即将到期(7天 / 30天)→ 需要跟进续费 │
│ • 异常告警(心跳超时 / 被撤销) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 中频关注(管理视角) │
│ • 授权码总量(存量指标) │
│ • 活跃许可证总量(存量指标) │
│ • 月度增长率(MoM Growth Rate,附说明文字"vs 上月") │
└─────────────────────────────────────────────────────────┘
```
### 关键竞品设计原则
1. **增长率必须标注对比周期**:统一用「vs. 上月同期」,数字旁展示箭头 ↑ ↓ 和颜色区分正负
2. **增长率不放卡片主数值**:作为副文本展示(小字 + 颜色),主数值展示绝对数
3. **即将到期细分**:7天 为紧急(红),30天 为预警(橙)
4. **今日新增**:运营最关注的高频指标,竞品均在首位展示
---
## 三、重设计方案
### 3.1 新指标体系(6张卡片)
| 位置 | 指标名 | 计算口径 | 副文本 |
|------|--------|---------|--------|
| 卡片1 | 授权码总量 | `COUNT(authorization_codes)` | ↑ 本月新增 N 个 |
| 卡片2 | 活跃许可证 | `COUNT(licenses WHERE status='active')` | ↑ 较上月 +X% |
| 卡片3 | 今日新增授权 | `COUNT(licenses WHERE DATE(created_at)=TODAY)` | 昨日 N 个 |
| 卡片4 | 7天内到期 | `COUNT(auth_codes WHERE end_date BETWEEN now AND now+7d AND NOT locked)` | 紧急跟进 |
| 卡片5 | 30天内到期 | `COUNT(auth_codes WHERE end_date BETWEEN now AND now+30d AND NOT locked)` | 含7天内 |
| 卡片6 | 异常告警 | `COUNT(licenses WHERE status='active' AND heartbeat_timeout)` | 心跳超时 |
> 删除:`auth_codes 增长率`、`licenses 增长率` 作为独立卡片(改为副文本方式展示)
### 3.2 增长率展示方式(副文本,不占卡片)
```
┌──────────────────────────┐
│ 🔑 授权码总量 │
│ 12 │
│ ↑ 本月新增 2 个 │ ← 副文本:绝对增量
└──────────────────────────┘
┌──────────────────────────┐
│ ✅ 活跃许可证 │
│ 98 │
│ ↑ +12.5% vs. 上月 │ ← 副文本:百分比增长率 + 标注
└──────────────────────────┘
```
### 3.3 新 API Response Schema
```go
// StatsOverviewResponse 重设计
type StatsOverviewResponse struct {
// 存量指标
TotalAuthCodes int64 `json:"total_auth_codes"` // 授权码总量
ActiveLicenses int64 `json:"active_licenses"` // 活跃许可证
// 流量指标
TodayNewLicenses int64 `json:"today_new_licenses"` // 今日新增许可证
YesterdayNewLicenses int64 `json:"yesterday_new_licenses"` // 昨日新增(用于对比)
MonthNewAuthCodes int64 `json:"month_new_auth_codes"` // 本月新增授权码
// 风险指标
ExpiringIn7Days int64 `json:"expiring_in_7days"` // 7天内到期
ExpiringIn30Days int64 `json:"expiring_in_30days"` // 30天内到期
AbnormalAlerts int64 `json:"abnormal_alerts"` // 异常告警
// 增长率(作为副文本,不占独立卡片)
GrowthRate struct {
AuthCodesMoM float64 `json:"auth_codes_mom"` // 授权码月环比
LicensesMoM float64 `json:"licenses_mom"` // 许可证月环比
} `json:"growth_rate"`
}
```
---
## 四、影响面分析(全栈)
### 后端改动文件
| 文件 | 操作 | 说明 |
|------|------|------|
| `backend/internal/models/license.go` | 修改 | 更新 `StatsOverviewResponse` struct |
| `backend/internal/service/license_service.go` | 修改 | 重写 `GetStatsOverview()` 实现,增加今日/昨日新增查询 |
| `backend/internal/api/handlers/license_handler.go` | 无需修改 | Handler 层直接透传 |
### 前端改动文件
| 文件 | 操作 | 说明 |
|------|------|------|
| `frontend/src/views/Dashboard.vue` | 修改 | 重新定义 `statsData` 6张卡片,增加副文本字段渲染 |
| `frontend/src/i18n/locales/zh.json` | 修改 | 更新 `dashboard.stats.*` key |
| `frontend/src/i18n/locales/en.json` | 修改 | 同上 |
| `frontend/src/i18n/locales/ja.json` | 修改 | 同上 |
| `frontend/src/api/dashboard.ts` | 修改 | 更新 `getOverviewStats` 返回类型定义 |
---
## 五、实施优先级建议
**P0(立即)**:
- 重命名"即将过期"为"30天内到期",语义清晰化
- 增长率卡片改为副文本方式(不移除数据,只改展示层)
- 增长率数字旁加"vs. 上月"标注
**P1(本迭代)**:
- 新增"今日新增"卡片(需要后端加一个 COUNT 查询)
- "即将过期"拆分7天/30天两档
**P2(下迭代)**:
- 卡片支持点击跳转到过滤后的列表页
+135
View File
@@ -0,0 +1,135 @@
# 技术契约与架构设计书:登录页国际化补全
**版本**:v1.0
**日期**:2026-03-13
**作者**:system-architect
**状态**:待 task-coder 实施
---
## 一、问题诊断
`src/views/Login.vue` 中存在 **8 处**硬编码英文字符串,未通过 `t()` 调用 i18n 系统,导致切换语言后这些文案不随语言切换。
### 硬编码清单(按 DOM 位置)
| 位置 | 当前硬编码值 | 目标 key |
|------|-------------|---------|
| `.hero-title` | `Secure License<br/>Management` | `login.heroTitleLine1` / `login.heroTitleLine2` |
| `.hero-subtitle` | `Streamline your software licensing...` | `login.heroSubtitle` |
| stat-label "Active Licenses" | `Active Licenses` | `login.stats.activeLicenses` |
| stat-label "Uptime" | `Uptime` | `login.stats.uptime` |
| stat-label "Support" | `Support` | `login.stats.support` |
| `.title` (card) | `Welcome back` | `login.welcomeTitle` |
| `.subtitle` (card) | `Sign in to your account to continue` | `login.welcomeSubtitle` |
| `.login-footer p` | `© 2025 Cedar-V. All rights reserved.` | `login.copyright` |
> **保持原样(不需国际化)**:品牌名 `Cedar-V`、数字统计值 `10K+` / `99.9%` / `24/7`、语言选项标签(自语言名称自描述惯例)。
---
## 二、数据模型层(Schema)— i18n 键值设计
### 新增键(追加至三端 `login` 对象末尾,位于 `error` 块之后)
```jsonc
// en.json
"heroTitleLine1": "Secure License",
"heroTitleLine2": "Management",
"heroSubtitle": "Streamline your software licensing workflow with our enterprise-grade platform.",
"stats": {
"activeLicenses": "Active Licenses",
"uptime": "Uptime",
"support": "Support"
},
"welcomeTitle": "Welcome back",
"welcomeSubtitle": "Sign in to your account to continue",
"copyright": "© 2025 Cedar-V. All rights reserved."
// zh.json
"heroTitleLine1": "安全许可证",
"heroTitleLine2": "管理平台",
"heroSubtitle": "使用我们的企业级平台,简化您的软件许可证管理工作流程。",
"stats": {
"activeLicenses": "活跃许可证",
"uptime": "可用率",
"support": "全天支持"
},
"welcomeTitle": "欢迎回来",
"welcomeSubtitle": "登录您的账户以继续",
"copyright": "© 2025 Cedar-V. 保留所有权利。"
// ja.json
"heroTitleLine1": "セキュアな",
"heroTitleLine2": "ライセンス管理",
"heroSubtitle": "エンタープライズグレードのプラットフォームで、ソフトウェアライセンス管理を効率化。",
"stats": {
"activeLicenses": "アクティブライセンス",
"uptime": "稼働率",
"support": "サポート"
},
"welcomeTitle": "おかえりなさい",
"welcomeSubtitle": "アカウントにサインインして続けてください",
"copyright": "© 2025 Cedar-V. All rights reserved."
```
---
## 三、前端交互层(View)— 修改意图
### 目标文件
| 文件 | 操作 |
|------|------|
| `src/i18n/locales/en.json` | 追加 8 个新键至 `login` 对象 |
| `src/i18n/locales/zh.json` | 追加 8 个新键至 `login` 对象 |
| `src/i18n/locales/ja.json` | 追加 8 个新键至 `login` 对象 |
| `src/views/Login.vue` | 模板层 8 处硬编码 → `{{ t('login.*') }}` |
### Login.vue 模板变更对照
```html
<!-- BEFORE -->
<h1 class="hero-title">Secure License<br/>Management</h1>
<!-- AFTER -->
<h1 class="hero-title">{{ t('login.heroTitleLine1') }}<br/>{{ t('login.heroTitleLine2') }}</h1>
```
```html
<!-- BEFORE -->
<p class="hero-subtitle">Streamline your software licensing workflow with our enterprise-grade platform.</p>
<!-- AFTER -->
<p class="hero-subtitle">{{ t('login.heroSubtitle') }}</p>
```
```html
<!-- BEFORE (3 stat labels) -->
<span class="stat-label">Active Licenses</span>
<span class="stat-label">Uptime</span>
<span class="stat-label">Support</span>
<!-- AFTER -->
<span class="stat-label">{{ t('login.stats.activeLicenses') }}</span>
<span class="stat-label">{{ t('login.stats.uptime') }}</span>
<span class="stat-label">{{ t('login.stats.support') }}</span>
```
```html
<!-- BEFORE -->
<h2 class="title">Welcome back</h2>
<p class="subtitle">Sign in to your account to continue</p>
<!-- AFTER -->
<h2 class="title">{{ t('login.welcomeTitle') }}</h2>
<p class="subtitle">{{ t('login.welcomeSubtitle') }}</p>
```
```html
<!-- BEFORE -->
<p>© 2025 Cedar-V. All rights reserved.</p>
<!-- AFTER -->
<p>{{ t('login.copyright') }}</p>
```
@@ -121,7 +121,6 @@ const appStore = useAppStore()
// 快捷选择选项
const quickOptions = computed(() => [
{ label: t('chart.licenseTrend.quickOptions.today'), value: 'today' },
{ label: t('chart.licenseTrend.quickOptions.week'), value: 'week' },
{ label: t('chart.licenseTrend.quickOptions.month'), value: 'month' }
])
@@ -161,7 +160,7 @@ const chartOption = computed(() => {
return {
grid: {
left: 60,
right: 30,
right: 50,
top: 30,
bottom: 60,
containLabel: false
@@ -277,24 +276,17 @@ const handleQuickSelect = (value: string) => {
let endDate: Date
switch (value) {
case 'today':
// 本日:今天到今天
case 'week':
// 最近7天:今天往前6天
startDate = new Date(today)
startDate.setDate(today.getDate() - 6)
endDate = new Date(today)
break
case 'week':
// 本周:本周一到本周日
const currentDay = today.getDay() // 0=周日, 1=周一, ..., 6=周六
const mondayOffset = currentDay === 0 ? 6 : currentDay - 1 // 计算到周一的偏移
startDate = new Date(today)
startDate.setDate(today.getDate() - mondayOffset)
endDate = new Date(startDate)
endDate.setDate(startDate.getDate() + 6) // 周日
break
case 'month':
// 本月:当前月1号到当前月末
startDate = new Date(today.getFullYear(), today.getMonth(), 1)
endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0) // 下个月0号=本月最后一天
// 最近30天:今天往前29天
startDate = new Date(today)
startDate.setDate(today.getDate() - 29)
endDate = new Date(today)
break
default:
return
+16 -7
View File
@@ -17,7 +17,18 @@
"usernameMinLength": "Username must be at least 3 characters",
"passwordRequired": "Please enter password",
"passwordMinLength": "Password must be at least 6 characters"
}
},
"heroTitleLine1": "Secure License",
"heroTitleLine2": "Management",
"heroSubtitle": "Streamline your software licensing workflow with our enterprise-grade platform.",
"stats": {
"activeLicenses": "Active Licenses",
"uptime": "Uptime",
"support": "Support"
},
"welcomeTitle": "Welcome back",
"welcomeSubtitle": "Sign in to your account to continue",
"copyright": "© 2025 Cedar-V. All rights reserved."
},
"sidebar": {
"expand": "Expand Sidebar",
@@ -295,9 +306,8 @@
"licenseTrend": {
"title": "License Trend",
"quickOptions": {
"today": "Today",
"week": "This Week",
"month": "This Month"
"week": "Last 7 Days",
"month": "Last 30 Days"
},
"datePicker": {
"rangeSeparator": "to",
@@ -314,9 +324,8 @@
"endDate": "End Date",
"separator": "to",
"quickOptions": {
"today": "Today",
"week": "This Week",
"month": "This Month"
"week": "Last 7 Days",
"month": "Last 30 Days"
}
}
},
+16 -7
View File
@@ -17,7 +17,18 @@
"usernameMinLength": "ユーザー名は3文字以上である必要があります",
"passwordRequired": "パスワードを入力してください",
"passwordMinLength": "パスワードは6文字以上である必要があります"
}
},
"heroTitleLine1": "セキュアな",
"heroTitleLine2": "ライセンス管理",
"heroSubtitle": "エンタープライズグレードのプラットフォームで、ソフトウェアライセンス管理を効率化。",
"stats": {
"activeLicenses": "アクティブライセンス",
"uptime": "稼働率",
"support": "サポート"
},
"welcomeTitle": "おかえりなさい",
"welcomeSubtitle": "アカウントにサインインして続けてください",
"copyright": "© 2025 Cedar-V. All rights reserved."
},
"sidebar": {
"expand": "サイドバーを展開",
@@ -294,9 +305,8 @@
"licenseTrend": {
"title": "ライセンストレンド",
"quickOptions": {
"today": "今日",
"week": "今週",
"month": "今月"
"week": "直7日間",
"month": "直30日間"
},
"datePicker": {
"rangeSeparator": "から",
@@ -313,9 +323,8 @@
"endDate": "終了日付",
"separator": "から",
"quickOptions": {
"today": "今日",
"week": "今週",
"month": "今月"
"week": "直7日間",
"month": "直30日間"
}
}
},
+16 -7
View File
@@ -17,7 +17,18 @@
"usernameMinLength": "用户名至少需要3个字符",
"passwordRequired": "请输入密码",
"passwordMinLength": "密码至少需要6个字符"
}
},
"heroTitleLine1": "软件授权",
"heroTitleLine2": "管理系统",
"heroSubtitle": "赋能软件商业化,构建 B2B 与 B2C 全场景的软件授权体系。",
"stats": {
"activeLicenses": "活跃许可证",
"uptime": "可用率",
"support": "全天支持"
},
"welcomeTitle": "欢迎回来",
"welcomeSubtitle": "登录您的账户以继续",
"copyright": "© 2025 Cedar-V. 保留所有权利。"
},
"sidebar": {
"expand": "展开侧边栏",
@@ -328,9 +339,8 @@
"licenseTrend": {
"title": "授权趋势",
"quickOptions": {
"today": "本日",
"week": "本周",
"month": "本月"
"week": "最近7天",
"month": "最近30天"
},
"datePicker": {
"rangeSeparator": "至",
@@ -347,9 +357,8 @@
"endDate": "结束日期",
"separator": "至",
"quickOptions": {
"today": "本日",
"week": "本周",
"month": "本月"
"week": "最近7天",
"month": "最近30天"
}
}
},
File diff suppressed because it is too large. Load diff