CRM: 完善销售漏斗

This commit is contained in:
puhui999 2024-04-15 15:26:05 +08:00
parent ed5f3a6bc2
commit 5cf34c2ba7
17 changed files with 110 additions and 314 deletions

View File

@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.CrmBusinessController; import cn.iocoder.yudao.module.crm.controller.admin.business.CrmBusinessController;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticBusinessEndStatusRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelSummaryRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
@ -31,37 +31,35 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Validated @Validated
public class CrmStatisticsFunnelController { public class CrmStatisticsFunnelController {
// TODO @puhui999crmStatisticsFunnelService 改成 funnelService 更好点哈
@Resource @Resource
private CrmStatisticsFunnelService crmStatisticsFunnelService; private CrmStatisticsFunnelService funnelService;
@GetMapping("/get-funnel-summary") @GetMapping("/get-funnel-summary")
@Operation(summary = "获取销售漏斗统计数据", description = "用于【销售漏斗】页面的【销售漏斗分析】") @Operation(summary = "获取销售漏斗统计数据", description = "用于【销售漏斗】页面的【销售漏斗分析】")
@PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')") @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
public CommonResult<CrmStatisticFunnelRespVO> getFunnelSummary(@Valid CrmStatisticsFunnelReqVO reqVO) { public CommonResult<CrmStatisticFunnelSummaryRespVO> getFunnelSummary(@Valid CrmStatisticsFunnelReqVO reqVO) {
return success(crmStatisticsFunnelService.getFunnelSummary(reqVO)); return success(funnelService.getFunnelSummary(reqVO));
} }
// TODO @puhui这个接口应该是 getBusinessSummaryByEndStatus这样更统一哈 @GetMapping("/get-business-summary-by-end-status")
@GetMapping("/get-business-end-status-summary")
@Operation(summary = "获取商机结束状态统计", description = "用于【销售漏斗】页面的【销售漏斗分析】") @Operation(summary = "获取商机结束状态统计", description = "用于【销售漏斗】页面的【销售漏斗分析】")
@PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')") @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
public CommonResult<List<CrmStatisticBusinessEndStatusRespVO>> getBusinessEndStatusSummary(@Valid CrmStatisticsFunnelReqVO reqVO) { public CommonResult<List<CrmStatisticsBusinessSummaryByEndStatusRespVO>> getBusinessSummaryByEndStatus(@Valid CrmStatisticsFunnelReqVO reqVO) {
return success(crmStatisticsFunnelService.getBusinessEndStatusSummary(reqVO)); return success(funnelService.getBusinessSummaryByEndStatus(reqVO));
} }
@GetMapping("/get-business-summary-by-date") @GetMapping("/get-business-summary-by-date")
@Operation(summary = "获取新增商机分析(按日期)", description = "用于【销售漏斗】页面") @Operation(summary = "获取新增商机分析(按日期)", description = "用于【销售漏斗】页面")
@PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')") @PreAuthorize("@ss.hasPermission('crm:statistics-funnel:query')")
public CommonResult<List<CrmStatisticsBusinessSummaryByDateRespVO>> getBusinessSummaryByDate(@Valid CrmStatisticsFunnelReqVO reqVO) { public CommonResult<List<CrmStatisticsBusinessSummaryByDateRespVO>> getBusinessSummaryByDate(@Valid CrmStatisticsFunnelReqVO reqVO) {
return success(crmStatisticsFunnelService.getBusinessSummaryByDate(reqVO)); return success(funnelService.getBusinessSummaryByDate(reqVO));
} }
@GetMapping("/get-business-page-by-date") @GetMapping("/get-business-page-by-date")
@Operation(summary = "获得商机分页(按日期)", description = "用于【销售漏斗】页面的【新增商机分析】") @Operation(summary = "获得商机分页(按日期)", description = "用于【销售漏斗】页面的【新增商机分析】")
@PreAuthorize("@ss.hasPermission('crm:business:query')") @PreAuthorize("@ss.hasPermission('crm:business:query')")
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByDate(@Valid CrmStatisticsFunnelReqVO pageVO) { public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByDate(@Valid CrmStatisticsFunnelReqVO pageVO) {
PageResult<CrmBusinessDO> pageResult = crmStatisticsFunnelService.getBusinessPageByDate(pageVO); PageResult<CrmBusinessDO> pageResult = funnelService.getBusinessPageByDate(pageVO);
return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal())); return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
} }

View File

