Browse Source
- 票01: 修 W-02 NOT NULL 字段默认值 + 补 GET /detail + POST /edit - 票02: 状态机 11 个 HTTP 接口(claim/assign/pause/resume/release-pool/close/reopen/handover) - 票03: 详情 Tab 6 子域接口(客户/跟进/勘察/附件/团队/日志) - 票04: 协作+看板+访问上报 6 个接口 - 票05: 规则族 /versions 端点(阶段模板/方案卡模板/公海规则) - 票06: 回归验证 33 条接口全通master
21 changed files with 1027 additions and 19 deletions
@ -0,0 +1,84 @@ |
|||||
|
package com.crm.opportunity.controller; |
||||
|
|
||||
|
import com.crm.base.domain.result.Result; |
||||
|
import com.crm.base.security.SecurityUtils; |
||||
|
import com.crm.opportunity.service.IOpportunityCollabService; |
||||
|
import io.swagger.v3.oas.annotations.Operation; |
||||
|
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
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; |
||||
|
|
||||
|
/** |
||||
|
* 商机协作接口:关注/取关/访问上报/看板(票 04 整改,薄适配层)。 |
||||
|
*/ |
||||
|
@Tag(name = "商机管理/协作") |
||||
|
@RestController |
||||
|
@RequestMapping("/api/opportunity") |
||||
|
@RequiredArgsConstructor |
||||
|
public class OpportunityCollabController { |
||||
|
|
||||
|
private final IOpportunityCollabService collabService; |
||||
|
|
||||
|
// ==================== 关注 ====================
|
||||
|
|
||||
|
@Operation(summary = "关注商机") |
||||
|
@PostMapping("/focus") |
||||
|
public Result<Void> focus(@RequestParam("oppId") Long oppId) { |
||||
|
Long userId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
collabService.focus(oppId, userId); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "取关商机") |
||||
|
@PostMapping("/unfocus") |
||||
|
public Result<Void> unfocus(@RequestParam("oppId") Long oppId) { |
||||
|
Long userId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
collabService.unfocus(oppId, userId); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "批量关注") |
||||
|
@PostMapping("/focus-batch") |
||||
|
public Result<Void> focusBatch(@RequestParam("ids") List<Long> ids) { |
||||
|
Long userId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
for (Long id : ids) { |
||||
|
collabService.focus(id, userId); |
||||
|
} |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
// ==================== 访问上报 ====================
|
||||
|
|
||||
|
@Operation(summary = "上报商机访问(打开详情时调用)") |
||||
|
@PostMapping("/view-touch") |
||||
|
public Result<Void> viewTouch(@RequestParam("oppId") Long oppId) { |
||||
|
Long userId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
collabService.touch(oppId, userId); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
// ==================== 看板 ====================
|
||||
|
|
||||
|
@Operation(summary = "看板各阶段数量+金额汇总") |
||||
|
@PostMapping("/board/stage-summary") |
||||
|
public Result<?> boardStageSummary( |
||||
|
@RequestParam(value = "stageTemplateId", required = false) Long stageTemplateId) { |
||||
|
Long userId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
return Result.success(collabService.boardStageSummary(stageTemplateId, userId)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "看板单阶段卡片列表") |
||||
|
@PostMapping("/board/cards") |
||||
|
public Result<?> boardCards( |
||||
|
@RequestParam("stageId") Long stageId, |
||||
|
@RequestParam(value = "offset", defaultValue = "0") int offset, |
||||
|
@RequestParam(value = "limit", defaultValue = "20") int limit) { |
||||
|
Long userId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
return Result.success(collabService.boardCards(stageId, offset, limit, userId)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
package com.crm.opportunity.controller; |
||||
|
|
||||
|
import com.crm.base.domain.result.Result; |
||||
|
import com.crm.opportunity.domain.dto.OpportunityDTO; |
||||
|
import com.crm.opportunity.service.IOpportunityDetailService; |
||||
|
import io.swagger.v3.oas.annotations.Operation; |
||||
|
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
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; |
||||
|
|
||||
|
/** |
||||
|
* 商机详情 & 编辑接口(薄适配层,票 01 整改)。 |
||||
|
* |
||||
|
* <p>路由风格与线索模块对齐:全 GET/POST,动词后缀,id 走 @RequestParam。</p> |
||||
|
*/ |
||||
|
@Tag(name = "商机管理/商机详情") |
||||
|
@RestController |
||||
|
@RequestMapping("/api/opportunity") |
||||
|
@RequiredArgsConstructor |
||||
|
public class OpportunityDetailController { |
||||
|
|
||||
|
private final IOpportunityDetailService detailService; |
||||
|
|
||||
|
@Operation(summary = "商机详情") |
||||
|
@GetMapping("/detail") |
||||
|
public Result<OpportunityDTO> detail(@RequestParam("id") Long id) { |
||||
|
return Result.success(detailService.getDetail(id)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "编辑商机(非终态可编辑;全量字段覆盖)") |
||||
|
@PostMapping("/edit") |
||||
|
public Result<Void> edit(OpportunityDTO dto) { |
||||
|
detailService.edit(dto); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,185 @@ |
|||||
|
package com.crm.opportunity.controller; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.crm.base.domain.result.Result; |
||||
|
import com.crm.base.security.SecurityUtils; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityAttachment; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityCustomer; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityFollow; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityOplog; |
||||
|
import com.crm.opportunity.domain.entity.OpportunitySiteSurvey; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityTeam; |
||||
|
import com.crm.opportunity.service.IOpportunitySubService; |
||||
|
import io.swagger.v3.oas.annotations.Operation; |
||||
|
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.format.annotation.DateTimeFormat; |
||||
|
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.time.LocalDate; |
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 商机详情 Tab 子域接口(票 03 整改,薄适配层)。 |
||||
|
* |
||||
|
* <p>六个子域:客户/跟进/勘察/附件/团队/日志。路由风格同项目其他 Controller: |
||||
|
* GET 查询,POST 写操作,id 走 @RequestParam,分页走 POST /page。</p> |
||||
|
*/ |
||||
|
@Tag(name = "商机管理/详情子域") |
||||
|
@RestController |
||||
|
@RequestMapping("/api/opportunity") |
||||
|
@RequiredArgsConstructor |
||||
|
public class OpportunitySubController { |
||||
|
|
||||
|
private final IOpportunitySubService subService; |
||||
|
|
||||
|
// ==================== 客户 Tab ====================
|
||||
|
|
||||
|
@Operation(summary = "客户列表") |
||||
|
@GetMapping("/customer/list") |
||||
|
public Result<List<OpportunityCustomer>> listCustomers(@RequestParam("oppId") Long oppId) { |
||||
|
return Result.success(subService.listCustomers(oppId)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 跟进 Tab ====================
|
||||
|
|
||||
|
@Operation(summary = "跟进记录分页") |
||||
|
@PostMapping("/follow/page") |
||||
|
public Result<Page<OpportunityFollow>> pageFollows( |
||||
|
@RequestParam("oppId") Long oppId, |
||||
|
@RequestParam(value = "pageNum", defaultValue = "1") long pageNum, |
||||
|
@RequestParam(value = "pageSize", defaultValue = "10") long pageSize) { |
||||
|
return Result.success(subService.pageFollows(oppId, pageNum, pageSize)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "新增跟进记录") |
||||
|
@PostMapping("/follow/add") |
||||
|
public Result<Long> addFollow( |
||||
|
@RequestParam("oppId") Long oppId, |
||||
|
@RequestParam("followContent") String followContent, |
||||
|
@RequestParam("followTime") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime followTime, |
||||
|
@RequestParam("followWay") String followWay, |
||||
|
@RequestParam("customerId") Long customerId, |
||||
|
@RequestParam(value = "contactId", required = false) Long contactId, |
||||
|
@RequestParam(value = "resultTag", required = false) String resultTag, |
||||
|
@RequestParam(value = "nextFollowTime", required = false) |
||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime nextFollowTime) { |
||||
|
OpportunityFollow follow = new OpportunityFollow(); |
||||
|
follow.setOppId(oppId); |
||||
|
follow.setFollowContent(followContent); |
||||
|
follow.setFollowTime(followTime); |
||||
|
follow.setFollowWay(followWay); |
||||
|
follow.setCustomerId(customerId); |
||||
|
follow.setContactId(contactId); |
||||
|
follow.setResultTag(resultTag); |
||||
|
follow.setNextFollowTime(nextFollowTime); |
||||
|
return Result.success(subService.addFollow(follow)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "删除跟进记录") |
||||
|
@PostMapping("/follow/delete") |
||||
|
public Result<Void> deleteFollow(@RequestParam("id") Long id) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
subService.deleteFollow(id, operatorId); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
// ==================== 勘察 Tab ====================
|
||||
|
|
||||
|
@Operation(summary = "勘察记录分页") |
||||
|
@PostMapping("/site-survey/page") |
||||
|
public Result<Page<OpportunitySiteSurvey>> pageSiteSurveys( |
||||
|
@RequestParam("oppId") Long oppId, |
||||
|
@RequestParam(value = "pageNum", defaultValue = "1") long pageNum, |
||||
|
@RequestParam(value = "pageSize", defaultValue = "10") long pageSize) { |
||||
|
return Result.success(subService.pageSiteSurveys(oppId, pageNum, pageSize)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "新增勘察记录") |
||||
|
@PostMapping("/site-survey/add") |
||||
|
public Result<Long> addSiteSurvey( |
||||
|
@RequestParam("oppId") Long oppId, |
||||
|
@RequestParam("surveyDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate surveyDate, |
||||
|
@RequestParam("engineerUserId") Long engineerUserId, |
||||
|
@RequestParam("applyWay") String applyWay, |
||||
|
@RequestParam("surveySeq") String surveySeq, |
||||
|
@RequestParam(value = "applyNo", required = false) String applyNo, |
||||
|
@RequestParam(value = "surveyDesc", required = false) String surveyDesc) { |
||||
|
OpportunitySiteSurvey survey = new OpportunitySiteSurvey(); |
||||
|
survey.setOppId(oppId); |
||||
|
survey.setSurveyDate(surveyDate); |
||||
|
survey.setEngineerUserId(engineerUserId); |
||||
|
survey.setApplyWay(applyWay); |
||||
|
survey.setSurveySeq(surveySeq); |
||||
|
survey.setApplyNo(applyNo); |
||||
|
survey.setSurveyDesc(surveyDesc); |
||||
|
return Result.success(subService.addSiteSurvey(survey)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "删除勘察记录") |
||||
|
@PostMapping("/site-survey/delete") |
||||
|
public Result<Void> deleteSiteSurvey(@RequestParam("id") Long id) { |
||||
|
subService.deleteSiteSurvey(id); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
// ==================== 附件 Tab ====================
|
||||
|
|
||||
|
@Operation(summary = "附件列表") |
||||
|
@GetMapping("/attachment/list") |
||||
|
public Result<List<OpportunityAttachment>> listAttachments( |
||||
|
@RequestParam("oppId") Long oppId, |
||||
|
@RequestParam(value = "bizType", required = false) String bizType) { |
||||
|
return Result.success(subService.listAttachments(oppId, bizType)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "新增附件") |
||||
|
@PostMapping("/attachment/add") |
||||
|
public Result<Long> addAttachment( |
||||
|
@RequestParam("oppId") Long oppId, |
||||
|
@RequestParam("fileId") Long fileId, |
||||
|
@RequestParam(value = "bizType", required = false) String bizType, |
||||
|
@RequestParam(value = "bizId", required = false) Long bizId, |
||||
|
@RequestParam(value = "materialType", required = false) String materialType, |
||||
|
@RequestParam(value = "remark", required = false) String remark) { |
||||
|
OpportunityAttachment att = new OpportunityAttachment(); |
||||
|
att.setOppId(oppId); |
||||
|
att.setFileId(fileId); |
||||
|
att.setBizType(bizType); |
||||
|
att.setBizId(bizId); |
||||
|
att.setMaterialType(materialType); |
||||
|
att.setRemark(remark); |
||||
|
return Result.success(subService.addAttachment(att)); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "删除附件") |
||||
|
@PostMapping("/attachment/delete") |
||||
|
public Result<Void> deleteAttachment(@RequestParam("id") Long id) { |
||||
|
subService.deleteAttachment(id); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
// ==================== 团队 Tab ====================
|
||||
|
|
||||
|
@Operation(summary = "团队成员列表") |
||||
|
@GetMapping("/team/list") |
||||
|
public Result<List<OpportunityTeam>> listTeamMembers(@RequestParam("oppId") Long oppId) { |
||||
|
return Result.success(subService.listTeamMembers(oppId)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 日志 Tab ====================
|
||||
|
|
||||
|
@Operation(summary = "操作日志分页") |
||||
|
@PostMapping("/oplog/page") |
||||
|
public Result<Page<OpportunityOplog>> pageOplogs( |
||||
|
@RequestParam("oppId") Long oppId, |
||||
|
@RequestParam(value = "pageNum", defaultValue = "1") long pageNum, |
||||
|
@RequestParam(value = "pageSize", defaultValue = "10") long pageSize) { |
||||
|
return Result.success(subService.pageOplogs(oppId, pageNum, pageSize)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,167 @@ |
|||||
|
package com.crm.opportunity.controller; |
||||
|
|
||||
|
import com.crm.base.domain.result.Result; |
||||
|
import com.crm.base.security.SecurityUtils; |
||||
|
import com.crm.opportunity.state.OpportunityTransition; |
||||
|
import com.crm.opportunity.state.OpportunityTransitionCmd; |
||||
|
import io.swagger.v3.oas.annotations.Operation; |
||||
|
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.format.annotation.DateTimeFormat; |
||||
|
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.time.LocalDate; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 商机状态机 HTTP 入口(票 02 整改,薄适配层)。 |
||||
|
* |
||||
|
* <p>路由风格与线索模块对齐:全 POST,动词路径,id 走 @RequestParam。 |
||||
|
* Controller 只做:取当前用户 + 组装 Cmd + 调 {@link OpportunityTransition#execute}。 |
||||
|
* 快照字段(ownerNameSnapshot/ownerDeptId)由前端传入或由上层服务预查,本期从请求参数取。</p> |
||||
|
*/ |
||||
|
@Tag(name = "商机管理/商机流转") |
||||
|
@RestController |
||||
|
@RequestMapping("/api/opportunity") |
||||
|
@RequiredArgsConstructor |
||||
|
public class OpportunityTransitionController { |
||||
|
|
||||
|
private final OpportunityTransition transition; |
||||
|
|
||||
|
@Operation(summary = "领取商机(待领取→推进中,E1)") |
||||
|
@PostMapping("/claim") |
||||
|
public Result<Void> claim( |
||||
|
@RequestParam("id") Long id, |
||||
|
@RequestParam(value = "ownerNameSnapshot", required = false) String ownerNameSnapshot, |
||||
|
@RequestParam(value = "ownerDeptId", required = false) Long ownerDeptId) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.ClaimCmd( |
||||
|
operatorId, ownerNameSnapshot, ownerDeptId)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "批量领取(待领取→推进中,E1)") |
||||
|
@PostMapping("/claim-batch") |
||||
|
public Result<Void> claimBatch( |
||||
|
@RequestParam("ids") List<Long> ids, |
||||
|
@RequestParam(value = "ownerNameSnapshot", required = false) String ownerNameSnapshot, |
||||
|
@RequestParam(value = "ownerDeptId", required = false) Long ownerDeptId) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
for (Long id : ids) { |
||||
|
transition.execute(id, new OpportunityTransitionCmd.ClaimCmd( |
||||
|
operatorId, ownerNameSnapshot, ownerDeptId)); |
||||
|
} |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "分配商机给指定销售(待领取→推进中,E2)") |
||||
|
@PostMapping("/assign") |
||||
|
public Result<Void> assign( |
||||
|
@RequestParam("id") Long id, |
||||
|
@RequestParam("userId") Long userId, |
||||
|
@RequestParam(value = "ownerNameSnapshot", required = false) String ownerNameSnapshot, |
||||
|
@RequestParam(value = "ownerDeptId", required = false) Long ownerDeptId) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.AssignToUserCmd( |
||||
|
operatorId, userId, ownerNameSnapshot, ownerDeptId)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "批量分配商机(待领取→推进中,E2)") |
||||
|
@PostMapping("/assign-batch") |
||||
|
public Result<Void> assignBatch( |
||||
|
@RequestParam("ids") List<Long> ids, |
||||
|
@RequestParam("userId") Long userId, |
||||
|
@RequestParam(value = "ownerNameSnapshot", required = false) String ownerNameSnapshot, |
||||
|
@RequestParam(value = "ownerDeptId", required = false) Long ownerDeptId) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
for (Long id : ids) { |
||||
|
transition.execute(id, new OpportunityTransitionCmd.AssignToUserCmd( |
||||
|
operatorId, userId, ownerNameSnapshot, ownerDeptId)); |
||||
|
} |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "抛公海(推进中→待领取,E3)") |
||||
|
@PostMapping("/release-pool") |
||||
|
public Result<Void> releasePool( |
||||
|
@RequestParam("id") Long id, |
||||
|
@RequestParam(value = "poolReason", required = false) String poolReason) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.ReleaseToPoolCmd(operatorId, poolReason)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "暂缓(推进中→暂缓中,E4)") |
||||
|
@PostMapping("/pause") |
||||
|
public Result<Void> pause( |
||||
|
@RequestParam("id") Long id, |
||||
|
@RequestParam(value = "pauseReason", required = false) String pauseReason, |
||||
|
@RequestParam(value = "pauseExpectedRestartDate", required = false) |
||||
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate pauseExpectedRestartDate, |
||||
|
@RequestParam(value = "pauseRemark", required = false) String pauseRemark) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.PauseCmd( |
||||
|
operatorId, pauseReason, pauseExpectedRestartDate, pauseRemark)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "取消暂缓(暂缓中→推进中,E5)") |
||||
|
@PostMapping("/resume") |
||||
|
public Result<Void> resume(@RequestParam("id") Long id) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.ResumeCmd(operatorId)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "关闭商机(推进中/暂缓中→已关闭,E6)") |
||||
|
@PostMapping("/close") |
||||
|
public Result<Void> close( |
||||
|
@RequestParam("id") Long id, |
||||
|
@RequestParam(value = "closeReason", required = false) String closeReason, |
||||
|
@RequestParam(value = "closeRemark", required = false) String closeRemark) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.CloseCmd(operatorId, closeReason, closeRemark)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "批量关闭(推进中/暂缓中→已关闭,E6)") |
||||
|
@PostMapping("/close-batch") |
||||
|
public Result<Void> closeBatch( |
||||
|
@RequestParam("ids") List<Long> ids, |
||||
|
@RequestParam(value = "closeReason", required = false) String closeReason, |
||||
|
@RequestParam(value = "closeRemark", required = false) String closeRemark) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
for (Long id : ids) { |
||||
|
transition.execute(id, new OpportunityTransitionCmd.CloseCmd(operatorId, closeReason, closeRemark)); |
||||
|
} |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "重启商机(已关闭→推进中,E7)") |
||||
|
@PostMapping("/reopen") |
||||
|
public Result<Void> reopen( |
||||
|
@RequestParam("id") Long id, |
||||
|
@RequestParam(value = "reopenReason", required = false) String reopenReason) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.ReopenCmd(operatorId, reopenReason)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "移交商机(推进中/暂缓中自环换owner,E9)") |
||||
|
@PostMapping("/handover") |
||||
|
public Result<Void> handover( |
||||
|
@RequestParam("id") Long id, |
||||
|
@RequestParam("expectedOwnerUserId") Long expectedOwnerUserId, |
||||
|
@RequestParam("newOwnerUserId") Long newOwnerUserId, |
||||
|
@RequestParam(value = "ownerNameSnapshot", required = false) String ownerNameSnapshot, |
||||
|
@RequestParam(value = "ownerDeptId", required = false) Long ownerDeptId) { |
||||
|
Long operatorId = Long.valueOf(SecurityUtils.getRequiredUserId()); |
||||
|
transition.execute(id, new OpportunityTransitionCmd.HandoverCmd( |
||||
|
operatorId, expectedOwnerUserId, newOwnerUserId, ownerNameSnapshot, ownerDeptId)); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
package com.crm.opportunity.service; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* 商机协作服务:关注/取关/访问上报/看板(票 04 整改)。 |
||||
|
*/ |
||||
|
public interface IOpportunityCollabService { |
||||
|
|
||||
|
/** 关注(幂等:已关注则忽略) */ |
||||
|
void focus(Long oppId, Long userId); |
||||
|
|
||||
|
/** 取关(幂等:未关注则忽略) */ |
||||
|
void unfocus(Long oppId, Long userId); |
||||
|
|
||||
|
/** 访问上报(upsert last_view_time) */ |
||||
|
void touch(Long oppId, Long userId); |
||||
|
|
||||
|
/** 看板阶段汇总:List<{stageId, stageName, count, totalAmount}> */ |
||||
|
List<Map<String, Object>> boardStageSummary(Long stageTemplateId, Long userId); |
||||
|
|
||||
|
/** 看板单阶段卡片列表(分页 offset/limit) */ |
||||
|
List<Map<String, Object>> boardCards(Long stageId, int offset, int limit, Long userId); |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.crm.opportunity.service; |
||||
|
|
||||
|
import com.crm.opportunity.domain.dto.OpportunityDTO; |
||||
|
|
||||
|
/** |
||||
|
* 商机详情 & 编辑服务(票 01 整改)。 |
||||
|
*/ |
||||
|
public interface IOpportunityDetailService { |
||||
|
|
||||
|
/** |
||||
|
* 商机详情(GET /api/opportunity/detail?id=)。 |
||||
|
*/ |
||||
|
OpportunityDTO getDetail(Long id); |
||||
|
|
||||
|
/** |
||||
|
* 编辑商机(POST /api/opportunity/edit)。 |
||||
|
*/ |
||||
|
void edit(OpportunityDTO dto); |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
package com.crm.opportunity.service; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityAttachment; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityCustomer; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityFollow; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityOplog; |
||||
|
import com.crm.opportunity.domain.entity.OpportunitySiteSurvey; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityTeam; |
||||
|
|
||||
|
/** |
||||
|
* 商机详情 Tab 子域查询服务(票 03 整改)。 |
||||
|
* 六个子域:客户、跟进、勘察、附件、团队、日志。 |
||||
|
*/ |
||||
|
public interface IOpportunitySubService { |
||||
|
|
||||
|
/** 客户列表(全量,通常不多) */ |
||||
|
java.util.List<OpportunityCustomer> listCustomers(Long oppId); |
||||
|
|
||||
|
/** 跟进记录分页 */ |
||||
|
Page<OpportunityFollow> pageFollows(Long oppId, long pageNum, long pageSize); |
||||
|
|
||||
|
/** 新增跟进 */ |
||||
|
Long addFollow(OpportunityFollow follow); |
||||
|
|
||||
|
/** 删除跟进(软删) */ |
||||
|
void deleteFollow(Long followId, Long operatorId); |
||||
|
|
||||
|
/** 勘察记录分页 */ |
||||
|
Page<OpportunitySiteSurvey> pageSiteSurveys(Long oppId, long pageNum, long pageSize); |
||||
|
|
||||
|
/** 新增勘察记录 */ |
||||
|
Long addSiteSurvey(OpportunitySiteSurvey survey); |
||||
|
|
||||
|
/** 删除勘察记录(软删) */ |
||||
|
void deleteSiteSurvey(Long surveyId); |
||||
|
|
||||
|
/** 附件列表(按 oppId + 可选 bizType) */ |
||||
|
java.util.List<OpportunityAttachment> listAttachments(Long oppId, String bizType); |
||||
|
|
||||
|
/** 新增附件 */ |
||||
|
Long addAttachment(OpportunityAttachment attachment); |
||||
|
|
||||
|
/** 删除附件(软删) */ |
||||
|
void deleteAttachment(Long attachmentId); |
||||
|
|
||||
|
/** 团队成员列表(全量) */ |
||||
|
java.util.List<OpportunityTeam> listTeamMembers(Long oppId); |
||||
|
|
||||
|
/** 操作日志分页 */ |
||||
|
Page<OpportunityOplog> pageOplogs(Long oppId, long pageNum, long pageSize); |
||||
|
} |
||||
@ -0,0 +1,132 @@ |
|||||
|
package com.crm.opportunity.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
||||
|
import com.crm.opportunity.domain.entity.Opportunity; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityFocus; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityViewLog; |
||||
|
import com.crm.opportunity.mapper.OpportunityFocusMapper; |
||||
|
import com.crm.opportunity.mapper.OpportunityMapper; |
||||
|
import com.crm.opportunity.mapper.OpportunityViewLogMapper; |
||||
|
import com.crm.opportunity.service.IOpportunityCollabService; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* 商机协作服务实现(票 04 整改)。 |
||||
|
*/ |
||||
|
@Service |
||||
|
@RequiredArgsConstructor |
||||
|
public class OpportunityCollabServiceImpl implements IOpportunityCollabService { |
||||
|
|
||||
|
private final OpportunityFocusMapper focusMapper; |
||||
|
private final OpportunityViewLogMapper viewLogMapper; |
||||
|
private final OpportunityMapper oppMapper; |
||||
|
|
||||
|
// ==================== 关注 ====================
|
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void focus(Long oppId, Long userId) { |
||||
|
boolean exists = focusMapper.exists(new LambdaQueryWrapper<OpportunityFocus>() |
||||
|
.eq(OpportunityFocus::getOpportunityId, oppId) |
||||
|
.eq(OpportunityFocus::getUserId, userId)); |
||||
|
if (!exists) { |
||||
|
OpportunityFocus focus = new OpportunityFocus(); |
||||
|
focus.setOpportunityId(oppId); |
||||
|
focus.setUserId(userId); |
||||
|
focus.setFocusTime(LocalDateTime.now()); |
||||
|
focusMapper.insert(focus); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void unfocus(Long oppId, Long userId) { |
||||
|
focusMapper.delete(new LambdaQueryWrapper<OpportunityFocus>() |
||||
|
.eq(OpportunityFocus::getOpportunityId, oppId) |
||||
|
.eq(OpportunityFocus::getUserId, userId)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 访问上报 ====================
|
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void touch(Long oppId, Long userId) { |
||||
|
OpportunityViewLog existing = viewLogMapper.selectOne(new LambdaQueryWrapper<OpportunityViewLog>() |
||||
|
.eq(OpportunityViewLog::getOpportunityId, oppId) |
||||
|
.eq(OpportunityViewLog::getUserId, userId)); |
||||
|
if (existing != null) { |
||||
|
viewLogMapper.update(null, new LambdaUpdateWrapper<OpportunityViewLog>() |
||||
|
.eq(OpportunityViewLog::getId, existing.getId()) |
||||
|
.set(OpportunityViewLog::getLastViewTime, LocalDateTime.now())); |
||||
|
} else { |
||||
|
OpportunityViewLog log = new OpportunityViewLog(); |
||||
|
log.setOpportunityId(oppId); |
||||
|
log.setUserId(userId); |
||||
|
log.setLastViewTime(LocalDateTime.now()); |
||||
|
viewLogMapper.insert(log); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ==================== 看板 ====================
|
||||
|
|
||||
|
@Override |
||||
|
public List<Map<String, Object>> boardStageSummary(Long stageTemplateId, Long userId) { |
||||
|
// 按 current_stage_id 分组统计:数量 + 金额汇总
|
||||
|
// 简单实现:查所有推进中商机,按 stageId 聚合
|
||||
|
LambdaQueryWrapper<Opportunity> wrapper = new LambdaQueryWrapper<Opportunity>() |
||||
|
.eq(Opportunity::getOppStatus, 2); // 推进中
|
||||
|
if (stageTemplateId != null) { |
||||
|
wrapper.eq(Opportunity::getStageTemplateId, stageTemplateId); |
||||
|
} |
||||
|
List<Opportunity> opps = oppMapper.selectList(wrapper |
||||
|
.select(Opportunity::getCurrentStageId, Opportunity::getProjectAmount)); |
||||
|
|
||||
|
// 聚合
|
||||
|
Map<Long, Map<String, Object>> grouped = new java.util.LinkedHashMap<>(); |
||||
|
for (Opportunity opp : opps) { |
||||
|
Long stageId = opp.getCurrentStageId(); |
||||
|
grouped.computeIfAbsent(stageId, k -> { |
||||
|
Map<String, Object> m = new HashMap<>(); |
||||
|
m.put("stageId", k); |
||||
|
m.put("count", 0); |
||||
|
m.put("totalAmount", java.math.BigDecimal.ZERO); |
||||
|
return m; |
||||
|
}); |
||||
|
Map<String, Object> m = grouped.get(stageId); |
||||
|
m.put("count", (int) m.get("count") + 1); |
||||
|
if (opp.getProjectAmount() != null) { |
||||
|
m.put("totalAmount", ((java.math.BigDecimal) m.get("totalAmount")).add(opp.getProjectAmount())); |
||||
|
} |
||||
|
} |
||||
|
return new ArrayList<>(grouped.values()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<Map<String, Object>> boardCards(Long stageId, int offset, int limit, Long userId) { |
||||
|
List<Opportunity> opps = oppMapper.selectList(new LambdaQueryWrapper<Opportunity>() |
||||
|
.eq(Opportunity::getCurrentStageId, stageId) |
||||
|
.eq(Opportunity::getOppStatus, 2) |
||||
|
.last("LIMIT " + offset + "," + limit)); |
||||
|
List<Map<String, Object>> result = new ArrayList<>(); |
||||
|
for (Opportunity opp : opps) { |
||||
|
Map<String, Object> card = new HashMap<>(); |
||||
|
card.put("id", opp.getId()); |
||||
|
card.put("oppName", opp.getOppName()); |
||||
|
card.put("ownerUserId", opp.getOwnerUserId()); |
||||
|
card.put("ownerNameSnapshot", opp.getOwnerNameSnapshot()); |
||||
|
card.put("projectAmount", opp.getProjectAmount()); |
||||
|
card.put("currentStageId", opp.getCurrentStageId()); |
||||
|
result.add(card); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
package com.crm.opportunity.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
||||
|
import com.crm.base.domain.exception.BusinessErrorException; |
||||
|
import com.crm.opportunity.domain.dto.OpportunityDTO; |
||||
|
import com.crm.opportunity.domain.entity.Opportunity; |
||||
|
import com.crm.opportunity.mapper.OpportunityMapper; |
||||
|
import com.crm.opportunity.service.IOpportunityDetailService; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
/** |
||||
|
* 商机详情 & 编辑实现(票 01 整改,薄适配层)。 |
||||
|
*/ |
||||
|
@Service |
||||
|
@RequiredArgsConstructor |
||||
|
public class OpportunityDetailServiceImpl implements IOpportunityDetailService { |
||||
|
|
||||
|
private final OpportunityMapper oppMapper; |
||||
|
|
||||
|
@Override |
||||
|
public OpportunityDTO getDetail(Long id) { |
||||
|
Opportunity entity = oppMapper.selectById(id); |
||||
|
if (entity == null) { |
||||
|
throw new BusinessErrorException("商机不存在:id=" + id); |
||||
|
} |
||||
|
return OpportunityDTO.fromEntity(entity); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void edit(OpportunityDTO dto) { |
||||
|
if (dto.getId() == null) { |
||||
|
throw new BusinessErrorException("编辑商机时 id 必填"); |
||||
|
} |
||||
|
Opportunity existing = oppMapper.selectById(dto.getId()); |
||||
|
if (existing == null) { |
||||
|
throw new BusinessErrorException("商机不存在:id=" + dto.getId()); |
||||
|
} |
||||
|
// 用 LambdaUpdateWrapper 明确指定更新字段,绕开 MP 乘观锁自动处理的 MetaObject 问题
|
||||
|
LambdaUpdateWrapper<Opportunity> wrapper = new LambdaUpdateWrapper<Opportunity>() |
||||
|
.eq(Opportunity::getId, dto.getId()); |
||||
|
Opportunity update = new Opportunity(); |
||||
|
if (dto.getOppName() != null) update.setOppName(dto.getOppName()); |
||||
|
if (dto.getOppSource() != null) update.setOppSource(dto.getOppSource()); |
||||
|
if (dto.getOppType() != null) update.setOppType(dto.getOppType()); |
||||
|
if (dto.getIndustryCode() != null) update.setIndustryCode(dto.getIndustryCode()); |
||||
|
if (dto.getBidForm() != null) update.setBidForm(dto.getBidForm()); |
||||
|
if (dto.getLocalityType() != null) update.setLocalityType(dto.getLocalityType()); |
||||
|
if (dto.getPartyAClear() != null) update.setPartyAClear(dto.getPartyAClear()); |
||||
|
if (dto.getPartyA() != null) update.setPartyA(dto.getPartyA()); |
||||
|
if (dto.getProvinceCode() != null) update.setProvinceCode(dto.getProvinceCode()); |
||||
|
if (dto.getCityCode() != null) update.setCityCode(dto.getCityCode()); |
||||
|
if (dto.getRemark() != null) update.setRemark(dto.getRemark()); |
||||
|
if (dto.getProjectAmount() != null) update.setProjectAmount(dto.getProjectAmount()); |
||||
|
oppMapper.update(update, wrapper); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,169 @@ |
|||||
|
package com.crm.opportunity.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.crm.base.domain.exception.BusinessErrorException; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityAttachment; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityCustomer; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityFollow; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityOplog; |
||||
|
import com.crm.opportunity.domain.entity.OpportunitySiteSurvey; |
||||
|
import com.crm.opportunity.domain.entity.OpportunityTeam; |
||||
|
import com.crm.opportunity.mapper.OpportunityAttachmentMapper; |
||||
|
import com.crm.opportunity.mapper.OpportunityCustomerMapper; |
||||
|
import com.crm.opportunity.mapper.OpportunityFollowMapper; |
||||
|
import com.crm.opportunity.mapper.OpportunityOplogMapper; |
||||
|
import com.crm.opportunity.mapper.OpportunitySiteSurveyMapper; |
||||
|
import com.crm.opportunity.mapper.OpportunityTeamMapper; |
||||
|
import com.crm.opportunity.service.IOpportunitySubService; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
import org.springframework.util.StringUtils; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 商机详情 Tab 子域服务实现(票 03 整改)。 |
||||
|
*/ |
||||
|
@Service |
||||
|
@RequiredArgsConstructor |
||||
|
public class OpportunitySubServiceImpl implements IOpportunitySubService { |
||||
|
|
||||
|
private final OpportunityCustomerMapper customerMapper; |
||||
|
private final OpportunityFollowMapper followMapper; |
||||
|
private final OpportunitySiteSurveyMapper siteSurveyMapper; |
||||
|
private final OpportunityAttachmentMapper attachmentMapper; |
||||
|
private final OpportunityTeamMapper teamMapper; |
||||
|
private final OpportunityOplogMapper oplogMapper; |
||||
|
|
||||
|
// ==================== 客户 ====================
|
||||
|
|
||||
|
@Override |
||||
|
public List<OpportunityCustomer> listCustomers(Long oppId) { |
||||
|
return customerMapper.selectList(new LambdaQueryWrapper<OpportunityCustomer>() |
||||
|
.eq(OpportunityCustomer::getOpportunityId, oppId) |
||||
|
.eq(OpportunityCustomer::getDeleteKey, 0L) |
||||
|
.orderByDesc(OpportunityCustomer::getIsPrimaryIntended)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 跟进 ====================
|
||||
|
|
||||
|
@Override |
||||
|
public Page<OpportunityFollow> pageFollows(Long oppId, long pageNum, long pageSize) { |
||||
|
return followMapper.selectPage(new Page<>(pageNum, pageSize), |
||||
|
new LambdaQueryWrapper<OpportunityFollow>() |
||||
|
.eq(OpportunityFollow::getOppId, oppId) |
||||
|
.eq(OpportunityFollow::getDeleteKey, 0L) |
||||
|
.orderByDesc(OpportunityFollow::getFollowTime)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Long addFollow(OpportunityFollow follow) { |
||||
|
if (follow.getOppId() == null) throw new BusinessErrorException("oppId 必填"); |
||||
|
if (!StringUtils.hasText(follow.getFollowContent())) throw new BusinessErrorException("跟进内容必填"); |
||||
|
if (follow.getFollowTime() == null) throw new BusinessErrorException("跟进时间必填"); |
||||
|
if (!StringUtils.hasText(follow.getFollowWay())) throw new BusinessErrorException("跟进方式必填"); |
||||
|
if (follow.getCustomerId() == null) throw new BusinessErrorException("关联客户必填"); |
||||
|
follow.setDeleteKey(0L); |
||||
|
followMapper.insert(follow); |
||||
|
return follow.getId(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void deleteFollow(Long followId, Long operatorId) { |
||||
|
OpportunityFollow follow = followMapper.selectById(followId); |
||||
|
if (follow == null) throw new BusinessErrorException("跟进记录不存在"); |
||||
|
followMapper.update(null, new LambdaUpdateWrapper<OpportunityFollow>() |
||||
|
.eq(OpportunityFollow::getId, followId) |
||||
|
.set(OpportunityFollow::getDeleteKey, followId) |
||||
|
.set(OpportunityFollow::getDeleted, 1)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 勘察 ====================
|
||||
|
|
||||
|
@Override |
||||
|
public Page<OpportunitySiteSurvey> pageSiteSurveys(Long oppId, long pageNum, long pageSize) { |
||||
|
return siteSurveyMapper.selectPage(new Page<>(pageNum, pageSize), |
||||
|
new LambdaQueryWrapper<OpportunitySiteSurvey>() |
||||
|
.eq(OpportunitySiteSurvey::getOppId, oppId) |
||||
|
.orderByDesc(OpportunitySiteSurvey::getSurveyDate)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Long addSiteSurvey(OpportunitySiteSurvey survey) { |
||||
|
if (survey.getOppId() == null) throw new BusinessErrorException("oppId 必填"); |
||||
|
if (survey.getSurveyDate() == null) throw new BusinessErrorException("勘察日期必填"); |
||||
|
if (survey.getEngineerUserId() == null) throw new BusinessErrorException("工程师必填"); |
||||
|
siteSurveyMapper.insert(survey); |
||||
|
return survey.getId(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void deleteSiteSurvey(Long surveyId) { |
||||
|
OpportunitySiteSurvey survey = siteSurveyMapper.selectById(surveyId); |
||||
|
if (survey == null) throw new BusinessErrorException("勘察记录不存在"); |
||||
|
siteSurveyMapper.update(null, new LambdaUpdateWrapper<OpportunitySiteSurvey>() |
||||
|
.eq(OpportunitySiteSurvey::getId, surveyId) |
||||
|
.set(OpportunitySiteSurvey::getDeleted, 1)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 附件 ====================
|
||||
|
|
||||
|
@Override |
||||
|
public List<OpportunityAttachment> listAttachments(Long oppId, String bizType) { |
||||
|
LambdaQueryWrapper<OpportunityAttachment> wrapper = new LambdaQueryWrapper<OpportunityAttachment>() |
||||
|
.eq(OpportunityAttachment::getOppId, oppId) |
||||
|
.eq(OpportunityAttachment::getDeleteKey, 0L); |
||||
|
if (StringUtils.hasText(bizType)) { |
||||
|
wrapper.eq(OpportunityAttachment::getBizType, bizType); |
||||
|
} |
||||
|
return attachmentMapper.selectList(wrapper.orderByDesc(OpportunityAttachment::getCreateTime)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Long addAttachment(OpportunityAttachment attachment) { |
||||
|
if (attachment.getOppId() == null) throw new BusinessErrorException("oppId 必填"); |
||||
|
if (attachment.getFileId() == null) throw new BusinessErrorException("fileId 必填"); |
||||
|
attachment.setDeleteKey(0L); |
||||
|
attachmentMapper.insert(attachment); |
||||
|
return attachment.getId(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void deleteAttachment(Long attachmentId) { |
||||
|
OpportunityAttachment att = attachmentMapper.selectById(attachmentId); |
||||
|
if (att == null) throw new BusinessErrorException("附件不存在"); |
||||
|
attachmentMapper.update(null, new LambdaUpdateWrapper<OpportunityAttachment>() |
||||
|
.eq(OpportunityAttachment::getId, attachmentId) |
||||
|
.set(OpportunityAttachment::getDeleteKey, attachmentId) |
||||
|
.set(OpportunityAttachment::getDeleted, 1)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 团队 ====================
|
||||
|
|
||||
|
@Override |
||||
|
public List<OpportunityTeam> listTeamMembers(Long oppId) { |
||||
|
return teamMapper.selectList(new LambdaQueryWrapper<OpportunityTeam>() |
||||
|
.eq(OpportunityTeam::getOpportunityId, oppId) |
||||
|
.eq(OpportunityTeam::getDeleteKey, 0L) |
||||
|
.orderByAsc(OpportunityTeam::getCreateTime)); |
||||
|
} |
||||
|
|
||||
|
// ==================== 操作日志 ====================
|
||||
|
|
||||
|
@Override |
||||
|
public Page<OpportunityOplog> pageOplogs(Long oppId, long pageNum, long pageSize) { |
||||
|
return oplogMapper.selectPage(new Page<>(pageNum, pageSize), |
||||
|
new LambdaQueryWrapper<OpportunityOplog>() |
||||
|
.eq(OpportunityOplog::getOppId, oppId) |
||||
|
.orderByDesc(OpportunityOplog::getOpTime)); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue