feat(crm): 添加销售报表功能模块
- 新增销售报表查询业务对象CrmSalesReportBo,支持多种查询条件 - 创建销售报表控制器CrmSalesReportController,提供汇总数据、订单明细、统计分析等接口 - 实现销售报表数据访问层CrmSalesReportMapper,包含销售汇总、订单明细、销售员统计等查询 - 开发销售报表服务层ICrmSalesReportService及其实现类,处理报表数据逻辑 - 设计销售报表视图对象CrmSalesReportVo,包含汇总信息、订单明细、统计分析等数据结构 - 集成Excel导出功能,支持订单明细、销售员统计、客户等级统计、行业统计的数据导出 - 实现多维度统计分析,包括销售员业绩、客户等级分布、行业分布等统计功能
This commit is contained in:
@@ -0,0 +1,127 @@
|
|||||||
|
package com.klp.crm.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.constraints.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import com.klp.common.annotation.Log;
|
||||||
|
import com.klp.common.core.controller.BaseController;
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.core.domain.R;
|
||||||
|
import com.klp.common.enums.BusinessType;
|
||||||
|
import com.klp.common.utils.poi.ExcelUtil;
|
||||||
|
import com.klp.crm.domain.vo.CrmSalesReportVo;
|
||||||
|
import com.klp.crm.domain.bo.CrmSalesReportBo;
|
||||||
|
import com.klp.crm.service.ICrmSalesReportService;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售报表
|
||||||
|
*
|
||||||
|
* @author klp
|
||||||
|
* @date 2025-12-29
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/salesReport")
|
||||||
|
public class CrmSalesReportController extends BaseController {
|
||||||
|
|
||||||
|
private final ICrmSalesReportService iCrmSalesReportService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售报表汇总数据
|
||||||
|
*/
|
||||||
|
@GetMapping("/summary")
|
||||||
|
public R<CrmSalesReportVo.SalesSummary> getSalesSummary(CrmSalesReportBo bo) {
|
||||||
|
CrmSalesReportVo.SalesSummary summary = iCrmSalesReportService.querySalesSummary(bo);
|
||||||
|
return R.ok(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询销售报表订单明细
|
||||||
|
*/
|
||||||
|
@GetMapping("/orderDetails")
|
||||||
|
public TableDataInfo<CrmSalesReportVo.OrderDetail> getOrderDetails(CrmSalesReportBo bo, PageQuery pageQuery) {
|
||||||
|
return iCrmSalesReportService.queryOrderDetailPageList(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询完整销售报表数据
|
||||||
|
*/
|
||||||
|
@GetMapping("/fullReport")
|
||||||
|
public R<CrmSalesReportVo> getFullSalesReport(CrmSalesReportBo bo, PageQuery pageQuery) {
|
||||||
|
CrmSalesReportVo reportVo = iCrmSalesReportService.queryFullSalesReport(bo, pageQuery);
|
||||||
|
return R.ok(reportVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售员统计数据
|
||||||
|
*/
|
||||||
|
@GetMapping("/salesmanStats")
|
||||||
|
public R<List<CrmSalesReportVo.SalesmanStat>> getSalesmanStats(CrmSalesReportBo bo) {
|
||||||
|
List<CrmSalesReportVo.SalesmanStat> stats = iCrmSalesReportService.querySalesmanStats(bo);
|
||||||
|
return R.ok(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询客户等级统计数据
|
||||||
|
*/
|
||||||
|
@GetMapping("/customerLevelStats")
|
||||||
|
public R<List<CrmSalesReportVo.CustomerLevelStat>> getCustomerLevelStats(CrmSalesReportBo bo) {
|
||||||
|
List<CrmSalesReportVo.CustomerLevelStat> stats = iCrmSalesReportService.queryCustomerLevelStats(bo);
|
||||||
|
return R.ok(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询行业统计数据
|
||||||
|
*/
|
||||||
|
@GetMapping("/industryStats")
|
||||||
|
public R<List<CrmSalesReportVo.IndustryStat>> getIndustryStats(CrmSalesReportBo bo) {
|
||||||
|
List<CrmSalesReportVo.IndustryStat> stats = iCrmSalesReportService.queryIndustryStats(bo);
|
||||||
|
return R.ok(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出销售报表订单明细
|
||||||
|
*/
|
||||||
|
@Log(title = "销售报表", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/exportOrderDetails")
|
||||||
|
public void exportOrderDetails(CrmSalesReportBo bo, HttpServletResponse response) {
|
||||||
|
List<CrmSalesReportVo.OrderDetail> list = iCrmSalesReportService.queryOrderDetailList(bo);
|
||||||
|
ExcelUtil.exportExcel(list, "销售报表订单明细", CrmSalesReportVo.OrderDetail.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出销售员统计数据
|
||||||
|
*/
|
||||||
|
@Log(title = "销售报表", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/exportSalesmanStats")
|
||||||
|
public void exportSalesmanStats(CrmSalesReportBo bo, HttpServletResponse response) {
|
||||||
|
List<CrmSalesReportVo.SalesmanStat> list = iCrmSalesReportService.querySalesmanStats(bo);
|
||||||
|
ExcelUtil.exportExcel(list, "销售员统计", CrmSalesReportVo.SalesmanStat.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出客户等级统计数据
|
||||||
|
*/
|
||||||
|
@Log(title = "销售报表", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/exportCustomerLevelStats")
|
||||||
|
public void exportCustomerLevelStats(CrmSalesReportBo bo, HttpServletResponse response) {
|
||||||
|
List<CrmSalesReportVo.CustomerLevelStat> list = iCrmSalesReportService.queryCustomerLevelStats(bo);
|
||||||
|
ExcelUtil.exportExcel(list, "客户等级统计", CrmSalesReportVo.CustomerLevelStat.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出行业统计数据
|
||||||
|
*/
|
||||||
|
@Log(title = "销售报表", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/exportIndustryStats")
|
||||||
|
public void exportIndustryStats(CrmSalesReportBo bo, HttpServletResponse response) {
|
||||||
|
List<CrmSalesReportVo.IndustryStat> list = iCrmSalesReportService.queryIndustryStats(bo);
|
||||||
|
ExcelUtil.exportExcel(list, "行业统计", CrmSalesReportVo.IndustryStat.class, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.klp.crm.domain.bo;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.klp.common.core.validate.AddGroup;
|
||||||
|
import com.klp.common.core.validate.EditGroup;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import javax.validation.constraints.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售报表查询业务对象
|
||||||
|
*
|
||||||
|
* @author klp
|
||||||
|
* @date 2025-12-29
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class CrmSalesReportBo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始时间
|
||||||
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
|
private Date startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束时间
|
||||||
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
|
private Date endTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售员列表
|
||||||
|
*/
|
||||||
|
private List<String> salesmanList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户ID列表
|
||||||
|
*/
|
||||||
|
private List<String> customerIdList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户等级列表
|
||||||
|
*/
|
||||||
|
private List<String> customerLevelList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行业列表
|
||||||
|
*/
|
||||||
|
private List<String> industryList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单状态列表
|
||||||
|
*/
|
||||||
|
private List<Long> orderStatusList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 财务状态列表
|
||||||
|
*/
|
||||||
|
private List<Long> financeStatusList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单类型列表
|
||||||
|
*/
|
||||||
|
private List<Long> orderTypeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小订单金额
|
||||||
|
*/
|
||||||
|
private java.math.BigDecimal minOrderAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大订单金额
|
||||||
|
*/
|
||||||
|
private java.math.BigDecimal maxOrderAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含异议订单
|
||||||
|
*/
|
||||||
|
private Boolean includeObjectionOrders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否只查询有未结款的订单
|
||||||
|
*/
|
||||||
|
private Boolean onlyUnpaidOrders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公司名称关键字
|
||||||
|
*/
|
||||||
|
private String companyNameKeyword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单编号关键字
|
||||||
|
*/
|
||||||
|
private String orderCodeKeyword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序字段
|
||||||
|
*/
|
||||||
|
private String orderBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序方向 ASC/DESC
|
||||||
|
*/
|
||||||
|
private String sortDirection;
|
||||||
|
}
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
package com.klp.crm.domain.vo;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售报表视图对象
|
||||||
|
*
|
||||||
|
* @author klp
|
||||||
|
* @date 2025-12-29
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ExcelIgnoreUnannotated
|
||||||
|
public class CrmSalesReportVo {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售汇总信息
|
||||||
|
*/
|
||||||
|
private SalesSummary salesSummary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单明细列表
|
||||||
|
*/
|
||||||
|
private List<OrderDetail> orderDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售汇总内部类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class SalesSummary {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总订单数
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "总订单数")
|
||||||
|
private Integer totalOrderCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总销售金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "总销售金额")
|
||||||
|
private BigDecimal totalSalesAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已完成订单数
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "已完成订单数")
|
||||||
|
private Integer completedOrderCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已完成销售金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "已完成销售金额")
|
||||||
|
private BigDecimal completedSalesAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未结款总金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "未结款总金额")
|
||||||
|
private BigDecimal totalUnpaidAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平均订单金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "平均订单金额")
|
||||||
|
private BigDecimal avgOrderAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售员统计
|
||||||
|
*/
|
||||||
|
private List<SalesmanStat> salesmanStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户等级统计
|
||||||
|
*/
|
||||||
|
private List<CustomerLevelStat> customerLevelStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行业统计
|
||||||
|
*/
|
||||||
|
private List<IndustryStat> industryStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单明细内部类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class OrderDetail {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单ID
|
||||||
|
*/
|
||||||
|
private String orderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单编号
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "订单编号")
|
||||||
|
private String orderCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户编码
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "客户编码")
|
||||||
|
private String customerCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公司名称
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "公司名称")
|
||||||
|
private String companyName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 联系人
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "联系人")
|
||||||
|
private String contactPerson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户等级
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "客户等级")
|
||||||
|
private String customerLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所属行业
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "所属行业")
|
||||||
|
private String industry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "订单金额")
|
||||||
|
private BigDecimal orderAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售员
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "销售员")
|
||||||
|
private String salesman;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交货日期
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "交货日期")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
|
private Date deliveryDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单状态
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "订单状态")
|
||||||
|
private Long orderStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 财务状态
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "财务状态")
|
||||||
|
private Long financeStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未结款金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "未结款金额")
|
||||||
|
private BigDecimal unpaidAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "创建时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产品明细数量
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "产品明细数量")
|
||||||
|
private Integer itemCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异议数量
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "异议数量")
|
||||||
|
private Integer objectionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售员统计内部类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class SalesmanStat {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售员
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "销售员")
|
||||||
|
private String salesman;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单数量
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "订单数量")
|
||||||
|
private Integer orderCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "销售金额")
|
||||||
|
private BigDecimal salesAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 占比
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "占比(%)")
|
||||||
|
private BigDecimal percentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户等级统计内部类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class CustomerLevelStat {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户等级
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "客户等级")
|
||||||
|
private String customerLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户数量
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "客户数量")
|
||||||
|
private Integer customerCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单数量
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "订单数量")
|
||||||
|
private Integer orderCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "销售金额")
|
||||||
|
private BigDecimal salesAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行业统计内部类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class IndustryStat {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行业
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "行业")
|
||||||
|
private String industry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户数量
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "客户数量")
|
||||||
|
private Integer customerCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单数量
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "订单数量")
|
||||||
|
private Integer orderCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售金额
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "销售金额")
|
||||||
|
private BigDecimal salesAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.klp.crm.mapper;
|
||||||
|
|
||||||
|
import com.klp.crm.domain.vo.CrmSalesReportVo;
|
||||||
|
import com.klp.crm.domain.bo.CrmSalesReportBo;
|
||||||
|
import com.klp.common.core.mapper.BaseMapperPlus;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售报表Mapper接口
|
||||||
|
*
|
||||||
|
* @author klp
|
||||||
|
* @date 2025-12-29
|
||||||
|
*/
|
||||||
|
public interface CrmSalesReportMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售汇总统计数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 销售汇总统计
|
||||||
|
*/
|
||||||
|
CrmSalesReportVo.SalesSummary selectSalesSummary(@Param("bo") CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询订单明细列表
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 订单明细列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.OrderDetail> selectOrderDetailList(@Param("bo") CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售员统计数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 销售员统计列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.SalesmanStat> selectSalesmanStats(@Param("bo") CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询客户等级统计数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 客户等级统计列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.CustomerLevelStat> selectCustomerLevelStats(@Param("bo") CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询行业统计数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 行业统计列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.IndustryStat> selectIndustryStats(@Param("bo") CrmSalesReportBo bo);
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.klp.crm.service;
|
||||||
|
|
||||||
|
import com.klp.crm.domain.vo.CrmSalesReportVo;
|
||||||
|
import com.klp.crm.domain.bo.CrmSalesReportBo;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售报表Service接口
|
||||||
|
*
|
||||||
|
* @author klp
|
||||||
|
* @date 2025-12-29
|
||||||
|
*/
|
||||||
|
public interface ICrmSalesReportService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售报表汇总数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 销售报表汇总数据
|
||||||
|
*/
|
||||||
|
CrmSalesReportVo.SalesSummary querySalesSummary(CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询销售报表订单明细
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @param pageQuery 分页参数
|
||||||
|
* @return 订单明细分页数据
|
||||||
|
*/
|
||||||
|
TableDataInfo<CrmSalesReportVo.OrderDetail> queryOrderDetailPageList(CrmSalesReportBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售报表订单明细列表
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 订单明细列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.OrderDetail> queryOrderDetailList(CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询完整销售报表数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @param pageQuery 分页参数
|
||||||
|
* @return 完整销售报表数据
|
||||||
|
*/
|
||||||
|
CrmSalesReportVo queryFullSalesReport(CrmSalesReportBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售员统计数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 销售员统计列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.SalesmanStat> querySalesmanStats(CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询客户等级统计数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 客户等级统计列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.CustomerLevelStat> queryCustomerLevelStats(CrmSalesReportBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询行业统计数据
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return 行业统计列表
|
||||||
|
*/
|
||||||
|
List<CrmSalesReportVo.IndustryStat> queryIndustryStats(CrmSalesReportBo bo);
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.klp.crm.service.impl;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.common.core.domain.PageQuery;
|
||||||
|
import com.klp.common.utils.StringUtils;
|
||||||
|
import com.klp.crm.domain.vo.CrmSalesReportVo;
|
||||||
|
import com.klp.crm.domain.bo.CrmSalesReportBo;
|
||||||
|
import com.klp.crm.mapper.CrmSalesReportMapper;
|
||||||
|
import com.klp.crm.service.ICrmSalesReportService;
|
||||||
|
import com.github.pagehelper.PageHelper;
|
||||||
|
import com.github.pagehelper.PageInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售报表Service业务层处理
|
||||||
|
*
|
||||||
|
* @author klp
|
||||||
|
* @date 2025-12-29
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Service
|
||||||
|
public class CrmSalesReportServiceImpl implements ICrmSalesReportService {
|
||||||
|
|
||||||
|
private final CrmSalesReportMapper baseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售报表汇总数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CrmSalesReportVo.SalesSummary querySalesSummary(CrmSalesReportBo bo) {
|
||||||
|
CrmSalesReportVo.SalesSummary summary = baseMapper.selectSalesSummary(bo);
|
||||||
|
if (summary != null) {
|
||||||
|
// 查询销售员统计
|
||||||
|
summary.setSalesmanStats(baseMapper.selectSalesmanStats(bo));
|
||||||
|
// 查询客户等级统计
|
||||||
|
summary.setCustomerLevelStats(baseMapper.selectCustomerLevelStats(bo));
|
||||||
|
// 查询行业统计
|
||||||
|
summary.setIndustryStats(baseMapper.selectIndustryStats(bo));
|
||||||
|
}
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询销售报表订单明细
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<CrmSalesReportVo.OrderDetail> queryOrderDetailPageList(CrmSalesReportBo bo, PageQuery pageQuery) {
|
||||||
|
PageHelper.startPage(pageQuery.getPageNum(), pageQuery.getPageSize());
|
||||||
|
List<CrmSalesReportVo.OrderDetail> list = baseMapper.selectOrderDetailList(bo);
|
||||||
|
return TableDataInfo.build(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售报表订单明细列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<CrmSalesReportVo.OrderDetail> queryOrderDetailList(CrmSalesReportBo bo) {
|
||||||
|
return baseMapper.selectOrderDetailList(bo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询完整销售报表数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CrmSalesReportVo queryFullSalesReport(CrmSalesReportBo bo, PageQuery pageQuery) {
|
||||||
|
CrmSalesReportVo reportVo = new CrmSalesReportVo();
|
||||||
|
|
||||||
|
// 查询汇总数据
|
||||||
|
reportVo.setSalesSummary(querySalesSummary(bo));
|
||||||
|
|
||||||
|
// 查询订单明细(分页)
|
||||||
|
TableDataInfo<CrmSalesReportVo.OrderDetail> orderDetailPage = queryOrderDetailPageList(bo, pageQuery);
|
||||||
|
reportVo.setOrderDetails(orderDetailPage.getRows());
|
||||||
|
|
||||||
|
return reportVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询销售员统计数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<CrmSalesReportVo.SalesmanStat> querySalesmanStats(CrmSalesReportBo bo) {
|
||||||
|
return baseMapper.selectSalesmanStats(bo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询客户等级统计数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<CrmSalesReportVo.CustomerLevelStat> queryCustomerLevelStats(CrmSalesReportBo bo) {
|
||||||
|
return baseMapper.selectCustomerLevelStats(bo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询行业统计数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<CrmSalesReportVo.IndustryStat> queryIndustryStats(CrmSalesReportBo bo) {
|
||||||
|
return baseMapper.selectIndustryStats(bo);
|
||||||
|
}
|
||||||
|
}
|
||||||
196
klp-crm/src/main/resources/mapper/klp/CrmSalesReportMapper.xml
Normal file
196
klp-crm/src/main/resources/mapper/klp/CrmSalesReportMapper.xml
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
<?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="com.klp.crm.mapper.CrmSalesReportMapper">
|
||||||
|
|
||||||
|
<!-- 通用查询条件 -->
|
||||||
|
<sql id="selectCondition">
|
||||||
|
<where>
|
||||||
|
o.del_flag = 0 AND c.del_flag = 0
|
||||||
|
<if test="bo.startTime != null">
|
||||||
|
AND DATE(o.create_time) >= #{bo.startTime}
|
||||||
|
</if>
|
||||||
|
<if test="bo.endTime != null">
|
||||||
|
AND DATE(o.create_time) <= #{bo.endTime}
|
||||||
|
</if>
|
||||||
|
<if test="bo.salesmanList != null and bo.salesmanList.size() > 0">
|
||||||
|
AND o.salesman IN
|
||||||
|
<foreach collection="bo.salesmanList" item="salesman" open="(" separator="," close=")">
|
||||||
|
#{salesman}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="bo.customerIdList != null and bo.customerIdList.size() > 0">
|
||||||
|
AND o.customer_id IN
|
||||||
|
<foreach collection="bo.customerIdList" item="customerId" open="(" separator="," close=")">
|
||||||
|
#{customerId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="bo.customerLevelList != null and bo.customerLevelList.size() > 0">
|
||||||
|
AND c.customer_level IN
|
||||||
|
<foreach collection="bo.customerLevelList" item="level" open="(" separator="," close=")">
|
||||||
|
#{level}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="bo.industryList != null and bo.industryList.size() > 0">
|
||||||
|
AND c.industry IN
|
||||||
|
<foreach collection="bo.industryList" item="industry" open="(" separator="," close=")">
|
||||||
|
#{industry}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="bo.orderStatusList != null and bo.orderStatusList.size() > 0">
|
||||||
|
AND o.order_status IN
|
||||||
|
<foreach collection="bo.orderStatusList" item="status" open="(" separator="," close=")">
|
||||||
|
#{status}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="bo.financeStatusList != null and bo.financeStatusList.size() > 0">
|
||||||
|
AND o.finance_status IN
|
||||||
|
<foreach collection="bo.financeStatusList" item="status" open="(" separator="," close=")">
|
||||||
|
#{status}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="bo.orderTypeList != null and bo.orderTypeList.size() > 0">
|
||||||
|
AND o.order_type IN
|
||||||
|
<foreach collection="bo.orderTypeList" item="type" open="(" separator="," close=")">
|
||||||
|
#{type}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="bo.minOrderAmount != null">
|
||||||
|
AND o.order_amount >= #{bo.minOrderAmount}
|
||||||
|
</if>
|
||||||
|
<if test="bo.maxOrderAmount != null">
|
||||||
|
AND o.order_amount <= #{bo.maxOrderAmount}
|
||||||
|
</if>
|
||||||
|
<if test="bo.onlyUnpaidOrders != null and bo.onlyUnpaidOrders == true">
|
||||||
|
AND o.unpaid_amount > 0
|
||||||
|
</if>
|
||||||
|
<if test="bo.companyNameKeyword != null and bo.companyNameKeyword != ''">
|
||||||
|
AND c.company_name LIKE CONCAT('%', #{bo.companyNameKeyword}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="bo.orderCodeKeyword != null and bo.orderCodeKeyword != ''">
|
||||||
|
AND o.order_code LIKE CONCAT('%', #{bo.orderCodeKeyword}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="bo.includeObjectionOrders != null and bo.includeObjectionOrders == false">
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM crm_sales_objection obj
|
||||||
|
WHERE obj.order_id = o.order_id AND obj.del_flag = 0
|
||||||
|
)
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 查询销售汇总统计数据 -->
|
||||||
|
<select id="selectSalesSummary" resultType="com.klp.crm.domain.vo.CrmSalesReportVo$SalesSummary">
|
||||||
|
SELECT
|
||||||
|
COUNT(o.order_id) as totalOrderCount,
|
||||||
|
COALESCE(SUM(o.order_amount), 0) as totalSalesAmount,
|
||||||
|
COUNT(CASE WHEN o.order_status = 3 THEN 1 END) as completedOrderCount,
|
||||||
|
COALESCE(SUM(CASE WHEN o.order_status = 3 THEN o.order_amount ELSE 0 END), 0) as completedSalesAmount,
|
||||||
|
COALESCE(SUM(o.unpaid_amount), 0) as totalUnpaidAmount,
|
||||||
|
COALESCE(AVG(o.order_amount), 0) as avgOrderAmount
|
||||||
|
FROM crm_order o
|
||||||
|
LEFT JOIN crm_customer c ON o.customer_id = c.customer_id
|
||||||
|
<include refid="selectCondition"/>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询订单明细列表 -->
|
||||||
|
<select id="selectOrderDetailList" resultType="com.klp.crm.domain.vo.CrmSalesReportVo$OrderDetail">
|
||||||
|
SELECT
|
||||||
|
o.order_id,
|
||||||
|
o.order_code,
|
||||||
|
c.customer_code,
|
||||||
|
c.company_name,
|
||||||
|
c.contact_person,
|
||||||
|
c.customer_level,
|
||||||
|
c.industry,
|
||||||
|
o.order_amount,
|
||||||
|
o.salesman,
|
||||||
|
o.delivery_date,
|
||||||
|
o.order_status,
|
||||||
|
o.finance_status,
|
||||||
|
o.unpaid_amount,
|
||||||
|
o.create_time,
|
||||||
|
COALESCE(item_stats.item_count, 0) as itemCount,
|
||||||
|
COALESCE(obj_stats.objection_count, 0) as objectionCount
|
||||||
|
FROM crm_order o
|
||||||
|
LEFT JOIN crm_customer c ON o.customer_id = c.customer_id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
order_id,
|
||||||
|
COUNT(*) as item_count
|
||||||
|
FROM crm_order_item
|
||||||
|
WHERE del_flag = 0
|
||||||
|
GROUP BY order_id
|
||||||
|
) item_stats ON o.order_id = item_stats.order_id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
order_id,
|
||||||
|
COUNT(*) as objection_count
|
||||||
|
FROM crm_sales_objection
|
||||||
|
WHERE del_flag = 0
|
||||||
|
GROUP BY order_id
|
||||||
|
) obj_stats ON o.order_id = obj_stats.order_id
|
||||||
|
<include refid="selectCondition"/>
|
||||||
|
<choose>
|
||||||
|
<when test="bo.orderBy != null and bo.orderBy != ''">
|
||||||
|
ORDER BY ${bo.orderBy}
|
||||||
|
<if test="bo.sortDirection != null and bo.sortDirection != ''">
|
||||||
|
${bo.sortDirection}
|
||||||
|
</if>
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
ORDER BY o.create_time DESC
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询销售员统计数据 -->
|
||||||
|
<select id="selectSalesmanStats" resultType="com.klp.crm.domain.vo.CrmSalesReportVo$SalesmanStat">
|
||||||
|
SELECT
|
||||||
|
o.salesman,
|
||||||
|
COUNT(o.order_id) as orderCount,
|
||||||
|
COALESCE(SUM(o.order_amount), 0) as salesAmount,
|
||||||
|
ROUND(
|
||||||
|
COALESCE(SUM(o.order_amount), 0) * 100.0 /
|
||||||
|
NULLIF((SELECT SUM(order_amount) FROM crm_order o2
|
||||||
|
LEFT JOIN crm_customer c2 ON o2.customer_id = c2.customer_id
|
||||||
|
<include refid="selectCondition"/>), 0),
|
||||||
|
2
|
||||||
|
) as percentage
|
||||||
|
FROM crm_order o
|
||||||
|
LEFT JOIN crm_customer c ON o.customer_id = c.customer_id
|
||||||
|
<include refid="selectCondition"/>
|
||||||
|
GROUP BY o.salesman
|
||||||
|
ORDER BY salesAmount DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询客户等级统计数据 -->
|
||||||
|
<select id="selectCustomerLevelStats" resultType="com.klp.crm.domain.vo.CrmSalesReportVo$CustomerLevelStat">
|
||||||
|
SELECT
|
||||||
|
c.customer_level,
|
||||||
|
COUNT(DISTINCT c.customer_id) as customerCount,
|
||||||
|
COUNT(o.order_id) as orderCount,
|
||||||
|
COALESCE(SUM(o.order_amount), 0) as salesAmount
|
||||||
|
FROM crm_order o
|
||||||
|
LEFT JOIN crm_customer c ON o.customer_id = c.customer_id
|
||||||
|
<include refid="selectCondition"/>
|
||||||
|
GROUP BY c.customer_level
|
||||||
|
ORDER BY salesAmount DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询行业统计数据 -->
|
||||||
|
<select id="selectIndustryStats" resultType="com.klp.crm.domain.vo.CrmSalesReportVo$IndustryStat">
|
||||||
|
SELECT
|
||||||
|
COALESCE(c.industry, '未分类') as industry,
|
||||||
|
COUNT(DISTINCT c.customer_id) as customerCount,
|
||||||
|
COUNT(o.order_id) as orderCount,
|
||||||
|
COALESCE(SUM(o.order_amount), 0) as salesAmount
|
||||||
|
FROM crm_order o
|
||||||
|
LEFT JOIN crm_customer c ON o.customer_id = c.customer_id
|
||||||
|
<include refid="selectCondition"/>
|
||||||
|
GROUP BY c.industry
|
||||||
|
ORDER BY salesAmount DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
Reference in New Issue
Block a user