# 06 修复:P0-2/3/4/5 领取上限与规则校验链 Type: task Status: resolved Blocked by: 02 ## Question 按 ticket 02 的裁决修复领取/分配/新增即领取的规则校验链(P0-5 若判误报则跳过,仅复核批量路径): **校验链规格(PRD §6.2/§6.3 定稿)**: 1. **claim_rule**:池为 `CLAIM_RULE_ADMIN_ONLY` 时销售不可自领(claimLead 已有,复核批量领取路径是否同守)。 2. **hold_limit**(个人持有上限):`COUNT(lead) WHERE owner_user_id=? AND pool_id=? AND status IN (已领取,跟进中)` ≥ hold_limit → 拒。**计数口径仅已领取+跟进中**(已转商机/过期失效/作废不占名额)。 3. **daily_claim_limit**(每日领取上限):当日该用户从该池领取计数 ≥ daily_claim_limit → 拒;**0=无限跳过**;"每日"=自然日。计数的落库依据自选(lead_history CLAIM 行 op_time 或 lead.claim_time),写进 ADR/注释。 **落点**: - `claimLead()`:进链校验,超限抛 `CODE_HOLD_LIMIT_EXCEEDED(65007)`/`CODE_DAILY_LIMIT_EXCEEDED(65006)`(以 `LeadConstants`/`ResultCode` 既有定义为准,先查后用)。 - `assignToUser()`:同一套校验,**额度归属=被指派销售**(不是操作管理员),按目标线索所属池的上限值算(grill #7)。 - `createLead(claimOnCreate=true)`:**复用同一校验链**。语义已由用户定稿(2026-08-19):勾选"是否领取"但超上限(持有/每日)或池 claim_rule 禁自领时,**整个新增失败报错**,用户只能改选"否"再提交。 - 批量路径 `claim-batch`/`assign-user-batch`:逐条走同一守卫,超限记 `LeadBatchFailReason.OVER_HOLD_LIMIT`/`OVER_DAILY_LIMIT`(枚举已存在),部分成功不拖垮整批。 **验收**:单测覆盖各边界(达上限拒/未达放行/limit=0 跳过/额度归属正确/claimOnCreate 超限整体失败)+ `mvn test`(crm-lead)全绿 + BOM 扫描。若校验逻辑可抽公共组件(如 `ClaimLimitChecker`),注意与 `LeadTransition` 对 crm-rule 零依赖契约的边界(查 crm-rule 侧的前置留在 LeadServiceImpl,参照 CONTEXT.md「状态机守卫」条目)。 ## Answer 已实施完毕(2026-08-19): - **ClaimLimitChecker 组件** `crm-lead/src/main/java/com/crm/lead/service/ClaimLimitChecker.java`——`@Component`,查 leadMapper: - hold_limit:`COUNT(lead) WHERE owner_user_id=? AND pool_id=? AND status IN (3,4)` ≥ hold_limit → 抛 65007;**hold_limit=0 = 字面 0**(0 ≥ 0 恒真 → 拒) - daily_claim_limit:当日 `claim_time ≥ 今日 0 时` 计数 ≥ daily_claim_limit → 抛 65006;**0=无限跳过** - 计数依据 = `lead.claim_time`(claim/assign 均写,release 不清),反映领取次数非当前持有 - 架构边界:crm-lead 内组件,pool 的 limit 值由 LeadServiceImpl 从 crm-rule 取 LeadPool 后传入,不侵入 LeadTransition 的 crm-rule 零依赖契约 - **注入+加校验** `LeadServiceImpl`——构造器加 `ClaimLimitChecker`,三处调用: - `claimLead()`:claim_rule 检查后、transition 前,调 `check(userId, poolId, holdLimit, dailyLimit)` - `assignToUser()`:UNDISTRIBUTED/PENDING 路径,取 pool 后调 `check(userId=被指派销售, ...)`(额度归属=被指派销售) - `createLead(claimOnCreate=true)`:claim_rule 禁自领 → 抛 65005;额度超限 → 抛 65007/65006,**整个新增失败不写库** - **validatePool 修正** `LeadPoolServiceImpl`——`holdLimit < 1` 改为 `< 0`(允许 0=字面 0) - **批量路径**:`claimBatch`/`assignToUserBatch` 委派 `self::claimLead`/`self.assignToUser`,单条校验自动继承,超限由 `runBatch` 的 `LeadBatchFailReason.fromCode()` 自动映射 `OVER_HOLD_LIMIT`/`OVER_DAILY_LIMIT` - **单测**: - `ClaimLimitCheckerTest` 7 例(hold 达/超/未达/0=字面0;daily 达/0=跳过/未达) - `LeadServiceImplTest` 5 例(claimLead hold/daily 超限拒;assignToUser 额度归属=被指派销售;createLead ADMIN_ONLY 拒/额度超限整体失败) - `LeadPoolServiceImplTest` 修正 Q10(holdLimit=0 合法,-1 拒) - **mvn test**:crm-rule 26 tests 0 failures / crm-lead 82 tests 0 failures / BUILD SUCCESS - **BOM 扫描**:全部通过