@ -5,12 +5,11 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
// TODO @puhui999改成 CrmStatisticFunnelSummaryRespVO 更有统计的味道
@Schema(description = "管理后台 - CRM 销售漏斗 Response VO") @Schema(description = "管理后台 - CRM 销售漏斗 Response VO")
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Data @Data
public class CrmStatisticFunnelRespVO { public class CrmStatisticFunnelSummaryRespVO {
@Schema(description = "客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long customerCount; private Long customerCount;
@ -18,8 +17,7 @@ public class CrmStatisticFunnelRespVO {
@Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long businessCount; private Long businessCount;
// TODO @puhui999这个改成 businessWinCount 可能会更合适点哈
@Schema(description = "赢单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "赢单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long winCount; private Long businessWinCount;
} }

View File

@ -1,107 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
// TODO @puhui999是不是可以删除哈
@Schema(description = "管理后台 - CRM 商机 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CrmStatisticsBusinessRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
@ExcelProperty("编号")
private Long id;
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@ExcelProperty("商机名称")
private String name;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
private Long customerId;
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@ExcelProperty("客户名称")
private String customerName;
@Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example ="true")
@ExcelProperty("跟进状态")
private Boolean followUpStatus;
@Schema(description = "最后跟进时间")
@ExcelProperty("最后跟进时间")
private LocalDateTime contactLastTime;
@Schema(description = "下次联系时间")
@ExcelProperty("下次联系时间")
private LocalDateTime contactNextTime;
@Schema(description = "负责人的用户编号", example = "25682")
@ExcelProperty("负责人的用户编号")
private Long ownerUserId;
@Schema(description = "负责人名字", example = "25682")
@ExcelProperty("负责人名字")
private String ownerUserName;
@Schema(description = "负责人部门")
@ExcelProperty("负责人部门")
private String ownerUserDeptName;
@Schema(description = "商机状态组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
private Long statusTypeId;
@Schema(description = "商机状组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")
@ExcelProperty("商机状态组")
private String statusTypeName;
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
private Long statusId;
@Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进中")
@ExcelProperty("商机状态")
private String statusName;
@Schema
@ExcelProperty("结束状态")
private Integer endStatus;
@ExcelProperty("结束时的备注")
private String endRemark;
@Schema(description = "预计成交日期")
@ExcelProperty("预计成交日期")
private LocalDateTime dealTime;
@Schema(description = "产品总金额", example = "12025")
@ExcelProperty("产品总金额")
private BigDecimal totalProductPrice;
@Schema(description = "整单折扣")
@ExcelProperty("整单折扣")
private BigDecimal discountPercent;
@Schema(description = "商机总金额", example = "12371")
@ExcelProperty("商机总金额")
private BigDecimal totalPrice;
@Schema(description = "备注", example = "随便")
@ExcelProperty("备注")
private String remark;
@Schema(description = "创建人", example = "1024")
@ExcelProperty("创建人")
private String creator;
@Schema(description = "创建人名字", example = "芋道源码")
@ExcelProperty("创建人名字")
private String creatorName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("更新时间")
private LocalDateTime updateTime;
}

View File

@ -15,8 +15,7 @@ public class CrmStatisticsBusinessSummaryByDateRespVO {
@Schema(description = "新增商机数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "新增商机数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer businessCreateCount; private Integer businessCreateCount;
// TODO @puhui999是不是金额哈不是数量
@Schema(description = "新增商机金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "新增商机金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private BigDecimal businessDealCount; private BigDecimal totalPrice;
} }

View File

@ -7,12 +7,11 @@ import lombok.NoArgsConstructor;
import java.math.BigDecimal; import java.math.BigDecimal;
// TODO @puhui999改成 CrmStatisticsBusinessSummaryByEndStatusRespVO按照结束状态
@Schema(description = "管理后台 - CRM 商机结束状态统计 Response VO") @Schema(description = "管理后台 - CRM 商机结束状态统计 Response VO")
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Data @Data
public class CrmStatisticBusinessEndStatusRespVO { public class CrmStatisticsBusinessSummaryByEndStatusRespVO {
@Schema(description = "结束状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "结束状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer endStatus; private Integer endStatus;

View File

@ -5,9 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -17,9 +16,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@Schema(description = "管理后台 - CRM 销售漏斗 Request VO") @Schema(description = "管理后台 - CRM 销售漏斗 Request VO")
@Data @Data
// TODO @puhui999不用写 EqualsAndHashCodeToString已经全局 lombok
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmStatisticsFunnelReqVO extends PageParam { public class CrmStatisticsFunnelReqVO extends PageParam {
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ -43,13 +39,9 @@ public class CrmStatisticsFunnelReqVO extends PageParam {
@InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
private Integer interval; private Integer interval;
// TODO @puhui999这个全部前端传递哈参考 CrmStatisticsCustomerReqVO
/**
* 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
* 并作为参数传递给Mapper
*/
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Size(min = 2, max = 2, message = "请选择时间范围")
private LocalDateTime[] times; private LocalDateTime[] times;
} }

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
@ -31,12 +32,9 @@ public class CrmStatisticsPortraitReqVO {
@Schema(description = "负责人用户 id 集合", hidden = true, example = "2") @Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
private List<Long> userIds; private List<Long> userIds;
/**
* 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间
* 并作为参数传递给Mapper
*/
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Size(min = 2, max = 2, message = "请选择时间范围")
private LocalDateTime[] times; private LocalDateTime[] times;
} }

View File

@ -1,18 +1,17 @@
package cn.iocoder.yudao.module.crm.dal.mysql.business; package cn.iocoder.yudao.module.crm.dal.mysql.business;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -66,32 +65,10 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
.eq(CrmBusinessDO::getOwnerUserId, ownerUserId)); .eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
} }
default Long selectCountByOwnerUserIdsAndEndStatus(Collection<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus) { default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {
return selectCount(new LambdaQueryWrapperX<CrmBusinessDO>() return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds) .in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())
.eqIfPresent(CrmBusinessDO::getEndStatus, endStatus) .betweenIfPresent(CrmBusinessDO::getCreateTime, pageVO.getTimes()));
.betweenIfPresent(CrmBusinessDO::getCreateTime, times));
}
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算
default List<CrmBusinessDO> selectListByOwnerUserIdsAndEndStatusNotNull(Collection<Long> ownerUserIds, LocalDateTime[] times) {
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
.betweenIfPresent(CrmBusinessDO::getCreateTime, times)
.isNotNull(CrmBusinessDO::getEndStatus));
}
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算
default List<CrmBusinessDO> selectListByOwnerUserIdsAndDate(Collection<Long> ownerUserIds, LocalDateTime[] times) {
return selectList(new LambdaQueryWrapperX<CrmBusinessDO>()
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
.betweenIfPresent(CrmBusinessDO::getCreateTime, times));
}
default PageResult<CrmBusinessDO> selectPage(Collection<Long> ownerUserIds, LocalDateTime[] times, Integer pageNo, Integer pageSize) {
return selectPage(new PageParam().setPageNo(pageNo).setPageSize(pageSize), new LambdaQueryWrapperX<CrmBusinessDO>()
.in(CrmBusinessDO::getOwnerUserId, ownerUserIds)
.betweenIfPresent(CrmBusinessDO::getCreateTime, times));
} }
} }

