# 四视图卡片口径映射表定稿 Type: grilling Status: resolved Blocked by: 04 ## Question 原问题: 产出一张权威的「视图 × 卡片 → 口径」映射表,作为 spec 的核心。每格给出 status 集合或派生条件(含今日新增的时间口径、销售持有的合并口径)。初稿如下,需逐格确认/修订: | 卡片 | 口径 | 公海 | 我的线索 | 我的关注 | 线索管理 | |---|---|---|---|---|---| | 线索总量 | 视图内 count(*) | ✓ | ✓ | ✓ | ✓ | | 待领取 | status=2 | ✓ | | ✓ | ✓ | | 今日新增 | DATE(create_time)=今天 | ✓ | | | ✓ | | 已领取 | status=3 | | ✓ | ✓ | | | 跟进中 | status=4 | | ✓ | ✓ | | | 已转商机 | status=5 | | ✓ | ✓ | ✓ | | 线索作废 | status=7(口径见 #01) | | ✓ | ✓ | | | 过期失效 | status=6 | | ✓ | ✓ | ✓ | | 未分发 | status=1 | | | | ✓ | | 销售持有 | status IN (3,4) | | | | ✓ | 待确认要点: - 「线索总量」在各视图分母是什么?是视图内全部可见线索,还是全部 7 态?(尤其 公海 现只见 2/3/4,我的线索 只见有 owner 的)——直接受 #01 结论影响。 - 各卡片口径是否要吃 `/page` 的动态筛选(keyword/channel/…),还是固定视图口径? - 「销售持有」= 已领取 + 跟进中,与 我的线索/我的关注 里的拆分字段是同源计算。 ## Answer ### 核心口径公式 **所有分卡口径 = `applyViewFilters(viewType, param, currentUserId)` + 该 status 条件**。无逐格特判、无额外 owner 断言。 其中 `applyViewFilters` 保持现状语义(不改): - `PUBLIC_POOL`:`status IN (2,3,4)` - `MY_LEAD`:`owner_user_id = 当前用户`(不加 status 过滤,天然含作废 owner=我 —— 由 ticket 01/04 保证) - `MY_FOLLOW`:`id IN (SELECT lead_id FROM lead_follow WHERE user_id = 当前用户)`(不看 owner、不看 status) - `MANAGE`:无业务过滤,仅靠 `@DataScope` 注入部门/OWNER 天花板 `@DataScope` 由 MyBatis 拦截器自动叠加在同一 wrapper 上(`Lead` 实体注解),四视图 stats 天然吃到。 ### 视图 × 卡片 → 口径映射表(权威定稿) | 卡片 | 口径(在 `applyViewFilters` 之上再叠加) | 公海 | 我的线索 | 我的关注 | 线索管理 | |---|---|---|---|---|---| | 线索总量 | `count(*)`(视图内全部可见) | ✓ | ✓ | ✓ | ✓ | | 今日新增 | `create_time >= todayStart AND create_time < tomorrowStart` | ✓ | ✗ | ✗ | ✓ | | 待领取 | `status = 2` | ✓ | ✗ | ✓ | ✓ | | 未分发 | `status = 1` | ✗ | ✗ | ✗ | ✓ | | 已领取 | `status = 3` | ✗ | ✓ | ✓ | ✗ | | 跟进中 | `status = 4` | ✗ | ✓ | ✓ | ✗ | | 销售持有 | `status IN (3, 4)` | ✗ | ✗ | ✗ | ✓ | | 已转商机 | `status = 5` | ✗ | ✓ | ✓ | ✓ | | 过期失效 | `status = 6` | ✗ | ✓ | ✓ | ✓ | | 线索作废 | `status = 7` | ✗ | ✓ | ✓ | ✗ | ### 决策来源(7 格 grilling 结论) 1. **总量分母(Grill 1,甲)**:「线索总量」= 视图 `applyViewFilters` 之后 `count(*)`,四视图一致。允许“分卡覆盖不全 7 态、之和 ≠ 总量”(如「我的关注」缺 status=1 分卡是允许的)。 2. **动态筛选吃不吃(Grill 2,甲)**:卡片吃 `/page` 全部动态筛选(keyword/channel/brand/product/province/statusIn/poolId/deptId/isUrgent/feedbackStatus)。现状 `countStats` 复用完整 `applyViewFilters`,零改动。 - **副作用(留给 03 前端契约处理)**:若用户传 `statusIn=[3]`,则「已领取」=「线索总量」、其他分卡=0。这是甲的必然、非 bug;前端是否把 `statusIn` 从卡片计算里剔除属于 03 决策。 3. **今日新增时间口径(Grill 3,甲)**: - 时区 = JVM 本地 `LocalDate.now()`(与 `ClaimLimitChecker` 同源)。 - SQL 用范围条件 `create_time >= todayStart AND create_time < tomorrowStart`(索引友好),**不再用** `DATE(create_time) = CURRENT_DATE`。 - 时间字段 = `create_time`(入库时间)。**不看当前 status**——今日入库又今日作废也算 1 条。 4. **销售持有(Grill 4,甲)**:`status IN (3,4)`,纯 status 判据。与 `ClaimLimitChecker`/`ConvertibleLeadCatalogPortImpl` 同源。不加 `owner IS NOT NULL` 防御——状态机不变式(status ∈ {3,4} ⇒ owner 非空)由状态机保证,统计层不重复兜底。加法关系:`销售持有 = 已领取 + 跟进中`。 5. **「我的关注」的分卡是否叠 owner(Grill 5,甲)**:不叠。所有分卡 = `follow` 表内 + 该 status。产品语义支持——“关注 = 盯着这条线索的动向”,别人 owner 的线索被作废,也要计入「我的关注」→「线索作废」。 6. **MANAGE 视图数据范围(Grill 6,甲)**:MANAGE 无业务过滤 + `@DataScope` 天花板。数字随角色变化(ALL_VISIBLE / DEPT / OWNER),入口权限由 `LeadPermissionInitializer` 的 catalog 权限点控制,spec 里注明此点。 7. **需求✗格严格按原清单(Grill 7,甲)**:✗ 就是 ✗,不向产品发起补卡建议。以下三处✗ **不追问**,按需求原样交付: - 「我的线索」/「我的关注」无「今日新增」。 - 「线索管理」无「已领取(3)」「跟进中(4)」独立卡,只有合并的「销售持有」。 - 「线索管理」无「线索作废(7)」卡。 ### 交付给 03 的接力 03(DTO 字段形态与前端契约)需要处理: - **DTO 字段命名**: - 新增 `following`(跟进中 status=4)、`expired`(过期失效 status=6)、`voided` 或类似(作废 status=7,`void` 是 Java 关键字禁用)。 - 现有 `claimed` 语义歧义(当前 =3+4)——建议改名 `sellerHolding`(对应「销售持有」),或将 `claimed` 收窄为仅 status=3、另增 `sellerHolding` 字段。由 03 定。 - **DTO 是否全量返回**:后端一次算 `total/undistributed/waiting/claimed/following/converted/expired/voided/todayNew/sellerHolding` 全套,前端按视图渲染 —— 还是按 `viewType` 裁剪只返回该视图卡片对应字段?现状「全量返回」;由 03 定。 - **`statusIn` 与卡片的交互**:如 Grill 2 副作用所述,前端点卡片下钻 = 加 `statusIn` 筛选;若卡片本身又吃 `statusIn`,会出现“点了「已领取」后所有其他卡=0”的观感—— 03 定前端在卡片计算的 payload 里是否剔除 `statusIn`。 - **测试口径重写**:`LeadViewQueryImplTest#countStats_*` 全部按上表重写。断言矩阵 = 4 视图 × 上表勾选卡片,覆盖: - 视图 filter 生效 - `@DataScope` 生效(OWNER/DEPT/ALL_VISIBLE 三档) - 今日新增按 JVM 时区、范围条件 - 「我的关注」含别人 owner 的作废/过期(Grill 5) - 加法关系:`sellerHolding = claimed(3) + following(4)`(若 DTO 拆到这个粒度) ### 明确不属于本 ticket 的 - DTO 字段名 & 数量(03)。 - 前端契约(03)。 - 后端实现改动(新起 session)。 - 需求✗格是否补卡(甲:不追问)。