feat(miniprogram): board-finance isCurrentMonthFilter 接入业务时钟 (W1-T1 / P0-3)
Wave 1 Day 2 看板沙箱接入。 调研修正: - xcx_board.py + board_service.py 服务层已接入 runtime_context (L264/L712) - board-customer.ts / board-coach.ts 0 处使用 new Date(),无需改 - 唯一遗漏: board-finance.ts isCurrentMonthFilter 用 new Date().getDate() 沙箱回放历史日期会判错"本月前 5 天预估"提示 修复: - import getBusinessClock from utils/runtime-clock - isCurrentMonthFilter 接收可选 businessDate 参数;空值降级 new Date() - data 加 businessDate: '' - onShow 拉业务时钟 + 修正 isCurrentMonth - onTimeChange 用 this.data.businessDate 参考: docs/audit/changes/2026-05-04__wave1_t1_board_sandbox_clock.md
This commit is contained in:
@@ -12,9 +12,23 @@
|
|||||||
import { checkPageAccess, getVisibleBoardTabs } from '../../utils/auth-guard'
|
import { checkPageAccess, getVisibleBoardTabs } from '../../utils/auth-guard'
|
||||||
import { getRandomAiColor } from '../../utils/ai-color'
|
import { getRandomAiColor } from '../../utils/ai-color'
|
||||||
import { fetchBoardFinance, fetchAICache } from '../../services/api'
|
import { fetchBoardFinance, fetchAICache } from '../../services/api'
|
||||||
|
import { getBusinessClock } from '../../utils/runtime-clock'
|
||||||
|
|
||||||
function isCurrentMonthFilter(selectedTime: string): boolean {
|
/**
|
||||||
return selectedTime === 'month' && new Date().getDate() <= 5
|
* 判断当前选择的时间筛选是否为"本月前 5 天"(用于显示"(预估)"提示)。
|
||||||
|
*
|
||||||
|
* Wave 1 W1-T1 沙箱接入(2026-05-04):
|
||||||
|
* - 优先使用业务时钟 businessDate(YYYY-MM-DD),沙箱模式下回放虚拟日期
|
||||||
|
* - businessDate 为空时降级到本地 new Date()(初次进入页面、业务时钟未就绪)
|
||||||
|
* - 后端 board_service 已通过 runtime_context 自处理时间窗口,本函数仅控制 UI 提示
|
||||||
|
*/
|
||||||
|
function isCurrentMonthFilter(selectedTime: string, businessDate?: string): boolean {
|
||||||
|
if (selectedTime !== 'month') return false
|
||||||
|
if (businessDate) {
|
||||||
|
const day = parseInt(businessDate.slice(8, 10), 10)
|
||||||
|
return !Number.isNaN(day) && day <= 5
|
||||||
|
}
|
||||||
|
return new Date().getDate() <= 5
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2026-04-22 小程序轻量 Markdown 内联解析:支持 **加粗** / *倾斜* / _倾斜_ / ***加粗倾斜***
|
// 2026-04-22 小程序轻量 Markdown 内联解析:支持 **加粗** / *倾斜* / _倾斜_ / ***加粗倾斜***
|
||||||
@@ -106,6 +120,8 @@ Page({
|
|||||||
|
|
||||||
compareEnabled: false,
|
compareEnabled: false,
|
||||||
isCurrentMonth: true,
|
isCurrentMonth: true,
|
||||||
|
// Wave 1 W1-T1 沙箱接入:业务时钟(YYYY-MM-DD),沙箱模式下回放虚拟日期。空字符串表示尚未拉取,降级用本地 new Date()
|
||||||
|
businessDate: '',
|
||||||
// 2026-04-22 改版:拆成 title/body 两段,title 在 wxml 渲染为 ai-insight-dim(灰色标题),body 为正文
|
// 2026-04-22 改版:拆成 title/body 两段,title 在 wxml 渲染为 ai-insight-dim(灰色标题),body 为正文
|
||||||
aiInsights: [] as Array<{ title: string; body: string }>,
|
aiInsights: [] as Array<{ title: string; body: string }>,
|
||||||
// 2026-04-22 seq11/12 置顶:从 aiInsights 派生,供"本期总结"卡片渲染
|
// 2026-04-22 seq11/12 置顶:从 aiInsights 派生,供"本期总结"卡片渲染
|
||||||
@@ -231,6 +247,16 @@ Page({
|
|||||||
})
|
})
|
||||||
const tabBar = this.getTabBar?.()
|
const tabBar = this.getTabBar?.()
|
||||||
if (tabBar) tabBar.setData({ active: 'board' })
|
if (tabBar) tabBar.setData({ active: 'board' })
|
||||||
|
// Wave 1 W1-T1:刷新业务时钟,沙箱模式下回放虚拟日期 → 修正"本月前 5 天预估"提示
|
||||||
|
getBusinessClock().then((clock) => {
|
||||||
|
const businessDate = clock.business_date || ''
|
||||||
|
this.setData({
|
||||||
|
businessDate,
|
||||||
|
isCurrentMonth: isCurrentMonthFilter(this.data.selectedTime, businessDate),
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
// 降级:保留默认 isCurrentMonth(本地时钟)
|
||||||
|
})
|
||||||
this._loadData()
|
this._loadData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -656,7 +682,7 @@ Page({
|
|||||||
onTimeChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
onTimeChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
||||||
const value = e.detail.value
|
const value = e.detail.value
|
||||||
const option = this.data.timeOptions.find(o => o.value === value)
|
const option = this.data.timeOptions.find(o => o.value === value)
|
||||||
this.setData({ selectedTime: value, selectedTimeText: option?.text || '本月', isCurrentMonth: isCurrentMonthFilter(value) })
|
this.setData({ selectedTime: value, selectedTimeText: option?.text || '本月', isCurrentMonth: isCurrentMonthFilter(value, this.data.businessDate) })
|
||||||
this._loadData()
|
this._loadData()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
150
docs/audit/changes/2026-05-04__wave1_t1_board_sandbox_clock.md
Normal file
150
docs/audit/changes/2026-05-04__wave1_t1_board_sandbox_clock.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# Wave 1 W1-T1 — 看板沙箱接入(P0-3)
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 日期 | 2026-05-04 |
|
||||||
|
| Wave | 1 / Day 2 |
|
||||||
|
| 范围 | P0-3 看板沙箱接入(实际改动量 < 原估算 5-7h,因调研发现后端已接入) |
|
||||||
|
| 文件改动 | 1 个小程序文件(board-finance.ts) |
|
||||||
|
| 编译验证 | tsc 对 board-finance.ts 零错误 ✅ |
|
||||||
|
|
||||||
|
## 一、调研修正前期 P0-3 报告
|
||||||
|
|
||||||
|
**P0-3 原结论(Wave 0)**:
|
||||||
|
> Grep 实证:小程序三大看板 + xcx_board.py **零处引用** runtime-clock / runtime_context
|
||||||
|
|
||||||
|
**Day 2 实地走查发现**:
|
||||||
|
- ✅ **xcx_board.py 是 router 层薄封装**,不直接引用 runtime_context 是正常设计
|
||||||
|
- ✅ **board_service.py(服务层)已接入 runtime_context**:
|
||||||
|
- L264 `runtime_ctx = get_runtime_context(site_id)` + `_calc_date_range(time, ref_date=runtime_ctx.business_date)`
|
||||||
|
- L482-490 60 天消费窗口已用 `as_runtime_today_param`
|
||||||
|
- L712 同样接入业务日
|
||||||
|
- ✅ **board-customer.ts / board-coach.ts 0 处使用 `new Date()`**,完全没问题
|
||||||
|
- ❌ **board-finance.ts 唯一遗漏**:`isCurrentMonthFilter` 函数用 `new Date().getDate() <= 5` 判断"本月前 5 天",沙箱模式回放历史日期时会判错
|
||||||
|
|
||||||
|
**P20 SPEC §13 已知冲突表的标记是准确的**:`board-finance.ts isCurrentMonthFilter` 设计文档要求改但未在 grep 结果中确认 → 现已确认未改,本轮修复。
|
||||||
|
|
||||||
|
## 二、修复内容
|
||||||
|
|
||||||
|
文件:[apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts](../../../apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts)
|
||||||
|
|
||||||
|
### 改动 1:import + isCurrentMonthFilter 改造
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { getBusinessClock } from '../../utils/runtime-clock'
|
||||||
|
|
||||||
|
function isCurrentMonthFilter(selectedTime: string, businessDate?: string): boolean {
|
||||||
|
if (selectedTime !== 'month') return false
|
||||||
|
if (businessDate) {
|
||||||
|
const day = parseInt(businessDate.slice(8, 10), 10)
|
||||||
|
return !Number.isNaN(day) && day <= 5
|
||||||
|
}
|
||||||
|
return new Date().getDate() <= 5 // 降级:业务时钟未就绪时用本地
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 改动 2:data 加 businessDate
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
data: {
|
||||||
|
...
|
||||||
|
isCurrentMonth: true,
|
||||||
|
businessDate: '', // YYYY-MM-DD,沙箱模式下回放虚拟日期
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 改动 3:onShow 拉业务时钟 + 修正 isCurrentMonth
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
onShow() {
|
||||||
|
checkPageAccess(...).then((allowed) => {
|
||||||
|
if (!allowed) return
|
||||||
|
...
|
||||||
|
// Wave 1 W1-T1:刷新业务时钟,沙箱模式下回放虚拟日期
|
||||||
|
getBusinessClock().then((clock) => {
|
||||||
|
const businessDate = clock.business_date || ''
|
||||||
|
this.setData({
|
||||||
|
businessDate,
|
||||||
|
isCurrentMonth: isCurrentMonthFilter(this.data.selectedTime, businessDate),
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
// 降级:保留默认 isCurrentMonth(本地时钟)
|
||||||
|
})
|
||||||
|
this._loadData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 改动 4:onTimeChange 用 businessDate
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
onTimeChange(...) {
|
||||||
|
this.setData({
|
||||||
|
selectedTime: value,
|
||||||
|
selectedTimeText: option?.text || '本月',
|
||||||
|
isCurrentMonth: isCurrentMonthFilter(value, this.data.businessDate), // 加 businessDate
|
||||||
|
})
|
||||||
|
this._loadData()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、未改动项(已是设计共识)
|
||||||
|
|
||||||
|
- `board-finance.ts L243 `Date.now()`** — onPageScroll 节流计时,纯 UI 行为(100ms 节流),与业务时间无关,**保留**(对齐 P20 SPEC §11.1 "操作时间戳保留 new Date()" 设计共识)
|
||||||
|
- `board-customer.ts` — 0 处 `new Date()`,**无需改**
|
||||||
|
- `board-coach.ts` — 0 处 `new Date()`,**无需改**
|
||||||
|
- 后端 `xcx_board.py` + `board_service.py` — 服务层已接入 runtime_context,**无需改**
|
||||||
|
|
||||||
|
## 四、行为对照(沙箱模式下)
|
||||||
|
|
||||||
|
| 场景 | 修改前 | 修改后 |
|
||||||
|
|---|---|---|
|
||||||
|
| live 模式,真实今天 = 2026-05-04(月初 4 日) | `isCurrentMonth = true`(预估提示)| 同 |
|
||||||
|
| live 模式,真实今天 = 2026-05-15(月中) | `isCurrentMonth = false`(无预估) | 同 |
|
||||||
|
| sandbox 切换到 2026-03-01 | `isCurrentMonth = false`(因为真实今天 = 2026-05-04 第 4 日)❌ | `isCurrentMonth = true`(基于 sandbox_date day=1 ≤ 5)✅ |
|
||||||
|
| sandbox 切换到 2026-03-15 | `isCurrentMonth = false`(同上 4 日)❌ | `isCurrentMonth = false`(基于 sandbox_date day=15 > 5)✅ |
|
||||||
|
| 业务时钟拉取失败 | 用 `new Date()`(降级一致)| 用 `new Date()`(降级一致)|
|
||||||
|
|
||||||
|
## 五、验证
|
||||||
|
|
||||||
|
### 工程层
|
||||||
|
|
||||||
|
- ✅ tsc `board-finance.ts` 零编译错误(其他遗留 component / app 类型错误与本次改动无关)
|
||||||
|
- ✅ getBusinessClock 已存在 `runtime-clock.ts`,接口稳定
|
||||||
|
- ✅ 降级路径(getBusinessClock 失败)保留 fallback `new Date()`,不会破坏 live 行为
|
||||||
|
|
||||||
|
### 成果层(留 W1-T8)
|
||||||
|
|
||||||
|
按 P20 SPEC §14.3.6 走查清单,W1-T8 时:
|
||||||
|
- 切 sandbox → 2026-03-01 → 进 board-finance → 经营一览 标题应显示"(预估)"
|
||||||
|
- 切 sandbox → 2026-03-15 → 进 board-finance → 经营一览 标题不应显示"(预估)"
|
||||||
|
- 切回 live → 行为应恢复
|
||||||
|
|
||||||
|
## 六、commit 建议消息
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(miniprogram): board-finance isCurrentMonthFilter 接入业务时钟 (W1-T1 / P0-3)
|
||||||
|
|
||||||
|
Wave 1 Day 2 看板沙箱接入。
|
||||||
|
|
||||||
|
调研修正:
|
||||||
|
- xcx_board.py + board_service.py 服务层已接入 runtime_context (L264/L712)
|
||||||
|
- board-customer.ts / board-coach.ts 0 处使用 new Date(),无需改
|
||||||
|
- 唯一遗漏:board-finance.ts `isCurrentMonthFilter` 用 new Date().getDate()
|
||||||
|
沙箱回放历史日期会判错"本月前 5 天预估"提示
|
||||||
|
|
||||||
|
修复:
|
||||||
|
- import getBusinessClock from utils/runtime-clock
|
||||||
|
- isCurrentMonthFilter 接收可选 businessDate 参数;空值降级 new Date()
|
||||||
|
- data 加 businessDate: ''
|
||||||
|
- onShow 拉业务时钟 + 修正 isCurrentMonth
|
||||||
|
- onTimeChange 用 this.data.businessDate
|
||||||
|
|
||||||
|
参考: docs/audit/changes/2026-05-04__wave1_t1_board_sandbox_clock.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 七、未覆盖 / 后续
|
||||||
|
|
||||||
|
- W1-T8 §14.3.6/7/8 走查时实地验证沙箱模式下三大看板行为
|
||||||
|
- 多角色身份走查(W1-T8 触发,主线提醒 Neo 切身份)
|
||||||
|
- 单测覆盖留 Wave 2(`tests/board-finance/test_isCurrentMonthFilter.ts`)
|
||||||
Reference in New Issue
Block a user