View File

@ -186,11 +186,4 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
return selectCount(query); return selectCount(query);
} }
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算
default Long selectCountByOwnerUserIds(Collection<Long> ownerUserIds, LocalDateTime[] times){
return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()
.in(CrmCustomerDO::getOwnerUserId, ownerUserIds)
.betweenIfPresent(CrmCustomerDO::getCreateTime, times));
}
} }

View File

@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.crm.dal.mysql.statistics; package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@ -15,6 +16,12 @@ import java.util.List;
@Mapper @Mapper
public interface CrmStatisticsFunnelMapper { public interface CrmStatisticsFunnelMapper {
List<CrmStatisticsBusinessSummaryByDateRespVO> selectBusinessCreateCountGroupByDate(CrmStatisticsFunnelReqVO reqVO); Long selectCustomerCountByDate(CrmStatisticsFunnelReqVO reqVO);
Long selectBusinessCountByDateAndEndStatus(@Param("reqVO") CrmStatisticsFunnelReqVO reqVO, @Param("status") Integer status);
List<CrmStatisticsBusinessSummaryByEndStatusRespVO> selectBusinessSummaryListGroupByEndStatus(CrmStatisticsFunnelReqVO reqVO);
List<CrmStatisticsBusinessSummaryByDateRespVO> selectBusinessSummaryGroupByDate(CrmStatisticsFunnelReqVO reqVO);
} }

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
@ -194,45 +195,12 @@ public interface CrmBusinessService {
*/ */
List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId); List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
/**
* 获得商机数
*
* @param ownerUserIds 负责人编号
* @param times 时间范围
* @param endStatus 商机结束状态允许为空
* @return 商机数
*/
Long getBusinessCountByOwnerUserIdsAndEndStatus(List<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus);
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算
/**
* 获得商机列表数据统计
*
* @param ownerUserIds 负责人编号
* @param times 时间范围
* @return 商机列表
*/
List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times);
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算
/**
* 获得商机列表数据统计
*
* @param ownerUserIds 负责人编号
* @param times 时间范围
* @return 商机列表
*/
List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndDate(List<Long> ownerUserIds, LocalDateTime[] times);
/** /**
* 获得商机分页目前用于数据统计 * 获得商机分页目前用于数据统计
* *
* @param ownerUserIds 负责人编号 * @param pageVO 请求
* @param times 时间范围
* @param pageNo 页码 TODO @puhui999直接传递 CrmStatisticsFunnelReqVO 虽然有点耦合但是更清晰一点
* @param pageSize 数量
* @return 商机分页 * @return 商机分页
*/ */
PageResult<CrmBusinessDO> getBusinessPageByDate(List<Long> ownerUserIds, LocalDateTime[] times, Integer pageNo, Integer pageSize); PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO);
} }

View File

@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateStatusReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
@ -376,35 +377,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
} }
@Override @Override
public Long getBusinessCountByOwnerUserIdsAndEndStatus(List<Long> ownerUserIds, LocalDateTime[] times, Integer endStatus) { public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {
if (CollUtil.isEmpty(ownerUserIds)) { return businessMapper.selectPage(pageVO);
return 0L;
}
return businessMapper.selectCountByOwnerUserIdsAndEndStatus(convertSet(ownerUserIds), times, endStatus);
}
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算
@Override
public List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndEndStatusNotNull(List<Long> ownerUserIds, LocalDateTime[] times) {
if (CollUtil.isEmpty(ownerUserIds)) {
return Collections.emptyList();
}
return businessMapper.selectListByOwnerUserIdsAndEndStatusNotNull(convertSet(ownerUserIds), times);
}
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算
@Override
public List<CrmBusinessDO> getBusinessListByOwnerUserIdsAndDate(List<Long> ownerUserIds, LocalDateTime[] times) {
if (CollUtil.isEmpty(ownerUserIds)) {
return Collections.emptyList();
}
return businessMapper.selectListByOwnerUserIdsAndDate(convertSet(ownerUserIds), times);
}
@Override
public PageResult<CrmBusinessDO> getBusinessPageByDate(List<Long> ownerUserIds, LocalDateTime[] times, Integer pageNo, Integer pageSize) {
return businessMapper.selectPage(ownerUserIds, times, pageNo, pageSize);
} }
} }

View File

@ -195,13 +195,4 @@ public interface CrmCustomerService {
*/ */
int autoPutCustomerPool(); int autoPutCustomerPool();
/**
* 获得客户数
*
* @param ownerUserIds 负责人编号
* @param times 时间范围
* @return 客户数
*/
Long getCustomerCountByOwnerUserIds(List<Long> ownerUserIds, LocalDateTime[] times);
} }

View File

