Parcourir la source

CRM: 完善回款计划

puhui999 il y a 1 an
Parent
commit
b3cfad9396
14 fichiers modifiés avec 181 ajouts et 176 suppressions
  1. 2 1
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
  2. 6 6
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
  3. 0 54
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanBaseVO.java
  4. 0 12
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanCreateReqVO.java
  5. 38 6
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java
  6. 55 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java
  7. 0 18
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanUpdateReqVO.java
  8. 0 8
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java
  9. 16 19
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java
  10. 0 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
  11. 4 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
  12. 47 41
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
  13. 8 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
  14. 5 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java

+ 2 - 1
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java

@@ -33,8 +33,9 @@ public interface ErrorCodeConstants {
     // ========== 回款 1-020-004-000 ==========
     ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在");
 
-    // ========== 合同管理 1-020-005-000 ==========
+    // ========== 回款计划 1-020-005-000 ==========
     ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
+    ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, "更想回款计划失败,原因:{}");
 
     // ========== 客户管理 1_020_006_000 ==========
     ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在");

+ 6 - 6
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java

@@ -5,12 +5,12 @@ import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@@ -67,14 +67,14 @@ public class CrmReceivablePlanController {
     @PostMapping("/create")
     @Operation(summary = "创建回款计划")
     @PreAuthorize("@ss.hasPermission('crm:receivable-plan:create')")
-    public CommonResult<Long> createReceivablePlan(@Valid @RequestBody CrmReceivablePlanCreateReqVO createReqVO) {
-        return success(receivablePlanService.createReceivablePlan(createReqVO, getLoginUserId()));
+    public CommonResult<Long> createReceivablePlan(@Valid @RequestBody CrmReceivablePlanSaveReqVO createReqVO) {
+        return success(receivablePlanService.createReceivablePlan(createReqVO));
     }
 
     @PutMapping("/update")
     @Operation(summary = "更新回款计划")
     @PreAuthorize("@ss.hasPermission('crm:receivable-plan:update')")
-    public CommonResult<Boolean> updateReceivablePlan(@Valid @RequestBody CrmReceivablePlanUpdateReqVO updateReqVO) {
+    public CommonResult<Boolean> updateReceivablePlan(@Valid @RequestBody CrmReceivablePlanSaveReqVO updateReqVO) {
         receivablePlanService.updateReceivablePlan(updateReqVO);
         return success(true);
     }
@@ -94,7 +94,7 @@ public class CrmReceivablePlanController {
     @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
     public CommonResult<CrmReceivablePlanRespVO> getReceivablePlan(@RequestParam("id") Long id) {
         CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id);
-        return success(CrmReceivablePlanConvert.INSTANCE.convert(receivablePlan));
+        return success(BeanUtils.toBean(receivablePlan, CrmReceivablePlanRespVO.class));
     }
 
     @GetMapping("/page")

+ 0 - 54
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanBaseVO.java

@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-/**
- * 回款计划 Base VO,提供给添加、修改、详细的子 VO 使用
- * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
- */
-@Data
-public class CrmReceivablePlanBaseVO {
-
-    @Schema(description = "期数", example = "1")
-    private Integer period;
-
-    @Schema(description = "回款计划编号", example = "19852")
-    private Long receivableId;
-
-    @Schema(description = "计划回款金额", example = "29675")
-    private Integer price;
-
-    @Schema(description = "计划回款日期")
-    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-    private LocalDateTime returnTime;
-
-    @Schema(description = "提前几天提醒")
-    private Integer remindDays;
-
-    @Schema(description = "提醒日期")
-    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-    private LocalDateTime remindTime;
-
-    @Schema(description = "客户名称", example = "18026")
-    private Long customerId;
-
-    @Schema(description = "合同编号", example = "3473")
-    private Long contractId;
-
-    // TODO @liuhongfeng:负责人编号
-    @Schema(description = "负责人编号", example = "17828")
-    private Long ownerUserId;
-
-    @Schema(description = "显示顺序")
-    private Integer sort;
-
-    @Schema(description = "备注", example = "备注")
-    private String remark;
-
-}

+ 0 - 12
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanCreateReqVO.java

@@ -1,12 +0,0 @@
-package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
-
-import lombok.*;
-import io.swagger.v3.oas.annotations.media.Schema;
-
-@Schema(description = "管理后台 - CRM 回款计划创建 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class CrmReceivablePlanCreateReqVO extends CrmReceivablePlanBaseVO {
-
-}

+ 38 - 6
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java

@@ -1,18 +1,50 @@
 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
+import lombok.Data;
+
 import java.time.LocalDateTime;
 
 @Schema(description = "管理后台 - CRM 回款计划 Response VO")
 @Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class CrmReceivablePlanRespVO extends CrmReceivablePlanBaseVO {
+public class CrmReceivablePlanRespVO {
 
     @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25153")
     private Long id;
 
+    @Schema(description = "期数", example = "1")
+    private Integer period;
+
+    @Schema(description = "回款计划编号", example = "19852")
+    private Long receivableId;
+
+    @Schema(description = "计划回款金额", example = "29675")
+    private Integer price;
+
+    @Schema(description = "计划回款日期")
+    private LocalDateTime returnTime;
+
+    @Schema(description = "提前几天提醒")
+    private Integer remindDays;
+
+    @Schema(description = "提醒日期")
+    private LocalDateTime remindTime;
+
+    @Schema(description = "客户名称", example = "18026")
+    private Long customerId;
+
+    @Schema(description = "合同编号", example = "3473")
+    private Long contractId;
+
+    @Schema(description = "负责人编号", example = "17828")
+    private Long ownerUserId;
+
+    @Schema(description = "显示顺序")
+    private Integer sort;
+
+    @Schema(description = "备注", example = "备注")
+    private String remark;
+
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
@@ -34,7 +66,7 @@ public class CrmReceivablePlanRespVO extends CrmReceivablePlanBaseVO {
     @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     private Boolean finishStatus;
 
-    @Schema(description = "回款方式", example = "1") // 来自 Receivable 的 returnType 字段
-    private Integer returnType;
+    @Schema(description = "回款方式", example = "1")
+    private Integer returnType; // 来自 Receivable 的 returnType 字段
 
 }

+ 55 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - CRM 回款计划新增/修改 Request VO")
+@Data
+public class CrmReceivablePlanSaveReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    private Long id;
+
+    @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "期数不能为空")
+    private Integer period;
+
+    @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
+    @NotNull(message = "计划回款金额不能为空")
+    private BigDecimal price;
+
+    @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
+    @NotNull(message = "计划回款日期不能为空")
+    private LocalDateTime returnTime;
+
+    @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "提前几天提醒不能为空")
+    private Integer remindDays;
+
+    @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
+    @NotNull(message = "提醒日期不能为空")
+    private LocalDateTime remindTime;
+
+    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "客户编号不能为空")
+    private Long customerId;
+
+    @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "合同编号不能为空")
+    private Long contractId;
+
+    @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "负责人编号不能为空")
+    private Long ownerUserId;
+
+    @Schema(description = "显示顺序")
+    private Integer sort;
+
+    @Schema(description = "备注", example = "备注")
+    private String remark;
+
+}

