Skip to content

feat(tokenstats): add monthly/yearly activity views and re-anchor ranges to today - #1110

Open
3316891527 wants to merge 5 commits into
AAswordman:devfrom
3316891527:feat/tokenstats-period-refresh
Open

feat(tokenstats): add monthly/yearly activity views and re-anchor ranges to today#1110
3316891527 wants to merge 5 commits into
AAswordman:devfrom
3316891527:feat/tokenstats-period-refresh

Conversation

@3316891527

@3316891527 3316891527 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

变更说明 / Description

Token 统计页的“活跃记录”此前只有每日/每周/累计三档,且日期范围固定在上次手动选择的日期——想看当天数据必须打开日期面板手动点选。本 PR 把模式扩展为五档(每日/每周/每月/每年/累计),点击任一模式自动刷新到最近一个完整周期,并按模式展示对应粒度的柱状图;原方格热力图独立为“活跃日历”卡片。

背景与动机 / Context and motivation

  • 用户想统计“今天”时,需要手动把日期面板的开始/结束都点到今天,操作繁琐;
  • 缺少按月、按年查看活跃度的入口;
  • 柱状图粒度应与所选周期匹配(日→小时、周→天、月→周、年→月),这是常见的粒度降级设计;
  • 原热力图与“每日”模式绑定,模式细化后需要给它一个独立、长期有效的呈现位置。

改动范围 / Changes

  • 选择器由三段扩展为五段:每日、每周、每月、每年、累计(累计保持最后一格);
  • 点击任一模式时,将范围刷新为“最近一个完整周期”(本地时区,未来时间未发生故不统计):
    • 每日:今天 ~ 今天 → 24 根小时柱
    • 每周:上周同日 ~ 昨天(对齐今天的星期几,整 7 天)→ 7 根日柱
    • 每月:上月同日 ~ 昨天(minusMonths 自动钳制月末)→ 按周分桶,2 月平年 28 天为 4 根,其余 5 根
    • 每年:去年本月 ~ 今年上月(12 个完整自然月)→ 12 根月柱
    • 累计:首次使用日 ~ 今天 → 折线(不变)
  • TokenActivityAggregator 新增小时分桶(仅短范围计算)与月/年分桶,复用现有分位数等级与柱高逻辑;周分桶改为从范围起点对齐的滚动 7 天分桶;
  • 数据层新增一条只读小时聚合查询 getActivityHoursInRangestrftime('%Y-%m-%d %H') 分组),不涉及任何表结构/索引/迁移变更
  • 柱状图月份标签在密度高时自动加宽柱间距(按标签文字宽度计算),保证每年 12 根柱下方每个月的标签完整显示、互不重叠;
  • 原方格热力图从“每日”模式中拆出,独立为“活跃日历”卡,固定展示最近一年(独立查询),保留点击查看当天详情与色阶图例;
  • 日期面板直接读取当前范围状态,点击模式后再打开面板会正确定位到新模式范围,面板本身无需改动;
  • 新增“每月/每年/活跃日历/小时详情”文案(8 种语言:中/英/西/印尼/韩/马来/葡/罗);
  • 测试:更新每周范围用例;新增月末钳制、闰年 2月29日、跨年闰日钳制、月/年聚合分组、小时分桶共 9 个用例;
  • 不包含:“本月”“今年”语义、范围持久化策略变更、图表交互变更。

兼容性与风险 / Compatibility and risks

  • 行为变化:每周从“周日对齐的自然周”改为“对齐今天星期几的最近 7 天”;每月从“上一个自然月”改为与每周同构的滚动月窗口;点击任一模式会重置范围为该模式的最近完整周期;
  • 存储的视图模式枚举新增两个值;旧版本读取未知枚举值时回退为默认“每日”(valueOf 失败回退 DAILY),不会崩溃;
  • 日期边界全部由 java.time 计算(minusDays/minusMonths/YearMonth.atEndOfMonth),无硬编码月长/闰年规则,也不会展示未来日期;
  • 数据库仅新增一条只读统计查询,表结构、索引、Room 版本与迁移均无变化,新旧版本数据完全兼容;
  • 活跃日历卡增加一次独立查询(最近一年),其余查询数量不变。

