fix: F1-5b MP-3 + MP-5 沙箱业务日小程序适配 (W1)

MP-3 customer-detail coachTasks.lastService 业务日上界裁剪:
- apps/backend/app/services/customer_service.py
  - import as_runtime_today_param 从 late import 提至模块顶部
  - _build_coach_tasks 开头取 ref_date,供两段 SQL 共用
  - 第一条直查 biz.coach_tasks 加 `AND updated_at < (%s::date + INTERVAL '1 day')::timestamptz`
  - 删除原方法内重复 ref_date 调用
- 业务影响:sandbox=2026-04-20 时,customer-detail 的"上次服务"
  时间不再展示 sandbox 业务日之后的助教任务更新(沙箱不读未来)
- 测试:apps/backend/tests/test_customer_detail_mp3_lastservice.py
  本地通过,因 .gitignore:71 不入仓(同 T1 / af02446 处理方式)

MP-5 coach-service-records 接入 getBusinessClock:
- apps/miniprogram/miniprogram/pages/coach-service-records/coach-service-records.ts
  - import getBusinessClock + data 加 clockYear/clockMonth/clockDay 字段
  - onLoad 改 async,await getBusinessClock() 取 business_year/month/date
  - loadData / switchMonth 4 处 new Date() → clockYear/Month/Day
- 业务影响:sandbox=2026-04-20 时,coach-service-records 默认显示
  "2026 年 4 月"业绩(而非 today 月),canGoNext=false 阻止翻到 5 月,
  "前 5 日预估金额"规则按 sandbox business_date 判断

双口径验证(weixin-devtools-mcp + DB 直查):
- MP-3 4a live: lastService 最大 04-19(无未来时间)
- MP-3 4b sandbox=4-20: 5-01 任务 task_id=8348/8347 完全消失
- MP-5 4a live: clockYear/Month/Day=2026/5/5,monthLabel="2026年5月"
- MP-5 4b sandbox=4-20: monthLabel="2026年4月" + 35 笔/¥4,657
   first group=2026-04-20(后端 SQL 上界裁剪生效)

审计:
- docs/audit/changes/2026-05-05__wave1_f1_5b_mp3_lastservice_upper_bound.md
- docs/audit/changes/2026-05-05__wave1_f1_5b_mp5_coach_service_records_clock.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Neo
2026-05-05 18:43:08 +08:00
parent 3c8d72edd4
commit 96dae0c778
4 changed files with 225 additions and 19 deletions

View File

@@ -12,6 +12,8 @@
* - 后端 /api/xcx/performance/records?coach_id=xxx权限码 view_board_coach
*/
import { checkPageAccess } from '../../utils/auth-guard'
// CHANGE 2026-05-05 | F1-5b MP-5: 业务时钟接入,sandbox 模式按 sandbox_date 判断"当前月/预估"
import { getBusinessClock } from '../../utils/runtime-clock'
import { fetchPerformanceRecords, fetchCoachBanner } from '../../services/api'
import { nameToAvatarColor } from '../../utils/avatar-color'
import { formatMoney, formatCount } from '../../utils/money'
@@ -61,13 +63,18 @@ Page({
/** Banner 主标题:用助教名生成"<助教名>的业绩" */
pageTitle: '业绩明细',
/** 月份切换 */
/** 月份切换onLoad 内会用 getBusinessClock 覆盖) */
currentYear: new Date().getFullYear(),
currentMonth: new Date().getMonth() + 1,
monthLabel: '',
canGoPrev: true,
canGoNext: false,
/** 业务时钟缓存onLoad 内由 getBusinessClock 写入,sandbox 模式取 sandbox_date */
clockYear: new Date().getFullYear(),
clockMonth: new Date().getMonth() + 1,
clockDay: new Date().getDate(),
/** 当月预估判断 */
isCurrentMonth: false,
@@ -86,7 +93,7 @@ Page({
hasMore: false,
},
onLoad(options: Record<string, string | undefined>) {
async onLoad(options: Record<string, string | undefined>) {
const coachIdNum = Number(options?.coachId)
const coachId = Number.isFinite(coachIdNum) && coachIdNum > 0 ? coachIdNum : 0
if (coachId === 0) {
@@ -95,12 +102,20 @@ Page({
setTimeout(() => wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/board-finance/board-finance' }) }), 1000)
return
}
const now = new Date()
// CHANGE 2026-05-05 | 业务时钟取代 new Date(),sandbox 模式下显示 sandbox_date 所在月
const clock = await getBusinessClock()
const clockYear = clock.business_year
const clockMonth = clock.business_month
// business_date 形如 "2026-04-20",取 day 用于"当月前 5 日预估"判断
const clockDay = Number(clock.business_date.slice(8, 10)) || new Date().getDate()
this.setData({
coachId,
currentYear: now.getFullYear(),
currentMonth: now.getMonth() + 1,
monthLabel: `${now.getFullYear()}${now.getMonth() + 1}`,
currentYear: clockYear,
currentMonth: clockMonth,
monthLabel: `${clockYear}${clockMonth}`,
clockYear,
clockMonth,
clockDay,
})
this.loadBanner()
this.loadData()
@@ -149,11 +164,11 @@ Page({
}
wx.showLoading({ title: '加载中...', mask: true })
const now = new Date()
const { currentYear, currentMonth } = this.data
const isCurrentMonth = currentYear === now.getFullYear()
&& currentMonth === now.getMonth() + 1
&& now.getDate() <= 5
// CHANGE 2026-05-05 | 业务时钟替代 new Date(),sandbox 模式下"当月预估"按 sandbox_date 判断
const { currentYear, currentMonth, clockYear, clockMonth, clockDay } = this.data
const isCurrentMonth = currentYear === clockYear
&& currentMonth === clockMonth
&& clockDay <= 5
try {
const res = await fetchPerformanceRecords({
@@ -253,11 +268,10 @@ Page({
if (currentMonth > 12) { currentMonth = 1; currentYear++ }
}
const now = new Date()
const nowYear = now.getFullYear()
const nowMonth = now.getMonth() + 1
const canGoNext = currentYear < nowYear || (currentYear === nowYear && currentMonth < nowMonth)
const isCurrentMonth = currentYear === nowYear && currentMonth === nowMonth && now.getDate() <= 5
// CHANGE 2026-05-05 | 业务时钟替代 new Date(),sandbox 模式下"未来月"按 sandbox_date 判断
const { clockYear, clockMonth, clockDay } = this.data
const canGoNext = currentYear < clockYear || (currentYear === clockYear && currentMonth < clockMonth)
const isCurrentMonth = currentYear === clockYear && currentMonth === clockMonth && clockDay <= 5
this.setData({
currentYear,