№ · 玩法设计 CH.07.31
主页 · 🎲 玩法设计 · 日结与时点锚点系统 v1 — 需求卡

日结与时点锚点系统 v1 — 需求卡

📅 2026-07-31 · 📦 25.6 KB · 🎲 玩法设计 · 📝 MD 文档 🆕 14d 🏷️ Buff🏷️ 终局🏷️ 时间🏷️ 路线图🏷️ 玩法设计🏷️ 界面

目的:把"时间一直在转"变成"我在错过什么"。 范围:4 个优先级 P0 → P3,串成一套从「看见后果」到「规划行为」的递进机制。 状态:草案,等 P1 拍板开工。 关联AGENTS.md §1 三层架构 · src/utils/gameTime.ts · src/components/AppHeaderBar.tsx · docs/design/active/经济循环体系-v1.md


0. 背景与现状

当前时间系统

  • 1 天 = 24 slot,1 slot = 6 秒真实时间 → 1 天约 2.5 分钟
  • 分段:清晨(6-12) / 白天(12-18) / 傍晚(18-24) / 深夜(0-6)
  • 业务阶段:open(营业)/ closing(打烊)/ closed(关门)
  • 顶部 AppHeaderBar 时辰区只渲染 3 个文字:gameTimeLabel / timeOfDayLabel / businessPhase无进度、无锚点、无倒计时
  • getTimePhasePolicy 只决定"哪些事能做",没有"做了奖励/不做惩罚"的反馈链

玩家痛点

  1. 时间在走 → 玩家只感受到"流水",感受不到"错失"
  2. 今日没做完的事 → 明日没有清晰的累计反馈
  3. 早做 / 晚做 → 玩家看不出差别
  4. 招人 / 备料 / 补货的"决策" → 没有"应到时间",没有"应到但没到"的负反馈

核心目标

让玩家在每天结束(深夜 0:00)时,看到一张"今天我亏了多少"的结算卡——这是"压迫感"转化为"决策动力"的支点。


1. 领域架构(按 AGENTS.md §1 三层分离)

新建领域 daily/,遵循项目「数据 / 逻辑 / UI」三分。

| 层 | 目录 | 内容 | | :-: | :--- | :--- | | 数据层 | src/data/daily/ | 类型 + 常量 + 静态配置(漏接定价、保护期天数、锚点定义、默认待办模板) | | 逻辑层 | src/utils/daily/ | 纯函数(结算构建、锚点推算、待办评估、应到检查) | | UI 层 | src/components/daily/ | 4 个组件(日结卡、时间条、待办面板、应到红点) |

import 方向(强制)

UI 层 ──> 逻辑层 ──> 数据层
  ↑           ↑
  └───────────┘
    (UI 也可直接读数据常量)

禁止

  • ❌ 数据层 import utils/ 或 components/
  • ❌ 逻辑层 import components/
  • ❌ 跨领域 import(daily/ 内部不直接读 auction/ 的内部数据,经 utils 走)

共享类型

  • DailySettlement / TimeAnchor / DailyChecklistItem / AnchorReadiness 全部定义在 src/data/daily/types.ts
  • 命名约定沿用 AGENTS.md §4:PascalCase 类型、UPPER_SNAKE_CASE 常量、camelCase 函数、Logic 后缀

2. P0 · 日结日报卡(最关键,零风险)

用户故事

作为掌柜,每天深夜 0:00 我想看到一张结算卡,知道「今天接待了多少 / 漏接了多少 / 亏了多少」,并对比昨日。

触发

  • 每进入 slot === 0(即新一天 0:00)时自动触发一次
  • 一次弹窗只能被关闭,不重复触发
  • 历史日结存档到 dailyHistory: DailySettlement[],供主厅"日结回顾"面板查询

数据流

触发 → collectDayMetrics(state) → buildDailySettlement(metrics, prevDay)
     → render <DailySettlementCard settlement={...} onClose={...} />
     → onClose: push to dailyHistory, 标记已读