关联 Issue / Related issue

N/A —— 该改动源自用户对 Token 统计页使用体验的反馈,无对应 Issue。

验证方式 / Verification

检查或命令:
- git diff --check
- Android Build(assembleDebug)工作流
- 单元测试:TokenStatsActivityRangePolicyTest(更新 + 新增 7 例)、TokenActivityAggregatorTest(新增 3 例)

环境与变体:
- 本地 Ubuntu 工作区(无可用 Android SDK/NDK,未做本地 Gradle 编译与本地 JVM 测试)
- GitHub Actions Android Build:早期版本 run 33944877588 success;最新提交对应的 run 33949602729 已触发,最终以 PR 页面为准
- 真机功能验证由 fork 作者在 Android Debug 构建上完成

结果:
- git diff --check:通过
- 真机:五档切换、点击刷新范围、各粒度柱状图、每年 12 个月标签不重叠、活跃日历卡、日期面板定位均符合预期

证据 / Evidence

• Android Build 工作流(最新提交):https://github.com/3316891527/Operit/actions/runs/33949602729
• 源分支:3316891527:feat/tokenstats-period-refresh

@CATMIAOZHI
CATMIAOZHI self-requested a review September 7, 2026 00:27

@CATMIAOZHI CATMIAOZHI left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCK:2 项 P2,触发条件、后果和建议见行内评论。

审计提交:6db9aa2523365f818bdcb6d50255504da11946b4,目标分支 dev。已先请求 CATMIAOZHI review,再完成独立工作树全 diff、运行调用链和独立 agent 复核;git diff --check 通过。

验证范围:上游仓库该 HEAD 的 check-runs 为 0,审计前无 review 线程或普通评论。已核对工作流内容:PR workflow 仅匹配 development,本 PR 目标为 dev;构建/单测 push workflow 匹配 main。已核对 Actions Variables(GITEE_OWNER、GITEE_REPO)。已核实作者 fork 的 Android Build 对应本次 HEAD 且成功;步骤只有构建,没有 JVM 单测执行步骤,因此不将其视为单测通过。遵守目标仓库 AGENTS.md,本次未执行本地编译、测试、Lint 或真机验证,未修改源码。


// Hourly buckets are only meaningful for single-day views (24 bars). Keep the
// range short to avoid wasteful computation for weekly/monthly/yearly ranges.
val hourly = if (dayCount <= 2 && hourTotals.isNotEmpty()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 每日图表需要覆盖实际允许的多日日期范围

首次进入时 loadInternal 默认加载 DAILY,但 defaultDateRange 仍为最近 30 天;这里对超过两天的范围返回空 hourly,而 DAILY 的 TokenActivityHourlyChart 无条件读取 data.hourly。因此即使有使用记录,首次进入也只看到空图。日期选择器在 DAILY 模式选择三天以上也能稳定触发,saveCustomRange 不会改变模式。请协调默认范围、模式和自定义日期的渲染规则,保证所有可选范围都产生可显示的数据,不能只修首次默认日期。

val rangeEnd = data.daily.lastOrNull()?.date
val points = data.monthly.map { month ->
TokenActivitySeriesPoint(
startDate = month.startDate,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 首月详情的开始日期应截到实际查询范围

例如 2026-09-07 点击“每年”,实际范围是 2025-09-07 至 2026-09-06。monthly 聚合按自然月分桶,首柱仅统计 09-07 至 09-30,但 month.startDate 固定为 09-01,这里的详情却显示 09-01 至 09-30 的用量,包含了实际上未查询的前六天。请像下面 TokenActivityYearlyChart 一样,使用 data.daily.first().date 截断起始日期;结束日期已经做了对应处理。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants