这个销售团队后台管理系统是基于SpringBoot框架开发的企业级应用,主要面向销售团队的管理需求。系统采用前后端分离架构,后端使用Java+SpringBoot+MyBatis技术栈,前端采用FreeMarker+Bootstrap+ECharts的组合方案。
作为一个完整的CRM系统,它包含了销售团队日常工作中最核心的几个功能模块:客户管理、业务机会跟踪、日报系统以及组织架构管理。特别值得一提的是,系统内置了数据可视化看板,通过ECharts实现了销售数据的直观展示,这对销售团队的决策支持非常有帮助。
SpringBoot 2.x作为基础框架,这是目前Java领域最主流的微服务框架。选择它的原因主要有三点:
MyBatis作为ORM框架,相比Hibernate更轻量级,对于需要精细控制SQL的场景特别适合。我们在项目中大量使用了MyBatis的动态SQL功能,比如:
java复制@SelectProvider(type = CustomerSqlProvider.class, method = "findByCondition")
List<Customer> findByCondition(QueryCustomerVo vo);
FreeMarker作为模板引擎,替代了传统的JSP。它的优势在于:
前端UI基于Bootstrap 4构建响应式布局,确保在PC和移动设备上都有良好的显示效果。数据可视化则使用ECharts 5,这是一个功能强大且文档完善的图表库。
系统采用RBAC(基于角色的访问控制)模型,包含用户、角色、菜单三个核心实体。权限控制通过自定义注解和拦截器实现:
java复制@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String value();
}
public class PermissionInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 权限校验逻辑
}
}
客户信息管理包含基础CRUD操作和高级查询功能。我们设计了客户分类、行业类型等维度数据,便于后续统计分析。一个典型的高级查询实现如下:
java复制public PageInfo<Customer> find(QueryCustomerVo vo) {
PageHelper.startPage(vo.getPageNum(), vo.getPageSize());
List<Customer> list = customerMapper.findByCondition(vo);
return new PageInfo<>(list);
}
日报系统分为个人日报和团队日报两个视图:
日报数据通过Activity实体进行建模,包含活动类型、客户关联、内容描述等字段。团队日报的查询逻辑涉及组织架构的层级关系:
java复制public PageInfo<Activity> findTeamDailyReport(QueryActivityVo vo) {
// 获取当前用户的所有下属ID
List<Integer> subordinate = orgUnitService.findSubordinate(getUser().getUserId());
vo.setCreateBy(subordinate);
return activityService.find(vo);
}
系统看板使用ECharts展示销售漏斗、业绩趋势等关键指标。后端提供JSON格式的数据接口,前端通过AJAX获取数据后渲染图表:
javascript复制$.post('/dashboard/salesTrend', function(data) {
var chart = echarts.init(document.getElementById('trend-chart'));
chart.setOption({
xAxis: {data: data.categories},
yAxis: {},
series: [{data: data.values}]
});
});
系统提供了数据导出功能,基于Apache POI实现。我们封装了一个通用的Excel导出工具类:
java复制public <T> void export(String fileName, List<T> data) {
// 创建Workbook
HSSFWorkbook workbook = new HSSFWorkbook();
// 设置表头
HSSFRow headerRow = sheet.createRow(0);
// 填充数据行
for (int i = 0; i < data.size(); i++) {
T item = data.get(i);
HSSFRow row = sheet.createRow(i + 1);
// 反射获取字段值
field.setAccessible(true);
Object value = field.get(item);
// 设置单元格值
cell.setCellValue(value.toString());
}
}
在application.properties中配置数据库连接:
properties复制spring.datasource.url=jdbc:mysql://localhost:3306/sales_crm?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
项目启动时会自动执行init.sql脚本,创建必要的表结构和初始化数据。管理员账号为admin/123456。
虽然使用了FreeMarker作为模板引擎,但我们仍然遵循前后端分离的原则:
这种架构既保留了传统MVC的便利性,又具备了前后端分离的灵活性。
在复杂查询场景下,我们推荐使用MyBatis的注解方式动态SQL:
java复制@Select("<script>" +
"SELECT * FROM customer " +
"<where>" +
" <if test='name != null'>AND name LIKE CONCAT('%',#{name},'%')</if>" +
" <if test='categoryId != null'>AND category_id = #{categoryId}</if>" +
"</where>" +
"</script>")
List<Customer> searchCustomers(@Param("name") String name,
@Param("categoryId") Integer categoryId);
数据库层面:
应用层面:
可能原因:
排查步骤:
优化建议:
解决方案:
java复制response.setContentType("application/vnd.ms-excel;charset=UTF-8");
java复制String fileName = URLEncoder.encode("客户列表.xls", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
这个系统还有很大的扩展空间,以下是一些可能的改进方向:
在实际开发中,我们遇到的一个典型挑战是组织架构的树形展示。最初使用递归查询性能较差,后来改为一次性查询所有节点,在内存中构建树形结构,性能提升了10倍以上。这个经验告诉我们,有时候数据库操作越少越好,合理利用应用层计算反而能获得更好的性能。