字段定义(src/data/daily/types.ts

export interface DailySettlement {
  day: number;                       // 第几天
  dateLabel: string;                 // '第 3 日 · 七月初九'
  businessDate: BusinessDate;        // 复用现有 BusinessDate 类型
  // —— 基础数据 ——
  servedGuests: number;              // 接待人数
  servedGuestsDelta: number;         // 对比昨日 (+/-)
  income: number;                    // 营业收入
  incomeDelta: number;               // 对比昨日
  // —— 漏接(关键)——
  missedGuests: number;              // 漏接人数
  missedLoss: number;                // 漏接潜在损失(按客单价估算)
  missedReasonTags: string[];        // 漏接原因 ['座位满', '厨房爆', '无人接待']
  // —— 资源警示 ——
  inventoryAlerts: InventoryAlert[]; // 库存告急清单
  staffFatigue: { name: string; delta: number }[]; // 员工疲劳累积
  // —— 明日预告 ——
  tomorrowPreview: { customerMult: number; weatherLabel: string };
  // —— 对比基准 ——
  comparedTo: 'prevDay' | 'weekAgo'; // 固定为 prevDay
  // —— 元数据 ——
  isProtectedDay: boolean;           // 是否在保护期内(前 3 天)
  createdAt: number;                 // 时间戳
}

export interface InventoryAlert {
  itemName: string;
  remaining: number;
  daysUntilEmpty: number;            // 按今日消耗速率估算
  severity: 'warn' | 'danger';
}

关键规则

| 规则 | 说明 | | :- | :- | | 「漏接」定义 | 客流高峰(12:00-18:00)时段,因 座位满 / 厨房爆 / 无人接待 而未能入座的客人总数。需要客流系统已经能输出"应到 vs 实际",开工前请先确认此数据源;如无则用「实际入座 < 应到」的差值 | | 「漏接损失」定价 | = 漏接人数 × 该时段的平均客单价。客单价取当日「已接待客人平均消费」× 0.85(折扣因子,模拟"本可赚到但没赚到") | | 保护期 | 前 3 天(day 1-3)isProtectedDay = true漏接损失显示但不入账。从 day 4 开始正式扣银(但仍仅展示,不直接扣到 coins,只做软提醒) | | 库存告警 | 当日 24:00 库存 ≤ 2 天消耗量 → 告警;≤ 0.5 天 → 红色危险 | | 员工疲劳 | 当日总疲劳累加 +N%,若 > 80% 提示"明日产能 -X%" | | 明日预告 | 复用现有 TodayCalendarSnapshot(节气/天气/客流倍率) |

三层落点

| 层 | 文件 | 内容 | | :-: | :--- | :--- | | 数据 | src/data/daily/settlement.ts | DailySettlement / InventoryAlert 类型 + MISSED_LOSS_DISCOUNT_FACTOR / PROTECTION_DAYS 常量 | | 数据 | src/data/daily/missedReasons.ts | MISSED_REASON_LABELS 映射(座位满/厨房爆/无人接待/打烊中/天气) | | 逻辑 | src/utils/daily/settlementLogic.ts | buildDailySettlement(state, prevDay): DailySettlement 纯函数 | | 逻辑 | src/utils/daily/missedGuestsLogic.ts | aggregateMissedGuests(customerFlowLog): { count, loss, reasons } | | UI | src/components/daily/DailySettlementCard.tsx | 模态弹窗组件 | | UI | src/components/daily/MissedLossLine.tsx | 漏接行(带 ⚠ 警示)独立组件便于复用 |

UI 草图

┌──────────────── 第 3 日 · 日结 ────────────────┐
│  掌柜,今天辛苦了,请过目。                    │
│                                                │
│  📈 接待客人  28 位  ↑ 5                       │
│  💰 营业收入  1,240 银  ↑ 180                  │
│                                                │
│  ⚠ 漏接      7 位  → 损失 约 105 银           │  ← 关键
│     原因:座位满 4 · 厨房爆 2 · 无人接待 1     │
│                                                │
│  📦 库存告急                                    │
│     · 米 剩 3 桶(约 1.5 天用完)🔴            │
│     · 油 剩 5 壶(约 2 天用完)🟡             │
│                                                │
│  😓 员工疲劳                                    │
│     · 大嘴  +18(明日产能 -8%)                │
│                                                │
│  🌤 明日预告                                    │
│     客流 +20% · 多云转晴                       │
│                                                │
│           [ 收下,继续经营 ]                    │
└────────────────────────────────────────────────┘

验收清单

  • [ ] 每天 0:00 slot 自动触发日结弹窗
  • [ ] 弹窗显示 7 个字段组(接待/营收/漏接/库存/疲劳/预告/对比)
  • [ ] "漏接"字段必显示「X 位 · 损失 Y 银」+ 原因 tag
  • [ ] 对比昨日用 ↑/↓ 标记,持平用 —
  • [ ] 关闭后写入 dailyHistory,不重复触发
  • [ ] day 1-3 保护期:isProtectedDay = true,漏接损失灰色"演示中"
  • [ ] day 4+:漏接损失正常显示(软提醒,不实际扣银)
  • [ ] 现有 66 个 tsc 错误不新增
  • [ ] npm run lint 通过
  • [ ] npm run build 通过

估时

半天(半天数据/逻辑 + 半天 UI 组件 + 半天联调)。等数据源(客流日志)确认后开始。

依赖

  • 🔴 开工前必须确认:客流系统是否能输出"应到 vs 实际入座"的对比数据?如无,P0 需要降级为"只显示接待数 + 营收"两个字段,"漏接"延后到 P1 + P3 一起做
  • 🟡 现有 TodayCalendarSnapshot 复用即可,无需新增

3. P1 · 顶部时间条改造(进度条 + 锚点 + 倒计时)

用户故事

作为掌柜,顶部时间条不再是冷冰冰的文字,而是一根"我正走在一根有刻度的尺子上"的进度条,让我随时知道「下一件该发生的事还有多久」。

触发

  • 持续渲染(不是事件),基于 gameTime.slot 实时计算

数据流

AppHeaderBar 渲染 → 内部嵌入 <TimeProgressBar gameTime={gameTime} />
                 → getNextAnchor(slot) / getTimeToNextAnchor(slot)
                 → 渲染进度条 + 4 个锚点 + 倒计时

字段定义

// src/data/daily/timeAnchors.ts
export type AnchorId = 'dawn-prep' | 'noon-peak' | 'dusk-closing' | 'night-settle';

export interface TimeAnchor {
  id: AnchorId;
  slot: number;             // 锚点 slot(6 / 12 / 18 / 0)
  label: string;            // '厨子备料' / '客流高峰' / '打烊收尾' / '深夜结算'
  iconKey: string;          // 复用 TONGFU_ICON_ASSETS key
  toneClass: string;        // Tailwind class
  hint: string;             // 锚点说明 '客人最多的时候'
}

export const TIME_ANCHORS: TimeAnchor[] = [
  { id: 'dawn-prep',     slot: 6,  label: '厨子备料',   iconKey: 'kitchenPot',  toneClass: '...', hint: '厨房开始一天的工作' },
  { id: 'noon-peak',     slot: 12, label: '客流高峰',   iconKey: 'guestGroup',  toneClass: '...', hint: '客人最多的时候' },
  { id: 'dusk-closing',  slot: 18, label: '打烊收尾',   iconKey: 'broom',       toneClass: '...', hint: '厨房清理,盘点' },
  { id: 'night-settle',  slot: 0,  label: '深夜结算',   iconKey: 'abacus',      toneClass: '...', hint: '日结日报' },
];

关键规则

| 规则 | 说明 | | :- | :- | | 当前锚点 | currentAnchor = TIME_ANCHORS.find(a => a.slot === currentTimeSlot);或「两个锚点之间」取上一个 | | 下一锚点倒计时 | getTimeToNextAnchor(slot) = (next.slot - slot + 24) % 24 | | 跨越 0 点 | slot 22 时,下一锚点是 24+0=0,逻辑用 (next.slot - slot + 24) % 24 自然处理 | | 已完成锚点 | 当前 slot > 锚点 slot(且未跨日)→ 显示 ✓ 状态 | | 下一锚点 | 高亮 + 倒计时数字 |

三层落点

| 层 | 文件 | 内容 | | :-: | :--- | :--- | | 数据 | src/data/daily/timeAnchors.ts | TimeAnchor 类型 + TIME_ANCHORS 常量数组 | | 逻辑 | src/utils/daily/timeAnchorLogic.ts | getCurrentAnchor(slot) / getNextAnchor(slot) / getTimeToNextAnchor(slot) / getAnchorsStatus(slot) | | UI | src/components/daily/TimeProgressBar.tsx | 进度条 + 锚点 + 倒计时 复合组件 | | UI | 修改 src/components/AppHeaderBar.tsx | 把 gameTimeLabel 段(line 92-108)替换为 <TimeProgressBar gameTime={gameTime} /> |

UI 草图

时辰  第 3 日 · 白天 12:00    [营业中]
      ┌──────────────────────────────────────────┐
      │  ●6:00  ◆12:00  ✦18:00  ◯0:00            │
      │  ✓已过   ◀当前  ↘ 还有 6 时段  ↘ 还有 12 │
      │  [▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░]   │
      └──────────────────────────────────────────┘

验收清单

  • [ ] 顶部时间条可见 4 个锚点
  • [ ] 当前时点对应锚点高亮(蓝色脉冲)
  • [ ] 已过锚点显示 ✓ 灰色
  • [ ] 未到锚点显示剩余时段数
  • [ ] 每过一个 slot 倒计时 -1
  • [ ] 通过 06:00 / 12:00 / 18:00 / 00:00 时点时锚点状态切换
  • [ ] 跨日(slot 0)自动重置
  • [ ] 移动端宽度 < 640 时锚点标签自动隐藏(只显图标)

估时

1 天。纯函数 + 1 个新组件 + 1 个文件小改。

依赖

  • 🟢 无。可独立施工。

4. P2 · 今日待办清单(玩家每日规划)

用户故事

作为掌柜,我每天可以为今天 4 个锚点各安排"该完成什么",完成后自动 ✓,没完成进入日结的"未完成"列。

触发

  • 每天清晨 06:00(dawn-prep 锚点)自动生成今日默认待办
  • 玩家可手动增删/勾选
  • 跨日 24:00 自动归档未完成项到日结

字段定义

// src/data/daily/checklist.ts
export type ChecklistCategory = 'staff' | 'inventory' | 'kitchen' | 'reputation' | 'finance';

export interface DailyChecklistItem {
  id: string;                       // uuid
  day: number;                      // 所属日期
  anchorId: AnchorId;               // 关联到哪个时点
  category: ChecklistCategory;
  title: string;                    // '派大嘴值早班'
  description?: string;             // '前厅 +1 接待力'
  autoCompleteCondition?: string;   // 自动化检查 key,如 'staff.dazui.shift.morning'
  isCompleted: boolean;             // 玩家手动勾 / 自动化 ✓
  completedAt?: number;             // 时间戳
  carriedOver: boolean;             // 是否为昨日逾期带入
}

export const DEFAULT_DAILY_CHECKLIST_TEMPLATE: Omit<DailyChecklistItem, 'id' | 'day' | 'isCompleted' | 'completedAt' | 'carriedOver'>[] = [
  { anchorId: 'dawn-prep',    category: 'kitchen',    title: '厨房备料',     description: '米/油/盐库存 ≥ 5' },
  { anchorId: 'dawn-prep',    category: 'staff',      title: '早班派班',     description: '前厅 +1 厨子 +1' },
  { anchorId: 'noon-peak',    category: 'inventory',  title: '高峰备货',     description: '招牌菜食材足量' },
  { anchorId: 'dusk-closing', category: 'kitchen',    title: '打烊清洁',     description: '厨子收尾' },
  { anchorId: 'night-settle', category: 'finance',    title: '盘点铜钱',     description: '查看日结' },
];

关键规则

| 规则 | 说明 | | :- | :- | | 生成时机 | 每天 slot 6(dawn-prep)时从 DEFAULT_DAILY_CHECKLIST_TEMPLATE 复制生成今日 checklist | | 逾期带入 | 昨日未完成项 carriedOver = true,移入今日最高优先级位置(不强制仍要在昨日锚点) | | 自动完成 | 若 autoCompleteCondition 命中(如 staff.dazui.shift.morning = true),自动 isCompleted = true | | 手动完成 | 玩家在面板上点 ✓ | | 未完成后果 | 跨日 24:00 时未完成项进入日结卡的"未完成 N 项"字段(不直接扣银) | | 逾期上限 | 同一 item carriedOver 最多 2 天,第 3 天强制归档到日结历史不显示在面板 |

三层落点

| 层 | 文件 | 内容 | | :-: | :--- | :--- | | 数据 | src/data/daily/checklist.ts | DailyChecklistItem 类型 + DEFAULT_DAILY_CHECKLIST_TEMPLATE 常量 | | 逻辑 | src/utils/daily/checklistLogic.ts | generateDailyChecklist(day, carriedOver) / evaluateChecklist(items, state) / archiveOverdue(items, day) | | 逻辑 | src/utils/daily/checklistAutoComplete.ts | tryAutoComplete(item, state): boolean 检查各项 autoCompleteCondition | | UI | src/components/daily/DailyChecklistPanel.tsx | 抽屉式面板,按 4 个锚点分组 | | UI | src/components/daily/ChecklistItem.tsx | 单个待办行(含自动完成态/手动勾选/逾期 badge) |

UI 草图

今日待办(第 3 日)                       [ + 新增 ]
┌─ 🌅 厨子备料 06:00 ─────────────────── 2/3 已完成
│  ✓ 厨房备料                              自动完成
│  ✓ 早班派班                              自动完成
│  ☐ 检查油壶是否够用                       [手动]
│
├─ ☀ 客流高峰 12:00 ─────────────────── 0/1 已完成
│  ☐ 高峰备货                              [手动]
│
├─ 🌆 打烊收尾 18:00 ─────────────────── 0/1 已完成
│  ☐ 打烊清洁                              [自动]
│
└─ 🌙 深夜结算 0:00 ─────────────────── 0/1 已完成
   ☐ 盘点铜钱                              [手动]

验收清单

  • [ ] 每天 slot 6 自动从模板生成今日待办
  • [ ] 玩家可手动勾选 / 取消
  • [ ] 自动化条件命中时自动 ✓ 并显示「自动完成」badge
  • [ ] 昨日未完成项带「⚠ 逾期」badge 带入今日
  • [ ] 同 item 逾期 ≥ 3 天强制归档
  • [ ] 跨日 24:00 未完成项进入日结卡"未完成 N 项"
  • [ ] 玩家可手动新增自定义待办
  • [ ] 面板支持按锚点折叠/展开

估时

1.5 天(半天数据/逻辑 + 1 天 UI + 半天自动化接入)。

依赖

  • 🟢 内部依赖 P1(P1 提供锚点定义),但 P2 内部也可独立 mock 锚点常量
  • 🟡 「自动完成条件」需要和现有员工 / 库存 / 厨房系统对接,对接工作量单独估算

5. P3 · 时点驱动的「应到 vs 实际」后果链(最重,后置)

用户故事

作为掌柜,每天到锚点时,客栈会自动检查「该到位的资源」是否到位。不到位时实际产出降级(出菜慢 / 拒客 / 关门),并把降级结果累计到日结"漏接"列。

触发

  • 每个锚点 slot(6/12/18/0)触发 checkAnchorReadiness(anchor, state)
  • 输出 AnchorReadiness 报告,降级结果回写到当日 metrics

字段定义

// src/data/daily/anchorRequirements.ts
export interface AnchorRequirement {
  anchorId: AnchorId;
  requirements: RequirementCheck[];
}

export type RequirementCheck =
  | { kind: 'staff-min';        role: 'front' | 'kitchen'; min: number; }
  | { kind: 'inventory-min';    itemId: string; min: number; }
  | { kind: 'kitchen-recipe';   recipeId: string; }
  | { kind: 'coin-min';         min: number; };

export interface AnchorReadiness {
  anchorId: AnchorId;
  isReady: boolean;
  gaps: RequirementGap[];          // 未达标的项
  downgrades: DowngradeEffect[];   // 实际触发的降级
}

export interface DowngradeEffect {
  kind: 'slow-service' | 'reject-customers' | 'auto-close' | 'reputation-loss';
  severity: number;                 // 1-3
  description: string;
}

关键规则

| 规则 | 说明 | | :- | :- | | 客流高峰 12:00 检查 | 座位 ≥ 阈值 / 厨子 ≥ 1 / 库存 ≥ 高峰消耗量 | | 厨子不足 | slow-service:出菜速度 -30%,顾客差评率 +15% | | 座位满 | reject-customers:漏接人数 +N | | 库存 < 0.5 天 | auto-close:该时点强制进入 closing | | 降级量化 | 每个 downgrade 转成「漏接 N 位 / 损失 M 银」累加到日结 | | 保护期 | day 1-3 所有 downgrade 降级 50% | | 可被 P2 待办缓解 | 玩家在 P2 待办里"完成"对应项,可减少 gap 数量 |

三层落点

| 层 | 文件 | 内容 | | :-: | :--- | :--- | | 数据 | src/data/daily/anchorRequirements.ts | AnchorRequirement / RequirementCheck / DowngradeEffect 类型 + 默认配置 | | 逻辑 | src/utils/daily/anchorCheckLogic.ts | checkAnchorReadiness(anchor, state): AnchorReadiness | | 逻辑 | src/utils/daily/downgradeLogic.ts | applyDowngrade(state, downgrade): NewState | | UI | 复用 P0 日结卡「漏接」列 + P2 待办面板红点 | 不用新增组件 | | UI | src/components/daily/AnchorGapChip.tsx | 在 P1 时间条锚点旁显示「⚠2」红 badge |

UI 草图(P1 时间条上的红 badge)

●6:00  ◆12:00 ⚠2  ✦18:00  ◯0:00
                  ↑ 当前高峰有 2 项资源缺口

验收清单

  • [ ] 4 个锚点 slot 自动检查资源
  • [ ] 不达标时弹出 toast「客流高峰:厨房人手不足」
  • [ ] 时间条对应锚点显示红 badge
  • [ ] 降级效果实际影响出菜/接待/关门
  • [ ] 降级结果进入日结"漏接"列
  • [ ] day 1-3 保护期降级 50%
  • [ ] P2 待办完成可缓解对应 gap(具体对接哪些待办项在 P2 落地时定)

估时

3-4 天(1 天配置 + 1 天逻辑 + 1 天 UI 接入 + 0.5-1 天联调 + 0.5 天 P0+P1+P2 兼容)

依赖

  • 🔴 必须在 P0 + P1 + P2 上线后跑 1-2 个版本,根据玩家反馈决定是否上 P3
  • 🔴 必须和员工 / 库存 / 厨房三个系统约定好"资源接口"

6. 节奏与平衡

1 天 2.5 分钟是否要调?

| 节奏 | 适合度 | 建议 | | :- | :- | :- | | 1 天 2.5 分钟(现状) | 加了 P0+P1 后会显得太快——日结卡读完时间已经过了 1/3 | 🟡 上 P0 后看玩家反馈再定 | | 1 天 4 分钟 | 4 个锚点各 1 分钟,玩家有"做事窗口" | ✅ 建议作为 P3 上线时的配套调整 | | 1 天 5 分钟 | 偏慢,挂机感下降 | ❌ 不建议 |

建议:P0 上线时先不动节奏,P1 上线后看玩家是否反映"日结还没看完就过了一天",如反映再调到 4 分钟。

「漏接损失」定价

  • 客单价 = 当日「已接待客人平均消费」
  • 漏接损失 = 漏接人数 × 客单价 × 0.85(折扣因子,模拟"本可赚到但没赚到")
  • 软提醒,不直接扣银——这是关键,让玩家先学会看

保护期

  • day 1-3 全机制保护:日结卡 isProtectedDay = true,漏接损失灰色"演示中"
  • day 4+:正常显示,仅展示不扣银
  • 真实扣银(如果要做)放到 P3 联调稳定后再开

7. 风险与依赖

风险

| 风险 | 等级 | 缓解 | | :- | :-: | :- | | 「漏接」数据源不存在 | 🔴 高 | 开工前先 grep 客流系统;若没有,降级方案:P0 只显示接待/营收两个字段,漏接延后 | | P0 + P1 同时上线,玩家没有「适应期」 | 🟡 中 | 隔版本上线,P0 先跑 1 版再上 P1 | | 自动化条件对接 P2 工作量低估 | 🟡 中 | 落地前先和员工/库存系统 owner 拉清单 | | 保护期结束玩家突然被罚 | 🟡 中 | day 4 弹一次性说明 toast「保护期结束,掌柜的判断将真实影响日结」 | | 顶部时间条宽度不够 | 🟢 低 | 移动端 < 640px 自动隐藏文字只显图标 | | 3 层架构破坏 | 🟢 低 | 每个文件落到对应层;提交前按 AGENTS.md §1 自检 |

全局依赖

  • 🟡 TodayCalendarSnapshot 复用,无新增
  • 🟡 BusinessDate / Calendar 类型复用
  • 🔴 客流系统**输出"应到 vs 实际"**的能力——P0 开工前必确认
  • 🟢 现有 coins / reputation / 库存接口稳定,无破坏性改动

8. 落地路线

| 阶段 | 内容 | 估时 | 准入条件 | | :- | :- | :- | :- | | P0 | 日结日报卡(漏接 + 营收 + 库存 + 疲劳 + 预告) | 0.5 天 | 客流日志源确认 | | P1 | 顶部时间条改造(进度条 + 锚点 + 倒计时) | 1 天 | 无 | | 观察期 | 跑 1-2 个版本(每版本 3-5 天) | — | 收集玩家反馈 | | P2 | 今日待办清单(手动 + 自动完成 + 逾期) | 1.5 天 | P1 已上线 | | 观察期 2 | 跑 1-2 个版本 | — | 收集玩家反馈 | | P3 | 时点后果链(应到 vs 实际 + 降级) | 3-4 天 | P0+P1+P2 稳定 + 资源接口定 | | 节奏调整 | 1 天从 2.5 分钟调到 4 分钟(如果需要) | 0.5 天 | P1 反馈确认需要 |

提交规范

每个阶段完工时:

  • [ ] npm run lint 通过
  • [ ] npm run build 通过
  • [ ] 现有 66 个 tsc 错误不新增
  • [ ] 三层架构自检(按 AGENTS.md §1)
  • [ ] 至少 1 个新测试(*.regression.ts 或现有测试文件)
  • [ ] 更新 docs/design/active/产品需求总览-v1.md 加一行"日结/锚点系统 · P0 ✅"

9. 验收总览

P0 验收(半日交付)

  • ✅ 每天 0:00 弹日结卡,7 个字段组齐全
  • ✅ 漏接数 + 损失银 + 原因 tag 必显示
  • ✅ 保护期正确标记
  • ✅ 历史日结可查

P1 验收(1 日交付)

  • ✅ 4 锚点进度条 + 倒计时实时刷新
  • ✅ 当前锚点高亮
  • ✅ 移动端降级(隐藏文字)

P2 验收(1.5 日交付)

  • ✅ 每日 slot 6 自动生成 checklist
  • ✅ 自动 + 手动完成双通道
  • ✅ 跨日逾期处理

P3 验收(3-4 日交付,需 P0+P1+P2 稳定)

  • ✅ 4 锚点资源检查触发降级
  • ✅ 降级量化进入日结
  • ✅ 时间条红 badge
  • ✅ 保护期降级 50%

10. 待拍板事项

开工前请 P1 拍板以下 5 项:

  1. P0 「漏接」数据源——客流系统是否已有"应到 vs 实际"?如无,是否同意降级方案(P0 只显示接待/营收,漏接延后)?
  2. 节奏是否调整——P0 上线时维持 2.5 分钟/天,还是直接调到 4 分钟/天?
  3. 保护期天数——3 天够吗?是否要 5 天?
  4. 漏接损失是否真扣银——P3 之前都做软提醒,P3 联调后再开真扣?还是有别的时间点?
  5. P2 自动化条件对接范围——是只做员工早班,还是全量(员工/库存/厨房/财务)?

文档版本:v1 · 2026-07-31 · 草案 作者:Mavis(产品向) 关联AGENTS.md §1 三层架构 · docs/design/active/经济循环体系-v1.md · docs/design/active/产品需求总览-v1.md