ljlleo 1 年間 前
コミット
bee6f41211
16 ファイル変更1128 行追加0 行削除
  1. 3 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
  2. 99 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
  3. 78 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessBaseVO.java
  4. 14 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java
  5. 82 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExcelVO.java
  6. 74 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExportReqVO.java
  7. 79 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessPageReqVO.java
  8. 19 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessRespVO.java
  9. 18 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java
  10. 34 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java
  11. 101 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java
  12. 67 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
  13. 75 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
  14. 90 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
  15. 12 0
      yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/business/CrmBusinessMapper.xml
  16. 283 0
      yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImplTest.java

+ 3 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java

@@ -15,4 +15,7 @@ public interface ErrorCodeConstants {
     // TODO @wanwan:要单独一个分段噢
     ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_000_001, "线索不存在");
 
+    // ========== 商机管理 1-020-001-000 ==========
+    ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_001_000, "商机不存在");
+
 }

+ 99 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java

@@ -0,0 +1,99 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+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.business.vo.*;
+import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+@Tag(name = "管理后台 - 商机")
+@RestController
+@RequestMapping("/crm/business")
+@Validated
+public class CrmBusinessController {
+
+    @Resource
+    private CrmBusinessService businessService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建商机")
+    @PreAuthorize("@ss.hasPermission('crm:business:create')")
+    public CommonResult<Long> createBusiness(@Valid @RequestBody CrmBusinessCreateReqVO createReqVO) {
+        return success(businessService.createBusiness(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新商机")
+    @PreAuthorize("@ss.hasPermission('crm:business:update')")
+    public CommonResult<Boolean> updateBusiness(@Valid @RequestBody CrmBusinessUpdateReqVO updateReqVO) {
+        businessService.updateBusiness(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除商机")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:business:delete')")
+    public CommonResult<Boolean> deleteBusiness(@RequestParam("id") Long id) {
+        businessService.deleteBusiness(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得商机")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:business:query')")
+    public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
+        CrmBusinessDO business = businessService.getBusiness(id);
+        return success(CrmBusinessConvert.INSTANCE.convert(business));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得商机列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('crm:business:query')")
+    public CommonResult<List<CrmBusinessRespVO>> getBusinessList(@RequestParam("ids") Collection<Long> ids) {
+        List<CrmBusinessDO> list = businessService.getBusinessList(ids);
+        return success(CrmBusinessConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得商机分页")
+    @PreAuthorize("@ss.hasPermission('crm:business:query')")
+    public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {
+        PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO);
+        return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出商机 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:business:export')")
+    @OperateLog(type = EXPORT)
+    public void exportBusinessExcel(@Valid CrmBusinessExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<CrmBusinessDO> list = businessService.getBusinessList(exportReqVO);
+        // 导出 Excel
+        List<CrmBusinessExcelVO> datas = CrmBusinessConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessExcelVO.class, datas);
+    }
+
+}

+ 78 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessBaseVO.java

@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+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 CrmBusinessBaseVO {
+
+    @Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
+    @NotNull(message = "商机名称不能为空")
+    private String name;
+
+    @Schema(description = "商机状态类型编号", example = "25714")
+    @NotNull(message = "商机状态类型不能为空")
+    private Long statusTypeId;
+
+    @Schema(description = "商机状态编号", example = "30320")
+    @NotNull(message = "商机状态不能为空")
+    private Long statusId;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactNextTime;
+
+    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
+    @NotNull(message = "客户不能为空")
+    private Long customerId;
+
+    @Schema(description = "预计成交日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime dealTime;
+
+    @Schema(description = "商机金额", example = "12371")
+    private BigDecimal price;
+
+    @Schema(description = "整单折扣")
+    private BigDecimal discountPercent;
+
+    @Schema(description = "产品总金额", example = "12025")
+    private BigDecimal productPrice;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "负责人的用户编号", example = "25562")
+    private Long ownerUserId;
+
+    @Schema(description = "只读权限的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String roUserIds;
+
+    @Schema(description = "读写权限的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String rwUserIds;
+
+    @Schema(description = "1赢单2输单3无效", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer endStatus;
+
+    @Schema(description = "结束时的备注", example = "你说的对")
+    private String endRemark;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactLastTime;
+
+    @Schema(description = "跟进状态", example = "1")
+    private Integer followUpStatus;
+
+}

+ 14 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 商机创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessCreateReqVO extends CrmBusinessBaseVO {
+
+}

+ 82 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExcelVO.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+
+/**
+ * 商机 Excel VO
+ *
+ * @author ljlleo
+ */
+@Data
+public class CrmBusinessExcelVO {
+
+    @ExcelProperty("主键")
+    private Long id;
+
+    @ExcelProperty("商机名称")
+    private String name;
+
+    @ExcelProperty("商机状态类型编号")
+    private Long statusTypeId;
+
+    @ExcelProperty("商机状态编号")
+    private Long statusId;
+
+    @ExcelProperty("下次联系时间")
+    private LocalDateTime contactNextTime;
+
+    @ExcelProperty("客户编号")
+    private Long customerId;
+
+    @ExcelProperty("预计成交日期")
+    private LocalDateTime dealTime;
+
+    @ExcelProperty("商机金额")
+    private BigDecimal price;
+
+    @ExcelProperty("整单折扣")
+    private BigDecimal discountPercent;
+
+    @ExcelProperty("产品总金额")
+    private BigDecimal productPrice;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("负责人的用户编号")
+    private Long ownerUserId;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+    @ExcelProperty("只读权限的用户编号数组")
+    private String roUserIds;
+
+    @ExcelProperty("读写权限的用户编号数组")
+    private String rwUserIds;
+
+    @ExcelProperty("1赢单2输单3无效")
+    private Integer endStatus;
+
+    @ExcelProperty("结束时的备注")
+    private String endRemark;
+
+    @ExcelProperty("最后跟进时间")
+    private LocalDateTime contactLastTime;
+
+    @ExcelProperty("跟进状态")
+    private Integer followUpStatus;
+
+}

+ 74 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExportReqVO.java

@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 商机 Excel 导出 Request VO,参数和 CrmBusinessPageReqVO 是一致的")
+@Data
+public class CrmBusinessExportReqVO {
+
+    @Schema(description = "商机名称", example = "李四")
+    private String name;
+
+    @Schema(description = "商机状态类型编号", example = "25714")
+    private Long statusTypeId;
+
+    @Schema(description = "商机状态编号", example = "30320")
+    private Long statusId;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactNextTime;
+
+    @Schema(description = "客户编号", example = "10299")
+    private Long customerId;
+
+    @Schema(description = "预计成交日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] dealTime;
+
+    @Schema(description = "商机金额", example = "12371")
+    private BigDecimal price;
+
+    @Schema(description = "整单折扣")
+    private BigDecimal discountPercent;
+
+    @Schema(description = "产品总金额", example = "12025")
+    private BigDecimal productPrice;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "负责人的用户编号", example = "25562")
+    private Long ownerUserId;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+    @Schema(description = "只读权限的用户编号数组")
+    private String roUserIds;
+
+    @Schema(description = "读写权限的用户编号数组")
+    private String rwUserIds;
+
+    @Schema(description = "1赢单2输单3无效", example = "1")
+    private Integer endStatus;
+
+    @Schema(description = "结束时的备注", example = "你说的对")
+    private String endRemark;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactLastTime;
+
+    @Schema(description = "跟进状态", example = "1")
+    private Integer followUpStatus;
+
+}

+ 79 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessPageReqVO.java

@@ -0,0 +1,79 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 商机分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessPageReqVO extends PageParam {
+
+    @Schema(description = "商机名称", example = "李四")
+    private String name;
+
+    @Schema(description = "商机状态类型编号", example = "25714")
+    private Long statusTypeId;
+
+    @Schema(description = "商机状态编号", example = "30320")
+    private Long statusId;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactNextTime;
+
+    @Schema(description = "客户编号", example = "10299")
+    private Long customerId;
+
+    @Schema(description = "预计成交日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] dealTime;
+
+    @Schema(description = "商机金额", example = "12371")
+    private BigDecimal price;
+
+    @Schema(description = "整单折扣")
+    private BigDecimal discountPercent;
+
+    @Schema(description = "产品总金额", example = "12025")
+    private BigDecimal productPrice;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "负责人的用户编号", example = "25562")
+    private Long ownerUserId;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+    @Schema(description = "只读权限的用户编号数组")
+    private String roUserIds;
+
+    @Schema(description = "读写权限的用户编号数组")
+    private String rwUserIds;
+
+    @Schema(description = "1赢单2输单3无效", example = "1")
+    private Integer endStatus;
+
+    @Schema(description = "结束时的备注", example = "你说的对")
+    private String endRemark;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactLastTime;
+
+    @Schema(description = "跟进状态", example = "1")
+    private Integer followUpStatus;
+
+}

+ 19 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 商机 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessRespVO extends CrmBusinessBaseVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 商机更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessUpdateReqVO extends CrmBusinessBaseVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
+    @NotNull(message = "主键不能为空")
+    private Long id;
+
+}

+ 34 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.crm.convert.business;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*;
+import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+
+/**
+ * 商机 Convert
+ *
+ * @author ljlleo
+ */
+@Mapper
+public interface CrmBusinessConvert {
+
+    CrmBusinessConvert INSTANCE = Mappers.getMapper(CrmBusinessConvert.class);
+
+    CrmBusinessDO convert(CrmBusinessCreateReqVO bean);
+
+    CrmBusinessDO convert(CrmBusinessUpdateReqVO bean);
+
+    CrmBusinessRespVO convert(CrmBusinessDO bean);
+
+    List<CrmBusinessRespVO> convertList(List<CrmBusinessDO> list);
+
+    PageResult<CrmBusinessRespVO> convertPage(PageResult<CrmBusinessDO> page);
+
+    List<CrmBusinessExcelVO> convertList02(List<CrmBusinessDO> list);
+
+}

+ 101 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java

@@ -0,0 +1,101 @@
+package cn.iocoder.yudao.module.crm.dal.dataobject.business;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+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;
+
+/**
+ * 商机 DO
+ *
+ * @author ljlleo
+ */
+@TableName("crm_business")
+@KeySequence("crm_business_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CrmBusinessDO extends BaseDO {
+
+    /**
+     * 主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 商机名称
+     */
+    private String name;
+    /**
+     * 商机状态类型编号
+     */
+    private Long statusTypeId;
+    /**
+     * 商机状态编号
+     */
+    private Long statusId;
+    /**
+     * 下次联系时间
+     */
+    private LocalDateTime contactNextTime;
+    /**
+     * 客户编号
+     */
+    private Long customerId;
+    /**
+     * 预计成交日期
+     */
+    private LocalDateTime dealTime;
+    /**
+     * 商机金额
+     */
+    private BigDecimal price;
+    /**
+     * 整单折扣
+     */
+    private BigDecimal discountPercent;
+    /**
+     * 产品总金额
+     */
+    private BigDecimal productPrice;
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 负责人的用户编号
+     */
+    private Long ownerUserId;
+    /**
+     * 只读权限的用户编号数组
+     */
+    private String roUserIds;
+    /**
+     * 读写权限的用户编号数组
+     */
+    private String rwUserIds;
+    /**
+     * 1赢单2输单3无效
+     */
+    private Integer endStatus;
+    /**
+     * 结束时的备注
+     */
+    private String endRemark;
+    /**
+     * 最后跟进时间
+     */
+    private LocalDateTime contactLastTime;
+    /**
+     * 跟进状态
+     */
+    private Integer followUpStatus;
+
+}

+ 67 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java

@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.crm.dal.mysql.business;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessExportReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessPageReqVO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 商机 Mapper
+ *
+ * @author ljlleo
+ */
+@Mapper
+public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
+
+    default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<CrmBusinessDO>()
+                .likeIfPresent(CrmBusinessDO::getName, reqVO.getName())
+                .eqIfPresent(CrmBusinessDO::getStatusTypeId, reqVO.getStatusTypeId())
+                .eqIfPresent(CrmBusinessDO::getStatusId, reqVO.getStatusId())
+                .betweenIfPresent(CrmBusinessDO::getContactNextTime, reqVO.getContactNextTime())
+                .eqIfPresent(CrmBusinessDO::getCustomerId, reqVO.getCustomerId())
+                .betweenIfPresent(CrmBusinessDO::getDealTime, reqVO.getDealTime())
+                .eqIfPresent(CrmBusinessDO::getPrice, reqVO.getPrice())
+                .eqIfPresent(CrmBusinessDO::getDiscountPercent, reqVO.getDiscountPercent())
+                .eqIfPresent(CrmBusinessDO::getProductPrice, reqVO.getProductPrice())
+                .eqIfPresent(CrmBusinessDO::getRemark, reqVO.getRemark())
+                .eqIfPresent(CrmBusinessDO::getOwnerUserId, reqVO.getOwnerUserId())
+                .betweenIfPresent(CrmBusinessDO::getCreateTime, reqVO.getCreateTime())
+                .eqIfPresent(CrmBusinessDO::getRoUserIds, reqVO.getRoUserIds())
+                .eqIfPresent(CrmBusinessDO::getRwUserIds, reqVO.getRwUserIds())
+                .eqIfPresent(CrmBusinessDO::getEndStatus, reqVO.getEndStatus())
+                .eqIfPresent(CrmBusinessDO::getEndRemark, reqVO.getEndRemark())
+                .betweenIfPresent(CrmBusinessDO::getContactLastTime, reqVO.getContactLastTime())
+                .eqIfPresent(CrmBusinessDO::getFollowUpStatus, reqVO.getFollowUpStatus())
+                .orderByDesc(CrmBusinessDO::getId));
+    }
+
+    default List<CrmBusinessDO> selectList(CrmBusinessExportReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
+                .likeIfPresent(CrmBusinessDO::getName, reqVO.getName())
+                .eqIfPresent(CrmBusinessDO::getStatusTypeId, reqVO.getStatusTypeId())
+                .eqIfPresent(CrmBusinessDO::getStatusId, reqVO.getStatusId())
+                .betweenIfPresent(CrmBusinessDO::getContactNextTime, reqVO.getContactNextTime())
+                .eqIfPresent(CrmBusinessDO::getCustomerId, reqVO.getCustomerId())
+                .betweenIfPresent(CrmBusinessDO::getDealTime, reqVO.getDealTime())
+                .eqIfPresent(CrmBusinessDO::getPrice, reqVO.getPrice())
+                .eqIfPresent(CrmBusinessDO::getDiscountPercent, reqVO.getDiscountPercent())
+                .eqIfPresent(CrmBusinessDO::getProductPrice, reqVO.getProductPrice())
+                .eqIfPresent(CrmBusinessDO::getRemark, reqVO.getRemark())
+                .eqIfPresent(CrmBusinessDO::getOwnerUserId, reqVO.getOwnerUserId())
+                .betweenIfPresent(CrmBusinessDO::getCreateTime, reqVO.getCreateTime())
+                .eqIfPresent(CrmBusinessDO::getRoUserIds, reqVO.getRoUserIds())
+                .eqIfPresent(CrmBusinessDO::getRwUserIds, reqVO.getRwUserIds())
+                .eqIfPresent(CrmBusinessDO::getEndStatus, reqVO.getEndStatus())
+                .eqIfPresent(CrmBusinessDO::getEndRemark, reqVO.getEndRemark())
+                .betweenIfPresent(CrmBusinessDO::getContactLastTime, reqVO.getContactLastTime())
+                .eqIfPresent(CrmBusinessDO::getFollowUpStatus, reqVO.getFollowUpStatus())
+                .orderByDesc(CrmBusinessDO::getId));
+    }
+
+}

+ 75 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java

@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.module.crm.service.business;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessCreateReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessExportReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessPageReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessUpdateReqVO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 商机 Service 接口
+ *
+ * @author ljlleo
+ */
+public interface CrmBusinessService {
+
+    /**
+     * 创建商机
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createBusiness(@Valid CrmBusinessCreateReqVO createReqVO);
+
+    /**
+     * 更新商机
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateBusiness(@Valid CrmBusinessUpdateReqVO updateReqVO);
+
+    /**
+     * 删除商机
+     *
+     * @param id 编号
+     */
+    void deleteBusiness(Long id);
+
+    /**
+     * 获得商机
+     *
+     * @param id 编号
+     * @return 商机
+     */
+    CrmBusinessDO getBusiness(Long id);
+
+    /**
+     * 获得商机列表
+     *
+     * @param ids 编号
+     * @return 商机列表
+     */
+    List<CrmBusinessDO> getBusinessList(Collection<Long> ids);
+
+    /**
+     * 获得商机分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 商机分页
+     */
+    PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO);
+
+    /**
+     * 获得商机列表, 用于 Excel 导出
+     *
+     * @param exportReqVO 查询条件
+     * @return 商机列表
+     */
+    List<CrmBusinessDO> getBusinessList(CrmBusinessExportReqVO exportReqVO);
+
+}

+ 90 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java

@@ -0,0 +1,90 @@
+package cn.iocoder.yudao.module.crm.service.business;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessCreateReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessExportReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessPageReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessUpdateReqVO;
+import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
+
+/**
+ * 商机 Service 实现类
+ *
+ * @author ljlleo
+ */
+@Service
+@Validated
+public class CrmBusinessServiceImpl implements CrmBusinessService {
+
+    @Resource
+    private CrmBusinessMapper businessMapper;
+
+    @Override
+    public Long createBusiness(CrmBusinessCreateReqVO createReqVO) {
+        // 插入
+        CrmBusinessDO business = CrmBusinessConvert.INSTANCE.convert(createReqVO);
+        businessMapper.insert(business);
+        // 返回
+        return business.getId();
+    }
+
+    @Override
+    public void updateBusiness(CrmBusinessUpdateReqVO updateReqVO) {
+        // 校验存在
+        validateBusinessExists(updateReqVO.getId());
+        // 更新
+        CrmBusinessDO updateObj = CrmBusinessConvert.INSTANCE.convert(updateReqVO);
+        businessMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteBusiness(Long id) {
+        // 校验存在
+        validateBusinessExists(id);
+        // 删除
+        businessMapper.deleteById(id);
+    }
+
+    private void validateBusinessExists(Long id) {
+        if (businessMapper.selectById(id) == null) {
+            throw exception(BUSINESS_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public CrmBusinessDO getBusiness(Long id) {
+        return businessMapper.selectById(id);
+    }
+
+    @Override
+    public List<CrmBusinessDO> getBusinessList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return ListUtil.empty();
+        }
+        return businessMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO) {
+        return businessMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<CrmBusinessDO> getBusinessList(CrmBusinessExportReqVO exportReqVO) {
+        return businessMapper.selectList(exportReqVO);
+    }
+
+}

+ 12 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/business/CrmBusinessMapper.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper">
+
+    <!--
+        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
+        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
+        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
+        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
+     -->
+
+</mapper>

+ 283 - 0
yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImplTest.java

@@ -0,0 +1,283 @@
+package cn.iocoder.yudao.module.crm.service.business;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessCreateReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessExportReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessPageReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.CrmBusinessUpdateReqVO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link CrmBusinessServiceImpl} 的单元测试类
+ *
+ * @author ljlleo
+ */
+@Import(CrmBusinessServiceImpl.class)
+public class CrmBusinessServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private CrmBusinessServiceImpl businessService;
+
+    @Resource
+    private CrmBusinessMapper businessMapper;
+
+    @Test
+    public void testCreateBusiness_success() {
+        // 准备参数
+        CrmBusinessCreateReqVO reqVO = randomPojo(CrmBusinessCreateReqVO.class);
+
+        // 调用
+        Long businessId = businessService.createBusiness(reqVO);
+        // 断言
+        assertNotNull(businessId);
+        // 校验记录的属性是否正确
+        CrmBusinessDO business = businessMapper.selectById(businessId);
+        assertPojoEquals(reqVO, business);
+    }
+
+    @Test
+    public void testUpdateBusiness_success() {
+        // mock 数据
+        CrmBusinessDO dbBusiness = randomPojo(CrmBusinessDO.class);
+        businessMapper.insert(dbBusiness);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        CrmBusinessUpdateReqVO reqVO = randomPojo(CrmBusinessUpdateReqVO.class, o -> {
+            o.setId(dbBusiness.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        businessService.updateBusiness(reqVO);
+        // 校验是否更新正确
+        CrmBusinessDO business = businessMapper.selectById(reqVO.getId()); // 获取最新的
+        assertPojoEquals(reqVO, business);
+    }
+
+    @Test
+    public void testUpdateBusiness_notExists() {
+        // 准备参数
+        CrmBusinessUpdateReqVO reqVO = randomPojo(CrmBusinessUpdateReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> businessService.updateBusiness(reqVO), BUSINESS_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteBusiness_success() {
+        // mock 数据
+        CrmBusinessDO dbBusiness = randomPojo(CrmBusinessDO.class);
+        businessMapper.insert(dbBusiness);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbBusiness.getId();
+
+        // 调用
+        businessService.deleteBusiness(id);
+       // 校验数据不存在了
+       assertNull(businessMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteBusiness_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> businessService.deleteBusiness(id), BUSINESS_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetBusinessPage() {
+       // mock 数据
+       CrmBusinessDO dbBusiness = randomPojo(CrmBusinessDO.class, o -> { // 等会查询到
+           o.setName(null);
+           o.setStatusTypeId(null);
+           o.setStatusId(null);
+           o.setContactNextTime(null);
+           o.setCustomerId(null);
+           o.setDealTime(null);
+           o.setPrice(null);
+           o.setDiscountPercent(null);
+           o.setProductPrice(null);
+           o.setRemark(null);
+           o.setOwnerUserId(null);
+           o.setCreateTime(null);
+           o.setRoUserIds(null);
+           o.setRwUserIds(null);
+           o.setEndStatus(null);
+           o.setEndRemark(null);
+           o.setContactLastTime(null);
+           o.setFollowUpStatus(null);
+       });
+       businessMapper.insert(dbBusiness);
+       // 测试 name 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setName(null)));
+       // 测试 statusTypeId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setStatusTypeId(null)));
+       // 测试 statusId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setStatusId(null)));
+       // 测试 contactNextTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setContactNextTime(null)));
+       // 测试 customerId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setCustomerId(null)));
+       // 测试 dealTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setDealTime(null)));
+       // 测试 price 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setPrice(null)));
+       // 测试 discountPercent 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setDiscountPercent(null)));
+       // 测试 productPrice 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setProductPrice(null)));
+       // 测试 remark 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setRemark(null)));
+       // 测试 ownerUserId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setOwnerUserId(null)));
+       // 测试 createTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setCreateTime(null)));
+       // 测试 roUserIds 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setRoUserIds(null)));
+       // 测试 rwUserIds 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setRwUserIds(null)));
+       // 测试 endStatus 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setEndStatus(null)));
+       // 测试 endRemark 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setEndRemark(null)));
+       // 测试 contactLastTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setContactLastTime(null)));
+       // 测试 followUpStatus 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setFollowUpStatus(null)));
+       // 准备参数
+       CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO();
+       reqVO.setName(null);
+       reqVO.setStatusTypeId(null);
+       reqVO.setStatusId(null);
+       reqVO.setContactNextTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setCustomerId(null);
+       reqVO.setDealTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setPrice(null);
+       reqVO.setDiscountPercent(null);
+       reqVO.setProductPrice(null);
+       reqVO.setRemark(null);
+       reqVO.setOwnerUserId(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setRoUserIds(null);
+       reqVO.setRwUserIds(null);
+       reqVO.setEndStatus(null);
+       reqVO.setEndRemark(null);
+       reqVO.setContactLastTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setFollowUpStatus(null);
+
+       // 调用
+       PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbBusiness, pageResult.getList().get(0));
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetBusinessList() {
+       // mock 数据
+       CrmBusinessDO dbBusiness = randomPojo(CrmBusinessDO.class, o -> { // 等会查询到
+           o.setName(null);
+           o.setStatusTypeId(null);
+           o.setStatusId(null);
+           o.setContactNextTime(null);
+           o.setCustomerId(null);
+           o.setDealTime(null);
+           o.setPrice(null);
+           o.setDiscountPercent(null);
+           o.setProductPrice(null);
+           o.setRemark(null);
+           o.setOwnerUserId(null);
+           o.setCreateTime(null);
+           o.setRoUserIds(null);
+           o.setRwUserIds(null);
+           o.setEndStatus(null);
+           o.setEndRemark(null);
+           o.setContactLastTime(null);
+           o.setFollowUpStatus(null);
+       });
+       businessMapper.insert(dbBusiness);
+       // 测试 name 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setName(null)));
+       // 测试 statusTypeId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setStatusTypeId(null)));
+       // 测试 statusId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setStatusId(null)));
+       // 测试 contactNextTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setContactNextTime(null)));
+       // 测试 customerId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setCustomerId(null)));
+       // 测试 dealTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setDealTime(null)));
+       // 测试 price 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setPrice(null)));
+       // 测试 discountPercent 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setDiscountPercent(null)));
+       // 测试 productPrice 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setProductPrice(null)));
+       // 测试 remark 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setRemark(null)));
+       // 测试 ownerUserId 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setOwnerUserId(null)));
+       // 测试 createTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setCreateTime(null)));
+       // 测试 roUserIds 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setRoUserIds(null)));
+       // 测试 rwUserIds 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setRwUserIds(null)));
+       // 测试 endStatus 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setEndStatus(null)));
+       // 测试 endRemark 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setEndRemark(null)));
+       // 测试 contactLastTime 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setContactLastTime(null)));
+       // 测试 followUpStatus 不匹配
+       businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setFollowUpStatus(null)));
+       // 准备参数
+       CrmBusinessExportReqVO reqVO = new CrmBusinessExportReqVO();
+       reqVO.setName(null);
+       reqVO.setStatusTypeId(null);
+       reqVO.setStatusId(null);
+       reqVO.setContactNextTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setCustomerId(null);
+       reqVO.setDealTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setPrice(null);
+       reqVO.setDiscountPercent(null);
+       reqVO.setProductPrice(null);
+       reqVO.setRemark(null);
+       reqVO.setOwnerUserId(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setRoUserIds(null);
+       reqVO.setRwUserIds(null);
+       reqVO.setEndStatus(null);
+       reqVO.setEndRemark(null);
+       reqVO.setContactLastTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+       reqVO.setFollowUpStatus(null);
+
+       // 调用
+       List<CrmBusinessDO> list = businessService.getBusinessList(reqVO);
+       // 断言
+       assertEquals(1, list.size());
+       assertPojoEquals(dbBusiness, list.get(0));
+    }
+
+}