package com.crm.lead.controller; import com.crm.base.domain.result.PageResult; import com.crm.base.domain.result.Result; import com.crm.base.domain.result.BatchResult; import com.crm.lead.domain.dto.LeadDTO; import com.crm.lead.domain.dto.LeadBatchFailItem; import com.crm.lead.domain.dto.LeadFeedbackDTO; import com.crm.lead.domain.dto.LeadHistoryDTO; import com.crm.lead.domain.dto.LeadStatsDTO; import com.crm.lead.domain.param.ConvertOpportunityParam; import com.crm.lead.domain.param.LeadPageParam; import com.crm.lead.service.ILeadService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * 线索管理接口(薄适配层) *

权限策略:不种子化 button 权限点(用户决策),ApiPermissionInterceptor 对未注册 URL fail-open。 * 数据隔离由 {@code @DataScope(module="lead", ownerColumn="owner_user_id", deptColumn="dept_id")} 自动注入。

*

tags 实挂原则(ADR-0024 修订):每个接口只挂有真实页面入口的菜单 tag(真相源 = 原型逐页交互矩阵, * .scratch/lead-docs-audit/research/02-page-endpoint-matrix.md),新增接口时按页面入口实挂,禁止全量四菜单。

*/ @RestController @RequestMapping("/api/lead") @RequiredArgsConstructor public class LeadController { private final ILeadService leadService; // ==================== 查询 ==================== @Operation(summary = "线索分页(四视图:PUBLIC_POOL/MY_LEAD/MY_FOLLOW/MANAGE)", tags = {"线索管理/线索公海", "线索管理/我的线索", "线索管理/我的关注", "线索管理/线索管理"}) @PostMapping("/page") public Result> page(LeadPageParam param) { return Result.success(leadService.pageLeads(param)); } @Operation(summary = "线索详情", tags = {"线索管理/线索公海", "线索管理/我的线索", "线索管理/我的关注", "线索管理/线索管理"}) @GetMapping("/detail") public Result detail(@RequestParam("id") Long id) { return Result.success(leadService.getLeadDetail(id)); } @Operation(summary = "历史记录(只展示 5 种:领取/释放/反馈/转商机/编辑)", tags = {"线索管理/线索公海", "线索管理/我的线索", "线索管理/我的关注", "线索管理/线索管理"}) @GetMapping("/history") public Result> history(@RequestParam("leadId") Long leadId) { return Result.success(leadService.listHistory(leadId)); } // ==================== 创建/编辑/删除 ==================== @Operation(summary = "新建线索(claimOnCreate=true 销售新增即领取;followOnCreate=true 创建即关注,原型「是否领取/是否关注」复选)", tags = {"线索管理/我的线索", "线索管理/线索管理"}) @PostMapping("/create") public Result create(LeadDTO dto, @RequestParam(value = "claimOnCreate", defaultValue = "false") boolean claimOnCreate, @RequestParam(value = "followOnCreate", defaultValue = "false") boolean followOnCreate) { return Result.success(leadService.createLead(dto, claimOnCreate, followOnCreate)); } @Operation(summary = "编辑线索(非终态可编辑)", tags = {"线索管理/我的线索"}) @PostMapping("/edit") public Result edit(LeadDTO dto) { leadService.editLead(dto); return Result.success(); } @Operation(summary = "删除线索(已转商机禁止删除)", tags = {"线索管理/我的线索", "线索管理/线索管理"}) @PostMapping("/delete") public Result delete(@RequestParam("id") Long id) { leadService.deleteLead(id); return Result.success(); } // ==================== 流转操作 ==================== @Operation(summary = "领取线索(待领取→已领取)", tags = {"线索管理/线索公海", "线索管理/我的关注"}) @PostMapping("/claim") public Result claim(@RequestParam("id") Long id) { leadService.claimLead(id); return Result.success(); } @Operation(summary = "分配线索到池(未分发→待领取)", tags = {"线索管理/线索管理"}) @PostMapping("/assign-pool") public Result assignToPool(@RequestParam("id") Long id, @RequestParam("poolId") Long poolId) { leadService.assignToPool(id, poolId); return Result.success(); } @Operation(summary = "分配线索到销售(未分发/待领取→已领取 or 作废自环)", tags = {"线索管理/线索管理"}) @PostMapping("/assign-user") public Result assignToUser(@RequestParam("id") Long id, @RequestParam("userId") Long userId) { leadService.assignToUser(id, userId); return Result.success(); } @Operation(summary = "保存反馈草稿(不改状态机)", tags = {"线索管理/我的线索"}) @PostMapping("/feedback-draft") public Result feedbackDraft(LeadFeedbackDTO dto) { leadService.saveFeedbackDraft(dto); return Result.success(); } @Operation(summary = "提交反馈(已领取→跟进中 or →作废)", tags = {"线索管理/我的线索"}) @PostMapping("/feedback-submit") public Result feedbackSubmit(LeadFeedbackDTO dto) { leadService.submitFeedback(dto); return Result.success(); } @Operation(summary = "转商机(已领取/跟进中→已转商机)", tags = {"线索管理/我的线索"}) @PostMapping("/convert") public Result convert(@RequestParam("id") Long id, ConvertOpportunityParam param) { leadService.convertToOpportunity(id, param); return Result.success(); } @Operation(summary = "释放线索(已领取/跟进中→待领取)", tags = {"线索管理/线索公海", "线索管理/我的线索"}) @PostMapping("/release") public Result release(@RequestParam("id") Long id) { leadService.releaseLead(id); return Result.success(); } @Operation(summary = "激活线索(过期失效→恢复失效前态)", tags = {"线索管理/线索管理"}) @PostMapping("/activate") public Result activate(@RequestParam("id") Long id) { leadService.activateLead(id); return Result.success(); } // ==================== 关注 ==================== @Operation(summary = "关注线索(公海页,任意状态可关注)", tags = {"线索管理/线索公海"}) @PostMapping("/follow") public Result follow(@RequestParam("id") Long id) { leadService.followLead(id); return Result.success(); } @Operation(summary = "取消关注(公海详情弹窗/我的关注操作列)", tags = {"线索管理/线索公海", "线索管理/我的关注"}) @PostMapping("/unfollow") public Result unfollow(@RequestParam("id") Long id) { leadService.unfollowLead(id); return Result.success(); } @Operation(summary = "视图统计卡片(与 /page 同套筛选口径,口径随视图)", tags = {"线索管理/线索公海", "线索管理/我的线索", "线索管理/我的关注", "线索管理/线索管理"}) @PostMapping("/stats") public Result stats(LeadPageParam param) { return Result.success(leadService.countStats(param)); } // ==================== 批量操作(非原子、逐条 CAS、部分成功,ADR-0023) ==================== @Operation(summary = "批量领取", tags = {"线索管理/线索公海", "线索管理/我的关注"}) @PostMapping("/claim-batch") public Result> claimBatch(@RequestParam("ids") List ids) { return Result.success(leadService.claimBatch(ids)); } @Operation(summary = "批量分配到池(未分发→待领取)", tags = {"线索管理/线索管理"}) @PostMapping("/assign-pool-batch") public Result> assignToPoolBatch(@RequestParam("ids") List ids, @RequestParam("poolId") Long poolId) { return Result.success(leadService.assignToPoolBatch(ids, poolId)); } @Operation(summary = "批量分配到销售(统一分配给同一销售)", tags = {"线索管理/线索管理"}) @PostMapping("/assign-user-batch") public Result> assignToUserBatch(@RequestParam("ids") List ids, @RequestParam("userId") Long userId) { return Result.success(leadService.assignToUserBatch(ids, userId)); } @Operation(summary = "批量释放(已领取/跟进中→待领取)", tags = {"线索管理/我的线索"}) @PostMapping("/release-batch") public Result> releaseBatch(@RequestParam("ids") List ids) { return Result.success(leadService.releaseBatch(ids)); } @Operation(summary = "批量激活(过期失效→恢复失效前态)", tags = {"线索管理/线索管理"}) @PostMapping("/activate-batch") public Result> activateBatch(@RequestParam("ids") List ids) { return Result.success(leadService.activateBatch(ids)); } @Operation(summary = "批量删除(已转商机禁止删除,记失败)", tags = {"线索管理/线索管理"}) @PostMapping("/delete-batch") public Result> deleteBatch(@RequestParam("ids") List ids) { return Result.success(leadService.deleteBatch(ids)); } @Operation(summary = "批量关注(公海页勾选任意状态执行,逐条幂等)", tags = {"线索管理/线索公海"}) @PostMapping("/follow-batch") public Result> followBatch(@RequestParam("ids") List ids) { return Result.success(leadService.followBatch(ids)); } @Operation(summary = "批量取关(我的关注页勾选执行,幂等)", tags = {"线索管理/我的关注"}) @PostMapping("/unfollow-batch") public Result> unfollowBatch(@RequestParam("ids") List ids) { return Result.success(leadService.unfollowBatch(ids)); } }