From 9c929301b9f3806a4e4dfc4f60d5b41434835d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sun, 22 Mar 2026 22:32:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(credit):=20=E6=B7=BB=E5=8A=A0=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E8=B7=9F=E8=BF=9B=E4=B8=83=E6=AD=A5=E9=AA=A4=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在CreditMpCustomer实体类中添加七个跟进步骤相关字段 - 实现跟进步骤审核功能,包括单个和批量审核接口 - 添加待审核步骤查询接口和客户跟进统计功能 - 实现流程结束功能和详细的步骤状态管理 - 创建相应的DTO和VO数据传输对象 - 在Mapper层添加待审核步骤查询SQL实现 --- docs/ai/后端实现指南.md | 458 ++++++++++++++++++ .../CreditMpCustomerController.java | 49 ++ .../credit/entity/CreditMpCustomer.java | 155 ++++++ .../credit/mapper/CreditMpCustomerMapper.java | 10 + .../mapper/xml/CreditMpCustomerMapper.xml | 124 +++++ .../param/BatchFollowStepApprovalDTO.java | 24 + .../credit/param/EndFollowProcessDTO.java | 24 + .../credit/param/FollowStepApprovalDTO.java | 36 ++ .../credit/param/FollowStepQueryDTO.java | 29 ++ .../service/CreditMpCustomerService.java | 43 ++ .../impl/CreditMpCustomerServiceImpl.java | 212 ++++++++ .../credit/vo/FollowStatisticsDTO.java | 32 ++ .../credit/vo/FollowStepDetailDTO.java | 32 ++ .../credit/vo/PendingApprovalStepVO.java | 38 ++ 14 files changed, 1266 insertions(+) create mode 100644 docs/ai/后端实现指南.md create mode 100644 src/main/java/com/gxwebsoft/credit/param/BatchFollowStepApprovalDTO.java create mode 100644 src/main/java/com/gxwebsoft/credit/param/EndFollowProcessDTO.java create mode 100644 src/main/java/com/gxwebsoft/credit/param/FollowStepApprovalDTO.java create mode 100644 src/main/java/com/gxwebsoft/credit/param/FollowStepQueryDTO.java create mode 100644 src/main/java/com/gxwebsoft/credit/vo/FollowStatisticsDTO.java create mode 100644 src/main/java/com/gxwebsoft/credit/vo/FollowStepDetailDTO.java create mode 100644 src/main/java/com/gxwebsoft/credit/vo/PendingApprovalStepVO.java diff --git a/docs/ai/后端实现指南.md b/docs/ai/后端实现指南.md new file mode 100644 index 0000000..c4bde09 --- /dev/null +++ b/docs/ai/后端实现指南.md @@ -0,0 +1,458 @@ +# 客户跟进7步骤后端实现指南 + +## 📋 概述 + +本指南详细说明如何实现客户跟进7个步骤功能的后端代码,包括数据库设计、Java后端实现和API接口。 + +## 🗄️ 数据库设计 + +### 1. 修改 credit_mp_customer 表结构 + +```sql +-- 为第5-7步添加字段(第1-4步字段已存在) +-- 第5步:合同签订 +ALTER TABLE credit_mp_customer ADD COLUMN follow_step5_submitted TINYINT DEFAULT 0 COMMENT '是否已提交'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step5_submitted_at VARCHAR(255) COMMENT '提交时间'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step5_contracts TEXT COMMENT '合同信息JSON数组'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step5_need_approval TINYINT DEFAULT 1 COMMENT '是否需要审核'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step5_approved TINYINT DEFAULT 0 COMMENT '是否审核通过'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step5_approved_at VARCHAR(255) COMMENT '审核时间'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step5_approved_by BIGINT COMMENT '审核人ID'; + +-- 第6步:订单回款 +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_submitted TINYINT DEFAULT 0 COMMENT '是否已提交'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_submitted_at VARCHAR(255) COMMENT '提交时间'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_payment_records TEXT COMMENT '财务录入的回款记录JSON数组'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_expected_payments TEXT COMMENT '预计回款JSON数组'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_need_approval TINYINT DEFAULT 1 COMMENT '是否需要审核'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_approved TINYINT DEFAULT 0 COMMENT '是否审核通过'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_approved_at VARCHAR(255) COMMENT '审核时间'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step6_approved_by BIGINT COMMENT '审核人ID'; + +-- 第7步:电话回访 +ALTER TABLE credit_mp_customer ADD COLUMN follow_step7_submitted TINYINT DEFAULT 0 COMMENT '是否已提交'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step7_submitted_at VARCHAR(255) COMMENT '提交时间'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step7_visit_records TEXT COMMENT '回访记录JSON数组'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step7_need_approval TINYINT DEFAULT 1 COMMENT '是否需要审核'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step7_approved TINYINT DEFAULT 0 COMMENT '是否审核通过'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step7_approved_at VARCHAR(255) COMMENT '审核时间'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_step7_approved_by BIGINT COMMENT '审核人ID'; + +-- 添加流程结束相关字段 +ALTER TABLE credit_mp_customer ADD COLUMN follow_process_ended TINYINT DEFAULT 0 COMMENT '流程是否已结束'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_process_end_time VARCHAR(255) COMMENT '流程结束时间'; +ALTER TABLE credit_mp_customer ADD COLUMN follow_process_end_reason TEXT COMMENT '流程结束原因'; +``` + +### 2. 创建审核记录表(可选) + +```sql +CREATE TABLE credit_follow_approval ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + customer_id BIGINT NOT NULL COMMENT '客户ID', + step TINYINT NOT NULL COMMENT '步骤号', + approved TINYINT NOT NULL COMMENT '是否通过', + remark TEXT COMMENT '审核备注', + approved_by BIGINT COMMENT '审核人ID', + approved_at VARCHAR(255) COMMENT '审核时间', + created_at VARCHAR(255) COMMENT '创建时间', + INDEX idx_customer_step (customer_id, step), + INDEX idx_approved_by (approved_by) +) COMMENT='跟进步骤审核记录'; +``` + +## ☕ Java后端实现 + +### 1. 实体类修改 + +```java +// CreditMpCustomer.java 添加字段 +public class CreditMpCustomer { + // ... 现有字段 + + // 第5步字段 + private Integer followStep5Submitted; + private String followStep5SubmittedAt; + private String followStep5Contracts; + private Integer followStep5NeedApproval; + private Integer followStep5Approved; + private String followStep5ApprovedAt; + private Long followStep5ApprovedBy; + + // 第6步字段 + private Integer followStep6Submitted; + private String followStep6SubmittedAt; + private String followStep6PaymentRecords; + private String followStep6ExpectedPayments; + private Integer followStep6NeedApproval; + private Integer followStep6Approved; + private String followStep6ApprovedAt; + private Long followStep6ApprovedBy; + + // 第7步字段 + private Integer followStep7Submitted; + private String followStep7SubmittedAt; + private String followStep7VisitRecords; + private Integer followStep7NeedApproval; + private Integer followStep7Approved; + private String followStep7ApprovedAt; + private Long followStep7ApprovedBy; + + // 流程结束字段 + private Integer followProcessEnded; + private String followProcessEndTime; + private String followProcessEndReason; + + // getter/setter 方法... +} +``` + +### 2. DTO类创建 + +```java +// FollowStepApprovalDTO.java +@Data +public class FollowStepApprovalDTO { + private Long customerId; + private Integer step; + private Boolean approved; + private String remark; +} + +// BatchFollowStepApprovalDTO.java +@Data +public class BatchFollowStepApprovalDTO { + private List approvals; +} + +// FollowStatisticsDTO.java +@Data +public class FollowStatisticsDTO { + private Integer totalSteps; + private Integer completedSteps; + private Integer currentStep; + private Double progress; + private List stepDetails; +} + +// FollowStepDetailDTO.java +@Data +public class FollowStepDetailDTO { + private Integer step; + private String title; + private String status; // pending, submitted, approved, rejected + private String submittedAt; + private String approvedAt; +} +``` + +### 3. Service层实现 + +```java +// CreditMpCustomerServiceImpl.java 添加方法 + +@Service +public class CreditMpCustomerServiceImpl implements CreditMpCustomerService { + + /** + * 审核跟进步骤 + */ + @Override + @Transactional + public void approveFollowStep(FollowStepApprovalDTO dto) { + CreditMpCustomer customer = getById(dto.getCustomerId()); + if (customer == null) { + throw new ServiceException("客户不存在"); + } + + // 验证步骤是否已提交 + if (!isStepSubmitted(customer, dto.getStep())) { + throw new ServiceException("该步骤尚未提交,无法审核"); + } + + // 更新审核状态 + updateStepApproval(customer, dto); + + // 记录审核日志 + saveApprovalLog(dto); + + // 如果审核通过,更新客户步骤状态 + if (dto.getApproved()) { + updateCustomerStep(customer, dto.getStep()); + } + } + + /** + * 批量审核跟进步骤 + */ + @Override + @Transactional + public void batchApproveFollowSteps(BatchFollowStepApprovalDTO dto) { + for (FollowStepApprovalDTO approval : dto.getApprovals()) { + approveFollowStep(approval); + } + } + + /** + * 获取待审核的跟进步骤列表 + */ + @Override + public List getPendingApprovalSteps(FollowStepQueryDTO query) { + return baseMapper.selectPendingApprovalSteps(query); + } + + /** + * 获取客户跟进统计 + */ + @Override + public FollowStatisticsDTO getFollowStatistics(Long customerId) { + CreditMpCustomer customer = getById(customerId); + if (customer == null) { + throw new ServiceException("客户不存在"); + } + + FollowStatisticsDTO statistics = new FollowStatisticsDTO(); + statistics.setTotalSteps(7); + + List stepDetails = new ArrayList<>(); + int completedSteps = 0; + int currentStep = 1; + + for (int i = 1; i <= 7; i++) { + FollowStepDetailDTO detail = getStepDetail(customer, i); + stepDetails.add(detail); + + if ("approved".equals(detail.getStatus())) { + completedSteps++; + currentStep = i + 1; + } else if ("submitted".equals(detail.getStatus()) && currentStep == 1) { + currentStep = i; + } + } + + statistics.setCompletedSteps(completedSteps); + statistics.setCurrentStep(Math.min(currentStep, 7)); + statistics.setProgress((double) completedSteps / 7 * 100); + statistics.setStepDetails(stepDetails); + + return statistics; + } + + /** + * 结束客户跟进流程 + */ + @Override + @Transactional + public void endFollowProcess(Long customerId, String reason) { + CreditMpCustomer customer = getById(customerId); + if (customer == null) { + throw new ServiceException("客户不存在"); + } + + customer.setFollowProcessEnded(1); + customer.setFollowProcessEndTime(DateUtil.formatDateTime(new Date())); + customer.setFollowProcessEndReason(reason); + + updateById(customer); + } + + // 私有辅助方法... + private boolean isStepSubmitted(CreditMpCustomer customer, Integer step) { + switch (step) { + case 1: return customer.getFollowStep1Submitted() == 1; + case 2: return customer.getFollowStep2Submitted() == 1; + // ... 其他步骤 + case 7: return customer.getFollowStep7Submitted() == 1; + default: return false; + } + } + + private void updateStepApproval(CreditMpCustomer customer, FollowStepApprovalDTO dto) { + String currentTime = DateUtil.formatDateTime(new Date()); + Long currentUserId = SecurityFrameworkUtils.getLoginUserId(); + + switch (dto.getStep()) { + case 5: + customer.setFollowStep5Approved(dto.getApproved() ? 1 : 0); + customer.setFollowStep5ApprovedAt(currentTime); + customer.setFollowStep5ApprovedBy(currentUserId); + break; + case 6: + customer.setFollowStep6Approved(dto.getApproved() ? 1 : 0); + customer.setFollowStep6ApprovedAt(currentTime); + customer.setFollowStep6ApprovedBy(currentUserId); + break; + case 7: + customer.setFollowStep7Approved(dto.getApproved() ? 1 : 0); + customer.setFollowStep7ApprovedAt(currentTime); + customer.setFollowStep7ApprovedBy(currentUserId); + break; + } + + updateById(customer); + } +} +``` + +### 4. Controller层实现 + +```java +// CreditMpCustomerController.java 添加接口 + +@RestController +@RequestMapping("/credit/credit-mp-customer") +public class CreditMpCustomerController { + + @PostMapping("/approve-follow-step") + @OperLog(title = "审核跟进步骤", businessType = BusinessType.UPDATE) + public R approveFollowStep(@RequestBody FollowStepApprovalDTO dto) { + creditMpCustomerService.approveFollowStep(dto); + return R.ok(); + } + + @PostMapping("/batch-approve-follow-steps") + @OperLog(title = "批量审核跟进步骤", businessType = BusinessType.UPDATE) + public R batchApproveFollowSteps(@RequestBody BatchFollowStepApprovalDTO dto) { + creditMpCustomerService.batchApproveFollowSteps(dto); + return R.ok(); + } + + @GetMapping("/pending-approval-steps") + @OperLog(title = "获取待审核跟进步骤", businessType = BusinessType.SELECT) + public R> getPendingApprovalSteps(FollowStepQueryDTO query) { + List list = creditMpCustomerService.getPendingApprovalSteps(query); + return R.ok(list); + } + + @GetMapping("/follow-statistics/{customerId}") + @OperLog(title = "获取客户跟进统计", businessType = BusinessType.SELECT) + public R getFollowStatistics(@PathVariable Long customerId) { + FollowStatisticsDTO statistics = creditMpCustomerService.getFollowStatistics(customerId); + return R.ok(statistics); + } + + @PostMapping("/end-follow-process") + @OperLog(title = "结束客户跟进流程", businessType = BusinessType.UPDATE) + public R endFollowProcess(@RequestBody EndFollowProcessDTO dto) { + creditMpCustomerService.endFollowProcess(dto.getCustomerId(), dto.getReason()); + return R.ok(); + } +} +``` + +### 5. Mapper层SQL + +```xml + + + +``` + +## 🔧 业务逻辑说明 + +### 1. 步骤解锁机制 +- 第一步始终可用 +- 后续步骤需要前一步审核通过才能进行 +- 前端通过 `canEnterStep` 逻辑控制 + +### 2. 审核流程 +- 步骤提交后设置 `needApproval = 1` +- 管理员在后台审核 +- 审核通过后设置 `approved = 1` 并更新时间 + +### 3. 数据格式 +- 所有复杂数据使用JSON格式存储 +- 文件上传返回URL,存储在JSON数组中 +- 时间统一使用 `YYYY-MM-DD HH:mm:ss` 格式 + +### 4. 权限控制 +- 销售只能提交和查看自己的客户 +- 管理员可以审核所有步骤 +- 财务人员可以录入第6步回款数据 + +## 📱 前端集成 + +前端代码已经完成,包括: +- 7个步骤的完整页面 +- 步骤状态显示和跳转逻辑 +- 数据提交和验证 +- 客户详情页面的汇总显示 + +## 🚀 部署步骤 + +1. 执行数据库迁移脚本 +2. 部署Java后端代码 +3. 更新前端API调用 +4. 测试完整流程 +5. 配置权限和审核流程 + +## 📝 注意事项 + +1. **数据备份**:执行数据库变更前请备份 +2. **权限配置**:确保各角色权限正确配置 +3. **文件上传**:确认文件上传服务正常 +4. **审核流程**:测试审核流程的完整性 +5. **性能优化**:大量数据时考虑分页和索引优化 + +## 🔄 后续扩展 + +可以考虑的功能: +- 跟进模板和标准化流程 +- 自动提醒和通知 +- 数据统计和报表 +- 跟进效率分析 +- 客户满意度评估 diff --git a/src/main/java/com/gxwebsoft/credit/controller/CreditMpCustomerController.java b/src/main/java/com/gxwebsoft/credit/controller/CreditMpCustomerController.java index 1622727..f300ce9 100644 --- a/src/main/java/com/gxwebsoft/credit/controller/CreditMpCustomerController.java +++ b/src/main/java/com/gxwebsoft/credit/controller/CreditMpCustomerController.java @@ -4,6 +4,12 @@ import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.credit.service.CreditMpCustomerService; import com.gxwebsoft.credit.entity.CreditMpCustomer; import com.gxwebsoft.credit.param.CreditMpCustomerParam; +import com.gxwebsoft.credit.param.BatchFollowStepApprovalDTO; +import com.gxwebsoft.credit.param.EndFollowProcessDTO; +import com.gxwebsoft.credit.param.FollowStepApprovalDTO; +import com.gxwebsoft.credit.param.FollowStepQueryDTO; +import com.gxwebsoft.credit.vo.FollowStatisticsDTO; +import com.gxwebsoft.credit.vo.PendingApprovalStepVO; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageParam; @@ -126,4 +132,47 @@ public class CreditMpCustomerController extends BaseController { return fail("删除失败"); } + @PreAuthorize("hasAuthority('credit:creditMpCustomer:update')") + @OperationLog + @Operation(summary = "审核跟进步骤") + @PostMapping("/approve-follow-step") + public ApiResult approveFollowStep(@RequestBody FollowStepApprovalDTO dto) { + creditMpCustomerService.approveFollowStep(dto); + return success("审核成功"); + } + + @PreAuthorize("hasAuthority('credit:creditMpCustomer:update')") + @OperationLog + @Operation(summary = "批量审核跟进步骤") + @PostMapping("/batch-approve-follow-steps") + public ApiResult batchApproveFollowSteps(@RequestBody BatchFollowStepApprovalDTO dto) { + creditMpCustomerService.batchApproveFollowSteps(dto); + return success("批量审核成功"); + } + + @PreAuthorize("hasAuthority('credit:creditMpCustomer:list')") + @Operation(summary = "获取待审核的跟进步骤") + @GetMapping("/pending-approval-steps") + public ApiResult> getPendingApprovalSteps(FollowStepQueryDTO query) { + List list = creditMpCustomerService.getPendingApprovalSteps(query); + return success(list); + } + + @PreAuthorize("hasAuthority('credit:creditMpCustomer:list')") + @Operation(summary = "获取客户跟进统计") + @GetMapping("/follow-statistics/{customerId}") + public ApiResult getFollowStatistics(@PathVariable Long customerId) { + FollowStatisticsDTO statistics = creditMpCustomerService.getFollowStatistics(customerId); + return success(statistics); + } + + @PreAuthorize("hasAuthority('credit:creditMpCustomer:update')") + @OperationLog + @Operation(summary = "结束客户跟进流程") + @PostMapping("/end-follow-process") + public ApiResult endFollowProcess(@RequestBody EndFollowProcessDTO dto) { + creditMpCustomerService.endFollowProcess(dto); + return success("流程结束成功"); + } + } diff --git a/src/main/java/com/gxwebsoft/credit/entity/CreditMpCustomer.java b/src/main/java/com/gxwebsoft/credit/entity/CreditMpCustomer.java index 469fb9e..50eb843 100644 --- a/src/main/java/com/gxwebsoft/credit/entity/CreditMpCustomer.java +++ b/src/main/java/com/gxwebsoft/credit/entity/CreditMpCustomer.java @@ -110,4 +110,159 @@ public class CreditMpCustomer implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; + // 第1步:案件受理字段 + @Schema(description = "第1步是否已提交") + private Integer followStep1Submitted; + + @Schema(description = "第1步提交时间") + private String followStep1SubmittedAt; + + @Schema(description = "第1步是否需要审核") + private Integer followStep1NeedApproval; + + @Schema(description = "第1步是否审核通过") + private Integer followStep1Approved; + + @Schema(description = "第1步审核时间") + private String followStep1ApprovedAt; + + @Schema(description = "第1步审核人ID") + private Long followStep1ApprovedBy; + + // 第2步:材料准备字段 + @Schema(description = "第2步是否已提交") + private Integer followStep2Submitted; + + @Schema(description = "第2步提交时间") + private String followStep2SubmittedAt; + + @Schema(description = "第2步是否需要审核") + private Integer followStep2NeedApproval; + + @Schema(description = "第2步是否审核通过") + private Integer followStep2Approved; + + @Schema(description = "第2步审核时间") + private String followStep2ApprovedAt; + + @Schema(description = "第2步审核人ID") + private Long followStep2ApprovedBy; + + // 第3步:案件办理字段 + @Schema(description = "第3步是否已提交") + private Integer followStep3Submitted; + + @Schema(description = "第3步提交时间") + private String followStep3SubmittedAt; + + @Schema(description = "第3步是否需要审核") + private Integer followStep3NeedApproval; + + @Schema(description = "第3步是否审核通过") + private Integer followStep3Approved; + + @Schema(description = "第3步审核时间") + private String followStep3ApprovedAt; + + @Schema(description = "第3步审核人ID") + private Long followStep3ApprovedBy; + + // 第4步:送达签收字段 + @Schema(description = "第4步是否已提交") + private Integer followStep4Submitted; + + @Schema(description = "第4步提交时间") + private String followStep4SubmittedAt; + + @Schema(description = "第4步是否需要审核") + private Integer followStep4NeedApproval; + + @Schema(description = "第4步是否审核通过") + private Integer followStep4Approved; + + @Schema(description = "第4步审核时间") + private String followStep4ApprovedAt; + + @Schema(description = "第4步审核人ID") + private Long followStep4ApprovedBy; + + // 第5步:合同签订字段 + @Schema(description = "第5步是否已提交") + private Integer followStep5Submitted; + + @Schema(description = "第5步提交时间") + private String followStep5SubmittedAt; + + @Schema(description = "第5步合同信息JSON数组") + private String followStep5Contracts; + + @Schema(description = "第5步是否需要审核") + private Integer followStep5NeedApproval; + + @Schema(description = "第5步是否审核通过") + private Integer followStep5Approved; + + @Schema(description = "第5步审核时间") + private String followStep5ApprovedAt; + + @Schema(description = "第5步审核人ID") + private Long followStep5ApprovedBy; + + // 第6步:订单回款字段 + @Schema(description = "第6步是否已提交") + private Integer followStep6Submitted; + + @Schema(description = "第6步提交时间") + private String followStep6SubmittedAt; + + @Schema(description = "第6步财务录入的回款记录JSON数组") + private String followStep6PaymentRecords; + + @Schema(description = "第6步预计回款JSON数组") + private String followStep6ExpectedPayments; + + @Schema(description = "第6步是否需要审核") + private Integer followStep6NeedApproval; + + @Schema(description = "第6步是否审核通过") + private Integer followStep6Approved; + + @Schema(description = "第6步审核时间") + private String followStep6ApprovedAt; + + @Schema(description = "第6步审核人ID") + private Long followStep6ApprovedBy; + + // 第7步:电话回访字段 + @Schema(description = "第7步是否已提交") + private Integer followStep7Submitted; + + @Schema(description = "第7步提交时间") + private String followStep7SubmittedAt; + + @Schema(description = "第7步回访记录JSON数组") + private String followStep7VisitRecords; + + @Schema(description = "第7步是否需要审核") + private Integer followStep7NeedApproval; + + @Schema(description = "第7步是否审核通过") + private Integer followStep7Approved; + + @Schema(description = "第7步审核时间") + private String followStep7ApprovedAt; + + @Schema(description = "第7步审核人ID") + private Long followStep7ApprovedBy; + + // 流程结束字段 + @Schema(description = "流程是否已结束") + private Integer followProcessEnded; + + @Schema(description = "流程结束时间") + private String followProcessEndTime; + + @Schema(description = "流程结束原因") + private String followProcessEndReason; + } diff --git a/src/main/java/com/gxwebsoft/credit/mapper/CreditMpCustomerMapper.java b/src/main/java/com/gxwebsoft/credit/mapper/CreditMpCustomerMapper.java index 085e318..aa4d152 100644 --- a/src/main/java/com/gxwebsoft/credit/mapper/CreditMpCustomerMapper.java +++ b/src/main/java/com/gxwebsoft/credit/mapper/CreditMpCustomerMapper.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.gxwebsoft.credit.entity.CreditMpCustomer; import com.gxwebsoft.credit.param.CreditMpCustomerParam; +import com.gxwebsoft.credit.param.FollowStepQueryDTO; +import com.gxwebsoft.credit.vo.PendingApprovalStepVO; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -34,4 +36,12 @@ public interface CreditMpCustomerMapper extends BaseMapper { */ List selectListRel(@Param("param") CreditMpCustomerParam param); + /** + * 获取待审核的跟进步骤列表 + * + * @param query 查询参数 + * @return 待审核步骤列表 + */ + List selectPendingApprovalSteps(@Param("param") FollowStepQueryDTO query); + } diff --git a/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml b/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml index 29962b1..be99d3e 100644 --- a/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml +++ b/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml @@ -92,4 +92,128 @@ + + + diff --git a/src/main/java/com/gxwebsoft/credit/param/BatchFollowStepApprovalDTO.java b/src/main/java/com/gxwebsoft/credit/param/BatchFollowStepApprovalDTO.java new file mode 100644 index 0000000..13af663 --- /dev/null +++ b/src/main/java/com/gxwebsoft/credit/param/BatchFollowStepApprovalDTO.java @@ -0,0 +1,24 @@ +package com.gxwebsoft.credit.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 批量跟进步骤审核DTO + * + * @author 科技小王子 + * @since 2026-03-22 + */ +@Data +@Schema(name = "BatchFollowStepApprovalDTO对象", description = "批量跟进步骤审核DTO") +public class BatchFollowStepApprovalDTO { + + @Schema(description = "审核列表", required = true) + @NotNull(message = "审核列表不能为空") + @Valid + private List approvals; +} diff --git a/src/main/java/com/gxwebsoft/credit/param/EndFollowProcessDTO.java b/src/main/java/com/gxwebsoft/credit/param/EndFollowProcessDTO.java new file mode 100644 index 0000000..0510829 --- /dev/null +++ b/src/main/java/com/gxwebsoft/credit/param/EndFollowProcessDTO.java @@ -0,0 +1,24 @@ +package com.gxwebsoft.credit.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * 结束跟进流程DTO + * + * @author 科技小王子 + * @since 2026-03-22 + */ +@Data +@Schema(name = "EndFollowProcessDTO对象", description = "结束跟进流程DTO") +public class EndFollowProcessDTO { + + @Schema(description = "客户ID", required = true) + @NotNull(message = "客户ID不能为空") + private Long customerId; + + @Schema(description = "结束原因") + private String reason; +} diff --git a/src/main/java/com/gxwebsoft/credit/param/FollowStepApprovalDTO.java b/src/main/java/com/gxwebsoft/credit/param/FollowStepApprovalDTO.java new file mode 100644 index 0000000..be06a46 --- /dev/null +++ b/src/main/java/com/gxwebsoft/credit/param/FollowStepApprovalDTO.java @@ -0,0 +1,36 @@ +package com.gxwebsoft.credit.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Min; +import javax.validation.constraints.Max; + +/** + * 跟进步骤审核DTO + * + * @author 科技小王子 + * @since 2026-03-22 + */ +@Data +@Schema(name = "FollowStepApprovalDTO对象", description = "跟进步骤审核DTO") +public class FollowStepApprovalDTO { + + @Schema(description = "客户ID", required = true) + @NotNull(message = "客户ID不能为空") + private Long customerId; + + @Schema(description = "步骤号", required = true, example = "5") + @NotNull(message = "步骤号不能为空") + @Min(value = 1, message = "步骤号最小为1") + @Max(value = 7, message = "步骤号最大为7") + private Integer step; + + @Schema(description = "是否审核通过", required = true) + @NotNull(message = "审核状态不能为空") + private Boolean approved; + + @Schema(description = "审核备注") + private String remark; +} diff --git a/src/main/java/com/gxwebsoft/credit/param/FollowStepQueryDTO.java b/src/main/java/com/gxwebsoft/credit/param/FollowStepQueryDTO.java new file mode 100644 index 0000000..b3bce3a --- /dev/null +++ b/src/main/java/com/gxwebsoft/credit/param/FollowStepQueryDTO.java @@ -0,0 +1,29 @@ +package com.gxwebsoft.credit.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.Min; +import javax.validation.constraints.Max; + +/** + * 跟进步骤查询DTO + * + * @author 科技小王子 + * @since 2026-03-22 + */ +@Data +@Schema(name = "FollowStepQueryDTO对象", description = "跟进步骤查询DTO") +public class FollowStepQueryDTO { + + @Schema(description = "步骤号", example = "5") + @Min(value = 1, message = "步骤号最小为1") + @Max(value = 7, message = "步骤号最大为7") + private Integer step; + + @Schema(description = "客户ID") + private Long customerId; + + @Schema(description = "用户ID") + private Long userId; +} diff --git a/src/main/java/com/gxwebsoft/credit/service/CreditMpCustomerService.java b/src/main/java/com/gxwebsoft/credit/service/CreditMpCustomerService.java index 8a11e2d..46a90a3 100644 --- a/src/main/java/com/gxwebsoft/credit/service/CreditMpCustomerService.java +++ b/src/main/java/com/gxwebsoft/credit/service/CreditMpCustomerService.java @@ -4,6 +4,12 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.credit.entity.CreditMpCustomer; import com.gxwebsoft.credit.param.CreditMpCustomerParam; +import com.gxwebsoft.credit.param.BatchFollowStepApprovalDTO; +import com.gxwebsoft.credit.param.EndFollowProcessDTO; +import com.gxwebsoft.credit.param.FollowStepApprovalDTO; +import com.gxwebsoft.credit.param.FollowStepQueryDTO; +import com.gxwebsoft.credit.vo.FollowStatisticsDTO; +import com.gxwebsoft.credit.vo.PendingApprovalStepVO; import java.util.List; @@ -39,4 +45,41 @@ public interface CreditMpCustomerService extends IService { */ CreditMpCustomer getByIdRel(Integer id); + /** + * 审核跟进步骤 + * + * @param dto 审核参数 + */ + void approveFollowStep(FollowStepApprovalDTO dto); + + /** + * 批量审核跟进步骤 + * + * @param dto 批量审核参数 + */ + void batchApproveFollowSteps(BatchFollowStepApprovalDTO dto); + + /** + * 获取待审核的跟进步骤列表 + * + * @param query 查询参数 + * @return 待审核步骤列表 + */ + List getPendingApprovalSteps(FollowStepQueryDTO query); + + /** + * 获取客户跟进统计 + * + * @param customerId 客户ID + * @return 跟进统计信息 + */ + FollowStatisticsDTO getFollowStatistics(Long customerId); + + /** + * 结束客户跟进流程 + * + * @param dto 结束流程参数 + */ + void endFollowProcess(EndFollowProcessDTO dto); + } diff --git a/src/main/java/com/gxwebsoft/credit/service/impl/CreditMpCustomerServiceImpl.java b/src/main/java/com/gxwebsoft/credit/service/impl/CreditMpCustomerServiceImpl.java index c7f60fe..2776ce3 100644 --- a/src/main/java/com/gxwebsoft/credit/service/impl/CreditMpCustomerServiceImpl.java +++ b/src/main/java/com/gxwebsoft/credit/service/impl/CreditMpCustomerServiceImpl.java @@ -1,14 +1,27 @@ package com.gxwebsoft.credit.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.common.core.exception.BusinessException; +import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.credit.mapper.CreditMpCustomerMapper; import com.gxwebsoft.credit.service.CreditMpCustomerService; import com.gxwebsoft.credit.entity.CreditMpCustomer; import com.gxwebsoft.credit.param.CreditMpCustomerParam; +import com.gxwebsoft.credit.param.BatchFollowStepApprovalDTO; +import com.gxwebsoft.credit.param.EndFollowProcessDTO; +import com.gxwebsoft.credit.param.FollowStepApprovalDTO; +import com.gxwebsoft.credit.param.FollowStepQueryDTO; +import com.gxwebsoft.credit.vo.FollowStatisticsDTO; +import com.gxwebsoft.credit.vo.FollowStepDetailDTO; +import com.gxwebsoft.credit.vo.PendingApprovalStepVO; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; /** @@ -20,6 +33,8 @@ import java.util.List; @Service public class CreditMpCustomerServiceImpl extends ServiceImpl implements CreditMpCustomerService { + private final BaseController baseController = new BaseController(); + @Override public PageResult pageRel(CreditMpCustomerParam param) { PageParam page = new PageParam<>(param); @@ -44,4 +59,201 @@ public class CreditMpCustomerServiceImpl extends ServiceImpl getPendingApprovalSteps(FollowStepQueryDTO query) { + return baseMapper.selectPendingApprovalSteps(query); + } + + @Override + public FollowStatisticsDTO getFollowStatistics(Long customerId) { + CreditMpCustomer customer = getById(customerId); + if (customer == null) { + throw new BusinessException("客户不存在"); + } + + FollowStatisticsDTO statistics = new FollowStatisticsDTO(); + statistics.setTotalSteps(7); + + List stepDetails = new ArrayList<>(); + int completedSteps = 0; + int currentStep = 1; + + for (int i = 1; i <= 7; i++) { + FollowStepDetailDTO detail = getStepDetail(customer, i); + stepDetails.add(detail); + + if ("approved".equals(detail.getStatus())) { + completedSteps++; + currentStep = i + 1; + } else if ("submitted".equals(detail.getStatus()) && currentStep == 1) { + currentStep = i; + } + } + + statistics.setCompletedSteps(completedSteps); + statistics.setCurrentStep(Math.min(currentStep, 7)); + statistics.setProgress((double) completedSteps / 7 * 100); + statistics.setStepDetails(stepDetails); + + return statistics; + } + + @Override + @Transactional + public void endFollowProcess(EndFollowProcessDTO dto) { + CreditMpCustomer customer = getById(dto.getCustomerId()); + if (customer == null) { + throw new BusinessException("客户不存在"); + } + + customer.setFollowProcessEnded(1); + customer.setFollowProcessEndTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + customer.setFollowProcessEndReason(dto.getReason()); + + updateById(customer); + } + + // 私有辅助方法 + private boolean isStepSubmitted(CreditMpCustomer customer, Integer step) { + switch (step) { + case 1: return customer.getFollowStep1Submitted() != null && customer.getFollowStep1Submitted() == 1; + case 2: return customer.getFollowStep2Submitted() != null && customer.getFollowStep2Submitted() == 1; + case 3: return customer.getFollowStep3Submitted() != null && customer.getFollowStep3Submitted() == 1; + case 4: return customer.getFollowStep4Submitted() != null && customer.getFollowStep4Submitted() == 1; + case 5: return customer.getFollowStep5Submitted() != null && customer.getFollowStep5Submitted() == 1; + case 6: return customer.getFollowStep6Submitted() != null && customer.getFollowStep6Submitted() == 1; + case 7: return customer.getFollowStep7Submitted() != null && customer.getFollowStep7Submitted() == 1; + default: return false; + } + } + + private void updateStepApproval(CreditMpCustomer customer, FollowStepApprovalDTO dto) { + String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + Integer currentUserId = baseController.getLoginUserId(); + + switch (dto.getStep()) { + case 5: + customer.setFollowStep5Approved(dto.getApproved() ? 1 : 0); + customer.setFollowStep5ApprovedAt(currentTime); + customer.setFollowStep5ApprovedBy(currentUserId != null ? currentUserId.longValue() : null); + break; + case 6: + customer.setFollowStep6Approved(dto.getApproved() ? 1 : 0); + customer.setFollowStep6ApprovedAt(currentTime); + customer.setFollowStep6ApprovedBy(currentUserId != null ? currentUserId.longValue() : null); + break; + case 7: + customer.setFollowStep7Approved(dto.getApproved() ? 1 : 0); + customer.setFollowStep7ApprovedAt(currentTime); + customer.setFollowStep7ApprovedBy(currentUserId != null ? currentUserId.longValue() : null); + break; + } + + updateById(customer); + } + + private void updateCustomerStep(CreditMpCustomer customer, Integer step) { + // 更新客户的总体步骤状态 + if (step >= customer.getStep()) { + customer.setStep(step + 1); + updateById(customer); + } + } + + private FollowStepDetailDTO getStepDetail(CreditMpCustomer customer, Integer step) { + FollowStepDetailDTO detail = new FollowStepDetailDTO(); + detail.setStep(step); + + // 设置步骤标题 + String[] stepTitles = {"", "案件受理", "材料准备", "案件办理", "送达签收", "合同签订", "订单回款", "电话回访"}; + detail.setTitle(stepTitles[step]); + + // 获取步骤状态 + boolean submitted = isStepSubmitted(customer, step); + boolean approved = isStepApproved(customer, step); + + if (approved) { + detail.setStatus("approved"); + detail.setApprovedAt(getStepApprovedAt(customer, step)); + } else if (submitted) { + detail.setStatus("submitted"); + detail.setSubmittedAt(getStepSubmittedAt(customer, step)); + } else { + detail.setStatus("pending"); + } + + return detail; + } + + private boolean isStepApproved(CreditMpCustomer customer, Integer step) { + switch (step) { + case 1: return customer.getFollowStep1Approved() != null && customer.getFollowStep1Approved() == 1; + case 2: return customer.getFollowStep2Approved() != null && customer.getFollowStep2Approved() == 1; + case 3: return customer.getFollowStep3Approved() != null && customer.getFollowStep3Approved() == 1; + case 4: return customer.getFollowStep4Approved() != null && customer.getFollowStep4Approved() == 1; + case 5: return customer.getFollowStep5Approved() != null && customer.getFollowStep5Approved() == 1; + case 6: return customer.getFollowStep6Approved() != null && customer.getFollowStep6Approved() == 1; + case 7: return customer.getFollowStep7Approved() != null && customer.getFollowStep7Approved() == 1; + default: return false; + } + } + + private LocalDateTime getStepSubmittedAt(CreditMpCustomer customer, Integer step) { + String submittedAt = null; + switch (step) { + case 1: submittedAt = customer.getFollowStep1SubmittedAt(); break; + case 2: submittedAt = customer.getFollowStep2SubmittedAt(); break; + case 3: submittedAt = customer.getFollowStep3SubmittedAt(); break; + case 4: submittedAt = customer.getFollowStep4SubmittedAt(); break; + case 5: submittedAt = customer.getFollowStep5SubmittedAt(); break; + case 6: submittedAt = customer.getFollowStep6SubmittedAt(); break; + case 7: submittedAt = customer.getFollowStep7SubmittedAt(); break; + } + return submittedAt != null ? LocalDateTime.parse(submittedAt, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null; + } + + private LocalDateTime getStepApprovedAt(CreditMpCustomer customer, Integer step) { + String approvedAt = null; + switch (step) { + case 1: approvedAt = customer.getFollowStep1ApprovedAt(); break; + case 2: approvedAt = customer.getFollowStep2ApprovedAt(); break; + case 3: approvedAt = customer.getFollowStep3ApprovedAt(); break; + case 4: approvedAt = customer.getFollowStep4ApprovedAt(); break; + case 5: approvedAt = customer.getFollowStep5ApprovedAt(); break; + case 6: approvedAt = customer.getFollowStep6ApprovedAt(); break; + case 7: approvedAt = customer.getFollowStep7ApprovedAt(); break; + } + return approvedAt != null ? LocalDateTime.parse(approvedAt, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null; + } + } diff --git a/src/main/java/com/gxwebsoft/credit/vo/FollowStatisticsDTO.java b/src/main/java/com/gxwebsoft/credit/vo/FollowStatisticsDTO.java new file mode 100644 index 0000000..6a04155 --- /dev/null +++ b/src/main/java/com/gxwebsoft/credit/vo/FollowStatisticsDTO.java @@ -0,0 +1,32 @@ +package com.gxwebsoft.credit.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 客户跟进统计DTO + * + * @author 科技小王子 + * @since 2026-03-22 + */ +@Data +@Schema(name = "FollowStatisticsDTO对象", description = "客户跟进统计DTO") +public class FollowStatisticsDTO { + + @Schema(description = "总步骤数") + private Integer totalSteps; + + @Schema(description = "已完成步骤数") + private Integer completedSteps; + + @Schema(description = "当前步骤") + private Integer currentStep; + + @Schema(description = "进度百分比") + private Double progress; + + @Schema(description = "步骤详情列表") + private List stepDetails; +} diff --git a/src/main/java/com/gxwebsoft/credit/vo/FollowStepDetailDTO.java b/src/main/java/com/gxwebsoft/credit/vo/FollowStepDetailDTO.java new file mode 100644 index 0000000..506c523 --- /dev/null +++ b/src/main/java/com/gxwebsoft/credit/vo/FollowStepDetailDTO.java @@ -0,0 +1,32 @@ +package com.gxwebsoft.credit.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 跟进步骤详情DTO + * + * @author 科技小王子 + * @since 2026-03-22 + */ +@Data +@Schema(name = "FollowStepDetailDTO对象", description = "跟进步骤详情DTO") +public class FollowStepDetailDTO { + + @Schema(description = "步骤号") + private Integer step; + + @Schema(description = "步骤标题") + private String title; + + @Schema(description = "状态: pending, submitted, approved, rejected") + private String status; + + @Schema(description = "提交时间") + private LocalDateTime submittedAt; + + @Schema(description = "审核时间") + private LocalDateTime approvedAt; +} diff --git a/src/main/java/com/gxwebsoft/credit/vo/PendingApprovalStepVO.java b/src/main/java/com/gxwebsoft/credit/vo/PendingApprovalStepVO.java new file mode 100644 index 0000000..fbc7d2f --- /dev/null +++ b/src/main/java/com/gxwebsoft/credit/vo/PendingApprovalStepVO.java @@ -0,0 +1,38 @@ +package com.gxwebsoft.credit.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 待审核跟进步骤VO + * + * @author 科技小王子 + * @since 2026-03-22 + */ +@Data +@Schema(name = "PendingApprovalStepVO对象", description = "待审核跟进步骤VO") +public class PendingApprovalStepVO { + + @Schema(description = "客户ID") + private Long customerId; + + @Schema(description = "客户名称") + private String customerName; + + @Schema(description = "步骤号") + private Integer step; + + @Schema(description = "步骤标题") + private String stepTitle; + + @Schema(description = "提交时间") + private LocalDateTime submittedAt; + + @Schema(description = "提交人") + private String submittedBy; + + @Schema(description = "内容") + private String content; +}