You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

52 lines
4.3 KiB

3 weeks ago
# 07 修复:P0-6/7 公海混合展示 + 新增线索归属约束
Type: task
Status: resolved
Blocked by: 02, 06
## Question
按 ticket 02 的裁决修复(注意与 06 串行——同改 `LeadServiceImpl`):
**P0-6 公海混合展示**(PRD §7 已定稿(E)):
- `LeadViewQueryImpl.buildViewWrapper()` PUBLIC_POOL 分支不再硬编码 `status=PENDING`,改为**"公海可见状态集"参数驱动**,默认 `{待领取, 已领取, 跟进中}`,不硬编码(后续可能收窄/按池细分——留参数化缝)。
- 参数放哪(常量类 / 配置项 / LeadPageParam 可选覆盖)由本 ticket 定,遵循"改规则不改代码"的定稿意图并写清注释。
- 已被领走的行只读属前端职责;后端保证列表含这些行 + "领取人"字段回填正确。
- `LeadViewQueryImplTest` 同步更新(现有 PUBLIC_POOL 断言会变)。
**P0-7 新增线索归属约束**(PRD §6.1 grill #6 定稿):
- 销售新增:**强制回填本人主部门的池**(不给选、不可编辑),忽略前端传入 poolId;兼职部门有池也只落主部门池。
- 主部门无池 → 报错拦截("本部门尚未配置公海池,请联系管理员"),不允许销售建挂空池线索。
- 管理员新增:可指定任意池,或 poolId=null 建"未分发"(管理员导入专属中间态)。
- **需要"当前用户是否管理员"判定**:先查本仓既有实践(角色/权限点判定机制,参照 crm-auth 能力与既有 Controller 用法)再落地,不发明新机制。
**验收**:新增单测(销售回填主部门池/主部门无池拒绝/管理员可建未分发/公海视图含已领取跟进中行)+ `mvn test`(crm-lead)全绿 + BOM 扫描。
## Answer
### P0-6 公海混合展示
- `LeadConstants` 新增常量 `PUBLIC_POOL_VISIBLE_STATUSES = List.of(STATUS_PENDING, STATUS_CLAIMED, STATUS_FOLLOWING)`(参数化缝——后续可按池细分或收窄,改常量不改代码)。
- `LeadViewQueryImpl` PUBLIC_POOL 分支从 `wrapper.eq(Lead::getStatus, STATUS_PENDING)` 改为 `wrapper.in(Lead::getStatus, LeadConstants.PUBLIC_POOL_VISIBLE_STATUSES)`
- 新增 `LeadViewQueryImplTest.pageLeads_publicPoolView_usesInClause`——验证 wrapper SQL 含 IN 子句。
### P0-7 新增线索归属约束
- `SecurityUtils` 新增 `hasRole(String roleCode)` 方法——遍历 `Authentication.getAuthorities()` 匹配角色码,不硬编码角色名;调用方传 `AuthConstants.ROLE_CODE_ADMIN`
- `ILeadService` 新增 4 参 `createLead(dto, claimOnCreate, followOnCreate, Long assignToUserId)` 抽象方法;3 参版本改为 default 委托(assignToUserId=null)。
- `LeadServiceImpl.createLead` 重构为 4 分支:
1. **非管理员**:`ownerSnapshotResolver.of(userId)` 取主部门 → `getPoolByDept(deptId)` 强制回填池(忽略前端 poolId);无池→65001 拒;claimOnCreate 时校验 claim_rule + 额度,设置 CLAIMED;否则 PENDING。
2. **管理员 + assignToUserId != null**(选人即分配):`getPoolByIdOrThrow(poolId)` 取池 → `ownerSnapshotResolver.of(assignToUserId)` → 额度校验归属=被指派人 → CLAIMED + owner=被指派人。
3. **管理员 + poolId=null**:建未分发(UNDISTRIBUTED)。
4. **管理员 + poolId 指定(不选人)**:`getPoolByIdOrThrow(poolId)` 取池 → claimOnCreate 时校验 claim_rule + 额度。
- `LeadController.create` 端点加 `@RequestParam(value = "assignToUserId", required = false) Long assignToUserId` 参数。
### 单测(LeadServiceImplTest +3, LeadViewQueryImplTest +1)
- `createLead_salesUser_deptNoPool_rejected`——主部门无池→65001 拒、不写库。
- `createLead_adminNoPool_undistributed`——管理员不指定池→UNDISTRIBUTED。
- `createLead_adminAssignToUser_claimedOnCreate`——选人即分配→CLAIMED + owner=被指派人 + 额度归属=被指派人 + ADMIN_ONLY 池不阻拦管理员分配。
- `pageLeads_publicPoolView_usesInClause`——PUBLIC_POOL 视图 wrapper 用 IN 子句(非 eq 单值)。
- 修复 4 个既有 createLead 测试(followOnCreate/noFollow/adminOnlyPool_rejected/limitExceeded)——适配非管理员路径的 `stubUserLookup` + `getPoolByDept` stub。
### 验证
- BOM 扫描:8 个修改文件全部无 BOM。
- `mvn test -pl crm-base,crm-lead -am`:86 tests, 0 failures, 0 errors, BUILD SUCCESS。