+ 0 - 18
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanUpdateReqVO.java

@@ -1,18 +0,0 @@
-package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
-import jakarta.validation.constraints.*;
-
-@Schema(description = "管理后台 - CRM 回款计划更新 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class CrmReceivablePlanUpdateReqVO extends CrmReceivablePlanBaseVO {
-
-    @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25153")
-    @NotNull(message = "ID不能为空")
-    private Long id;
-
-}

+ 0 - 8
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java

@@ -2,9 +2,7 @@ package cn.iocoder.yudao.module.crm.convert.receivable;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
@@ -29,12 +27,6 @@ public interface CrmReceivablePlanConvert {
 
     CrmReceivablePlanConvert INSTANCE = Mappers.getMapper(CrmReceivablePlanConvert.class);
 
-    CrmReceivablePlanDO convert(CrmReceivablePlanCreateReqVO bean);
-
-    CrmReceivablePlanDO convert(CrmReceivablePlanUpdateReqVO bean);
-
-    CrmReceivablePlanRespVO convert(CrmReceivablePlanDO bean);
-
     default PageResult<CrmReceivablePlanRespVO> convertPage(PageResult<CrmReceivablePlanDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
                                                             List<CrmCustomerDO> customerList, List<CrmContractDO> contractList,
                                                             List<CrmReceivableDO> receivableList) {

+ 16 - 19
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java

@@ -1,11 +1,15 @@
 package cn.iocoder.yudao.module.crm.dal.dataobject.receivable;
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
@@ -24,7 +28,7 @@ import java.time.LocalDateTime;
 public class CrmReceivablePlanDO extends BaseDO {
 
     /**
-     * ID
+     * 编号
      */
     @TableId
     private Long id;
@@ -33,19 +37,13 @@ public class CrmReceivablePlanDO extends BaseDO {
      */
     private Integer period;
     /**
-     * 回款ID
-     *
-     * TODO @liuhongfeng:少关联实体;
+     * 回款编号,关联 {@link CrmReceivableDO#getId()}
      */
     private Long receivableId;
     /**
-     * 完成状态
+     * 计划回款金额,单位:元
      */
-    private Boolean finishStatus;
-    /**
-     * 计划回款金额,单位:分
-     */
-    private Integer price;
+    private BigDecimal price;
     /**
      * 计划回款日期
      */
@@ -59,21 +57,15 @@ public class CrmReceivablePlanDO extends BaseDO {
      */
     private LocalDateTime remindTime;
     /**
-     * 客户 ID
-     *
-     * TODO @liuhongfeng:少关联实体;
+     * 客户编号,关联 {@link CrmCustomerDO#getId()}
      */
     private Long customerId;
     /**
-     * 合同 ID
-     *
-     * TODO @liuhongfeng:少关联实体;
+     * 合同编号,关联 {@link CrmContractDO#getId()}
      */
     private Long contractId;
     /**
-     * 负责人 ID
-     *
-     * TODO @liuhongfeng:少关联实体;
+     * 负责人编号,关联 {@link AdminUserRespDTO#getId()}
      */
     private Long ownerUserId;
     /**
@@ -85,4 +77,9 @@ public class CrmReceivablePlanDO extends BaseDO {
      */
     private String remark;
 
+    /**
+     * 完成状态
+     */
+    private Boolean finishStatus;
+
 }

+ 0 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java

@@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.time.LocalDateTime;
@@ -25,12 +24,6 @@ import java.util.List;
 @Mapper
 public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO> {
 
-    default int updateOwnerUserIdById(Long id, Long ownerUserId) {
-        return update(new LambdaUpdateWrapper<CrmReceivablePlanDO>()
-                .eq(CrmReceivablePlanDO::getId, id)
-                .set(CrmReceivablePlanDO::getOwnerUserId, ownerUserId));
-    }
-
     default PageResult<CrmReceivablePlanDO> selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivablePlanDO>()
                 .eq(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId()) // 必须传递

+ 4 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java

@@ -1,9 +1,8 @@
 package cn.iocoder.yudao.module.crm.service.receivable;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
 import jakarta.validation.Valid;
@@ -24,14 +23,14 @@ public interface CrmReceivablePlanService {
      * @param createReqVO 创建信息
      * @return 编号
      */
-    Long createReceivablePlan(@Valid CrmReceivablePlanCreateReqVO createReqVO, Long userId);
+    Long createReceivablePlan(@Valid CrmReceivablePlanSaveReqVO createReqVO);
 
     /**
      * 更新回款计划
      *
      * @param updateReqVO 更新信息
      */
-    void updateReceivablePlan(@Valid CrmReceivablePlanUpdateReqVO updateReqVO);
+    void updateReceivablePlan(@Valid CrmReceivablePlanSaveReqVO updateReqVO);
 
     /**
      * 删除回款计划
@@ -84,4 +83,5 @@ public interface CrmReceivablePlanService {
      * @return 提醒数量
      */
     Long getRemindReceivablePlanCount(Long userId);
+
 }

+ 47 - 41
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java

@@ -5,10 +5,8 @@ import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
-import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
-import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
+import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
@@ -20,22 +18,24 @@ import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.service.impl.DiffParseFunction;
 import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
+import org.hibernate.validator.internal.util.stereotypes.Lazy;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
 
-// TODO @liuhongfeng:参考 CrmReceivableServiceImpl 写的 todo 哈;
-
 /**
  * 回款计划 Service 实现类
  *
@@ -48,73 +48,79 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
     @Resource
     private CrmReceivablePlanMapper receivablePlanMapper;
 
+    @Resource
+    @Lazy // 延迟加载,避免循环依赖
+    private CrmReceivableService receivableService;
     @Resource
     private CrmContractService contractService;
     @Resource
     private CrmCustomerService customerService;
     @Resource
     private CrmPermissionService permissionService;
+    @Resource
+    private AdminUserApi adminUserApi;
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}",
             success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)
-    public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO, Long userId) {
-        // TODO @liuhongfeng:第几期的计算;基于是 contractId + contractDO 的第几个还款
-        // TODO @liuhongfeng contractId:校验合同是否存在
-        // 插入
-        CrmReceivablePlanDO receivablePlan = CrmReceivablePlanConvert.INSTANCE.convert(createReqVO);
-        receivablePlan.setFinishStatus(false);
-
-        checkReceivablePlan(receivablePlan);
-
+    public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) {
+        // 1.1 校验关联数据是否存在
+        checkReceivablePlan(createReqVO);
+        // 1.2 查验关联合同回款数量
+        Long count = receivableService.getReceivableCountByContractId(createReqVO.getContractId());
+        int period = (int) (count + 1);
+        createReqVO.setPeriod(createReqVO.getPeriod() != period ? period : createReqVO.getPeriod()); // 如果期数不对则纠正
+
+        // 2.1 插入
+        CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setId(null).setFinishStatus(false);
         receivablePlanMapper.insert(receivablePlan);
-        // 创建数据权限
-        permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
+        // 2.2 创建数据权限
+        permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(createReqVO.getOwnerUserId())
                 .setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())
                 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
 
-        // 4. 记录操作日志上下文
+        // 3. 记录操作日志上下文
         LogRecordContext.putVariable("receivablePlan", receivablePlan);
         return receivablePlan.getId();
     }
 
-    private void checkReceivablePlan(CrmReceivablePlanDO receivablePlan) {
-
-        if (ObjectUtil.isNull(receivablePlan.getContractId())) {
-            throw exception(CONTRACT_NOT_EXISTS);
-        }
-
-        CrmContractDO contract = contractService.getContract(receivablePlan.getContractId());
-        if (ObjectUtil.isNull(contract)) {
-            throw exception(CONTRACT_NOT_EXISTS);
-        }
-
-        CrmCustomerDO customer = customerService.getCustomer(receivablePlan.getCustomerId());
-        if (ObjectUtil.isNull(customer)) {
-            throw exception(CUSTOMER_NOT_EXISTS);
-        }
-
-    }
-
     @Override
+    @Transactional(rollbackFor = Exception.class)
     @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
             success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS)
     @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
-    public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) {
-        // TODO @liuhongfeng:如果已经有对应的还款,则不允许编辑;
-        // 校验存在
+    public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) {
+        // 1. 校验存在
+        checkReceivablePlan(updateReqVO);
         CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
+        if (Objects.nonNull(oldReceivablePlan.getReceivableId())) { // 如果已经有对应的还款,则不允许编辑;
+            throw exception(RECEIVABLE_PLAN_UPDATE_FAIL, "已经有对应的还款");
+        }
 
-        // 更新
-        CrmReceivablePlanDO updateObj = CrmReceivablePlanConvert.INSTANCE.convert(updateReqVO);
+        // 2. 更新
+        CrmReceivablePlanDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivablePlanDO.class);
         receivablePlanMapper.updateById(updateObj);
 
         // 3. 记录操作日志上下文
-        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanUpdateReqVO.class));
+        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanSaveReqVO.class));
         LogRecordContext.putVariable("receivablePlan", oldReceivablePlan);
     }
 
+    private void checkReceivablePlan(CrmReceivablePlanSaveReqVO reqVO) {
+        adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
+        CrmContractDO contract = contractService.getContract(reqVO.getContractId());
+        if (ObjectUtil.isNull(contract)) { // 合同不存在
+            throw exception(CONTRACT_NOT_EXISTS);
+        }
+        CrmCustomerDO customer = customerService.getCustomer(reqVO.getCustomerId());
+        if (ObjectUtil.isNull(customer)) { // 客户不存在
+            throw exception(CUSTOMER_NOT_EXISTS);
+        }
+    }
+
     @Override
+    @Transactional(rollbackFor = Exception.class)
     @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE, bizNo = "{{#id}}",
             success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS)
     @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)

+ 8 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java

@@ -86,4 +86,12 @@ public interface CrmReceivableService {
      */
     Long getCheckReceivablesCount(Long userId);
 
+    /**
+     * 根据合同编号,获取合同关联的回款数量
+     *
+     * @param contractId 合同编号
+     * @return 数量
+     */
+    Long getReceivableCountByContractId(Long contractId);
+
 }

+ 5 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java

@@ -187,4 +187,9 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
         return receivableMapper.selectCheckReceivablesCount(userId);
     }
 
+    @Override
+    public Long getReceivableCountByContractId(Long contractId) {
+        return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId);
+    }
+
 }