@ -651,14 +651,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
} }
} }
@Override
public Long getCustomerCountByOwnerUserIds(List<Long> ownerUserIds, LocalDateTime[] times) {
if (CollUtil.isEmpty(ownerUserIds)) {
return 0L;
}
return customerMapper.selectCountByOwnerUserIds(convertSet(ownerUserIds), times);
}
/** /**
* 获得自身的代理对象解决 AOP 生效问题 * 获得自身的代理对象解决 AOP 生效问题
* *

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.crm.service.statistics; package cn.iocoder.yudao.module.crm.service.statistics;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticBusinessEndStatusRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelSummaryRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
@ -22,7 +22,7 @@ public interface CrmStatisticsFunnelService {
* @param reqVO 请求 * @param reqVO 请求
* @return 销售漏斗数据 * @return 销售漏斗数据
*/ */
CrmStatisticFunnelRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO); CrmStatisticFunnelSummaryRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO);
/** /**
* 获得商机结束状态统计 * 获得商机结束状态统计
@ -30,7 +30,7 @@ public interface CrmStatisticsFunnelService {
* @param reqVO 请求 * @param reqVO 请求
* @return 商机结束状态统计 * @return 商机结束状态统计
*/ */
List<CrmStatisticBusinessEndStatusRespVO> getBusinessEndStatusSummary(CrmStatisticsFunnelReqVO reqVO); List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO);
/** /**
* 获取新增商机分析(按日期) * 获取新增商机分析(按日期)

View File

@ -4,15 +4,14 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticBusinessEndStatusRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelSummaryRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticFunnelRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsFunnelReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper;
import cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum; import cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@ -22,14 +21,10 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/** /**
* CRM 销售漏斗分析 Service 实现类 * CRM 销售漏斗分析 Service 实现类
@ -45,15 +40,12 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
@Resource @Resource
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@Resource @Resource
private CrmCustomerService customerService;
@Resource
private CrmBusinessService businessService; private CrmBusinessService businessService;
@Resource @Resource
private DeptApi deptApi; private DeptApi deptApi;
// TODO @puhui999貌似想了下可能还是得按照
@Override @Override
public CrmStatisticFunnelRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO) { public CrmStatisticFunnelSummaryRespVO getFunnelSummary(CrmStatisticsFunnelReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
List<Long> userIds = getUserIds(reqVO); List<Long> userIds = getUserIds(reqVO);
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(userIds)) {
@ -62,34 +54,22 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
reqVO.setUserIds(userIds); reqVO.setUserIds(userIds);
// 2. 获得漏斗数据 // 2. 获得漏斗数据
return new CrmStatisticFunnelRespVO( Long customerCount = funnelMapper.selectCustomerCountByDate(reqVO);
customerService.getCustomerCountByOwnerUserIds(userIds, reqVO.getTimes()), Long businessCount = funnelMapper.selectBusinessCountByDateAndEndStatus(reqVO, null);
businessService.getBusinessCountByOwnerUserIdsAndEndStatus(userIds, reqVO.getTimes(), null), Long businessWinCount = funnelMapper.selectBusinessCountByDateAndEndStatus(reqVO, CrmBusinessEndStatusEnum.WIN.getStatus());
businessService.getBusinessCountByOwnerUserIdsAndEndStatus(userIds, reqVO.getTimes(), CrmBusinessEndStatusEnum.WIN.getStatus()) return new CrmStatisticFunnelSummaryRespVO(customerCount, businessCount, businessWinCount);
);
} }
@Override @Override
public List<CrmStatisticBusinessEndStatusRespVO> getBusinessEndStatusSummary(CrmStatisticsFunnelReqVO reqVO) { public List<CrmStatisticsBusinessSummaryByEndStatusRespVO> getBusinessSummaryByEndStatus(CrmStatisticsFunnelReqVO reqVO) {
// 1. 获得用户编号数组 // 1. 获得用户编号数组
reqVO.setUserIds(getUserIds(reqVO)); reqVO.setUserIds(getUserIds(reqVO));
if (CollUtil.isEmpty(reqVO.getUserIds())) { if (CollUtil.isEmpty(reqVO.getUserIds())) {
return Collections.emptyList(); return Collections.emptyList();
} }
// TODO @puhui999这个可以优化下通过统计 sql不通过内存计算 // 2. 获得统计数据
// 2.1 获得用户负责的商机 return funnelMapper.selectBusinessSummaryListGroupByEndStatus(reqVO);
List<CrmBusinessDO> businessList = businessService.getBusinessListByOwnerUserIdsAndEndStatusNotNull(reqVO.getUserIds(), reqVO.getTimes());
// 2.2 统计各阶段数据
Map<Integer, List<CrmBusinessDO>> businessMap = convertMultiMap(businessList, CrmBusinessDO::getEndStatus);
return convertList(CrmBusinessEndStatusEnum.values(), endStatusEnum -> {
List<CrmBusinessDO> list = businessMap.get(endStatusEnum.getStatus());
if (CollUtil.isEmpty(list)) {
return new CrmStatisticBusinessEndStatusRespVO(endStatusEnum.getStatus(), 0L, BigDecimal.ZERO);
}
return new CrmStatisticBusinessEndStatusRespVO(endStatusEnum.getStatus(), (long) list.size(),
getSumValue(list, CrmBusinessDO::getTotalPrice, BigDecimal::add));
});
} }
@Override @Override
@ -101,26 +81,20 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
} }
// 2. 按天统计获取分项统计数据 // 2. 按天统计获取分项统计数据
// TODO @puhui999可以这个统计返回的时候就把数量金额一起统计好 List<CrmStatisticsBusinessSummaryByDateRespVO> businessSummaryList = funnelMapper.selectBusinessSummaryGroupByDate(reqVO);
List<CrmStatisticsBusinessSummaryByDateRespVO> businessCreateCountList = funnelMapper.selectBusinessCreateCountGroupByDate(reqVO);
List<CrmBusinessDO> businessList = businessService.getBusinessListByOwnerUserIdsAndDate(reqVO.getUserIds(), reqVO.getTimes());
Map<String, BigDecimal> businessDealCountMap = businessList.stream().collect(Collectors.groupingBy(business ->
business.getCreateTime().format(DateTimeFormatter.ofPattern(FORMAT_YEAR_MONTH_DAY)),
Collectors.reducing(BigDecimal.ZERO, CrmBusinessDO::getTotalPrice, BigDecimal::add)));
// 3. 按照日期间隔合并数据 // 3. 按照日期间隔合并数据
List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
return convertList(timeRanges, times -> { return convertList(timeRanges, times -> {
Integer businessCreateCount = businessCreateCountList.stream() Integer businessCreateCount = businessSummaryList.stream()
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
.mapToInt(CrmStatisticsBusinessSummaryByDateRespVO::getBusinessCreateCount).sum(); .mapToInt(CrmStatisticsBusinessSummaryByDateRespVO::getBusinessCreateCount).sum();
BigDecimal businessDealCount = businessDealCountMap.entrySet().stream() BigDecimal businessDealCount = businessSummaryList.stream()
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getKey())) .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime()))
.map(Map.Entry::getValue) .map(CrmStatisticsBusinessSummaryByDateRespVO::getTotalPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
return new CrmStatisticsBusinessSummaryByDateRespVO() return new CrmStatisticsBusinessSummaryByDateRespVO()
.setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
.setBusinessCreateCount(businessCreateCount).setBusinessDealCount(businessDealCount); .setBusinessCreateCount(businessCreateCount).setTotalPrice(businessDealCount);
}); });
} }
@ -132,7 +106,7 @@ public class CrmStatisticsFunnelServiceImpl implements CrmStatisticsFunnelServic
return PageResult.empty(); return PageResult.empty();
} }
// 2. 执行查询 // 2. 执行查询
return businessService.getBusinessPageByDate(pageVO.getUserIds(), pageVO.getTimes(), pageVO.getPageNo(), pageVO.getPageSize()); return businessService.getBusinessPageByDate(pageVO);
} }
/** /**

View File

@ -2,18 +2,61 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!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.statistics.CrmStatisticsFunnelMapper"> <mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsFunnelMapper">
<select id="selectBusinessCreateCountGroupByDate" <select id="selectCustomerCountByDate" resultType="java.lang.Long">
SELECT COUNT(*)
FROM crm_customer
WHERE deleted = 0
AND owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId}
</foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
#{times[1],javaType=java.time.LocalDateTime}
</select>
<select id="selectBusinessCountByDateAndEndStatus" resultType="java.lang.Long">
SELECT COUNT(*)
FROM crm_business
WHERE deleted = 0
<if test="status != null">
AND end_status = #{status}
</if>
AND owner_user_id IN
<foreach collection="reqVO.userIds" item="userId" open="(" close=")" separator=",">
#{userId}
</foreach>
AND create_time BETWEEN #{reqVO.times[0],javaType=java.time.LocalDateTime} AND
#{reqVO.times[1],javaType=java.time.LocalDateTime}
</select>
<select id="selectBusinessSummaryListGroupByEndStatus"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByEndStatusRespVO">
SELECT end_status AS endStatus,COUNT(*) AS businessCount, SUM(total_price) AS totalPrice
FROM crm_business
WHERE deleted = 0 AND end_status IS NOT NULL
AND owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId}
</foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
#{times[1],javaType=java.time.LocalDateTime}
GROUP BY end_status
</select>
<select id="selectBusinessSummaryGroupByDate"
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO"> resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel.CrmStatisticsBusinessSummaryByDateRespVO">
SELECT SELECT
DATE_FORMAT(create_time, '%Y-%m-%d') AS time, DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
COUNT(*) AS businessCreateCount COUNT(*) AS businessCreateCount,
SUM(total_price) AS totalPrice
FROM crm_business FROM crm_business
WHERE deleted = 0 WHERE deleted = 0
AND owner_user_id IN AND owner_user_id IN
<foreach collection="userIds" item="userId" open="(" close=")" separator=","> <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
#{userId} #{userId}
</foreach> </foreach>
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
#{times[1],javaType=java.time.LocalDateTime}
GROUP BY time GROUP BY time
</select> </select>