feat(crm): 添加销售报表功能模块

- 新增销售报表查询业务对象CrmSalesReportBo,支持多种查询条件
- 创建销售报表控制器CrmSalesReportController,提供汇总数据、订单明细、统计分析等接口
- 实现销售报表数据访问层CrmSalesReportMapper,包含销售汇总、订单明细、销售员统计等查询
- 开发销售报表服务层ICrmSalesReportService及其实现类,处理报表数据逻辑
- 设计销售报表视图对象CrmSalesReportVo,包含汇总信息、订单明细、统计分析等数据结构
- 集成Excel导出功能,支持订单明细、销售员统计、客户等级统计、行业统计的数据导出
- 实现多维度统计分析,包括销售员业绩、客户等级分布、行业分布等统计功能
This commit is contained in:
2025-12-29 10:05:05 +08:00
parent 96b6e844d9
commit 11c21f2a33
7 changed files with 954 additions and 0 deletions

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}