Spring Boot+MyBatis实现企业级CRM系统:附完整代码与部署教程

Java+MySQL CRM客户关系管理系统

一、系统概述

CRM(客户关系管理)系统是企业管理中重要的一环,本文实现的Java+MySQL CRM系统采用MVC架构模式,结合Spring Boot、MyBatis-Plus等技术,实现了客户信息管理、销售机会跟踪、合同管理、统计分析等核心功能。

二、系统架构设计

1. 技术选型

  • 后端框架:Spring Boot 2.7.10
  • 数据访问:MyBatis-Plus 3.5.3.1
  • 数据库:MySQL 8.0
  • 前端:Thymeleaf模板引擎
  • 安全认证:Spring Security
  • 项目构建:Maven

2. 系统架构

├── src/main/java
│   └── com/crm
│       ├── config (配置类)
│       ├── controller (控制器层)
│       ├── entity (实体类)
│       ├── mapper (数据访问层)
│       ├── service (业务逻辑层)
│       ├── dto (数据传输对象)
│       └── utils (工具类)
└── src/main/resources
    ├── static (静态资源)
    ├── templates (视图模板)
    └── application.yml (配置文件)

三、核心代码实现

1. 数据库设计与实体类

// Customer.java
@Data
@TableName("customer")
public class Customer {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String customerName;
    private String industry;
    private String scale;
    private String contactPerson;
    private String phone;
    private String email;
    private String address;
    private Integer status;
    private Date createTime;
    private Date updateTime;
}

// Opportunity.java
@Data
@TableName("opportunity")
public class Opportunity {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long customerId;
    private String opportunityName;
    private Double amount;
    private Integer stage;
    private String probability;
    private String responsiblePerson;
    private Date expectedClosingDate;
    private Date createTime;
}

2. 数据访问层

// CustomerMapper.java
@Mapper
public interface CustomerMapper extends BaseMapper<Customer> {
    // 自定义SQL查询
    @Select("SELECT * FROM customer WHERE status = #{status}")
    List<Customer> selectByStatus(Integer status);
    
    // 分页查询
    IPage<Customer> selectPageVo(Page<Customer> page, @Param("ew") Wrapper<Customer> queryWrapper);
}

// OpportunityMapper.java
@Mapper
public interface OpportunityMapper extends BaseMapper<Opportunity> {
    // 关联查询客户信息
    @Select("SELECT o.*, c.customer_name FROM opportunity o " +
            "LEFT JOIN customer c ON o.customer_id = c.id " +
            "WHERE o.id = #{id}")
    OpportunityVO selectOpportunityVOById(Long id);
}

3. 业务逻辑层

// CustomerServiceImpl.java
@Service
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements CustomerService {
    
    @Autowired
    private CustomerMapper customerMapper;
    
    @Override
    public Page<Customer> getCustomerPage(int pageNum, int pageSize, String keyword) {
        Page<Customer> page = new Page<>(pageNum, pageSize);
        QueryWrapper<Customer> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(keyword)) {
            wrapper.like("customer_name", keyword)
                   .or().like("contact_person", keyword)
                   .or().like("phone", keyword);
        }
        return customerMapper.selectPage(page, wrapper);
    }
    
    @Override
    public boolean saveCustomer(Customer customer) {
        if (customer.getId() == null) {
            customer.setCreateTime(new Date());
            return save(customer);
        } else {
            customer.setUpdateTime(new Date());
            return updateById(customer);
        }
    }
}

// OpportunityServiceImpl.java
@Service
public class OpportunityServiceImpl extends ServiceImpl<OpportunityMapper, Opportunity> implements OpportunityService {
    
    @Autowired
    private OpportunityMapper opportunityMapper;
    
    @Override
    public List<Opportunity> getOpportunitiesByCustomerId(Long customerId) {
        QueryWrapper<Opportunity> wrapper = new QueryWrapper<>();
        wrapper.eq("customer_id", customerId);
        return opportunityMapper.selectList(wrapper);
    }
    
    @Override
    public boolean updateOpportunityStage(Long id, Integer stage) {
        Opportunity opportunity = new Opportunity();
        opportunity.setId(id);
        opportunity.setStage(stage);
        return updateById(opportunity);
    }
}

4. 控制器层

// CustomerController.java
@RestController
@RequestMapping("/api/customer")
@Api(tags = "客户管理")
public class CustomerController {
    
    @Autowired
    private CustomerService customerService;
    
    @GetMapping("/list")
    @ApiOperation("客户列表分页查询")
    public Result<Page<Customer>> list(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize,
            @RequestParam(required = false) String keyword) {
        Page<Customer> page = customerService.getCustomerPage(pageNum, pageSize, keyword);
        return Result.success(page);
    }
    
    @PostMapping("/save")
    @ApiOperation("保存客户信息")
    public Result<?> save(@RequestBody Customer customer) {
        boolean result = customerService.saveCustomer(customer);
        return result ? Result.success() : Result.error("保存失败");
    }
    
    @DeleteMapping("/{id}")
    @ApiOperation("删除客户")
    public Result<?> delete(@PathVariable Long id) {
        boolean result = customerService.removeById(id);
        return result ? Result.success() : Result.error("删除失败");
    }
}

// OpportunityController.java
@RestController
@RequestMapping("/api/opportunity")
@Api(tags = "销售机会管理")
public class OpportunityController {
    
    @Autowired
    private OpportunityService opportunityService;
    
    @GetMapping("/list/{customerId}")
    @ApiOperation("获取客户的销售机会")
    public Result<List<Opportunity>> listByCustomerId(@PathVariable Long customerId) {
        List<Opportunity> list = opportunityService.getOpportunitiesByCustomerId(customerId);
        return Result.success(list);
    }
    
    @PostMapping("/updateStage")
    @ApiOperation("更新销售机会阶段")
    public Result<?> updateStage(@RequestParam Long id, @RequestParam Integer stage) {
        boolean result = opportunityService.updateOpportunityStage(id, stage);
        return result ? Result.success() : Result.error("更新失败");
    }
}

5. 安全认证配置

// SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/static/**", "/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/index")
            .permitAll()
            .and()
            .logout()
            .permitAll();
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

四、系统界面设计

1. 客户列表页面


<table class="table table-striped">
    <thead>
        <tr>
            <th>IDth>
            <th>客户名称th>
            <th>行业th>
            <th>规模th>
            <th>联系人th>
            <th>电话th>
            <th>状态th>
            <th>操作th>
        tr>
    thead>
    <tbody>
        <tr th:each="customer : ${page.records}">
            <td th:text="${customer.id}">td>
            <td th:text="${customer.customerName}">td>
            <td th:text="${customer.industry}">td>
            <td th:text="${customer.scale}">td>
            <td th:text="${customer.contactPerson}">td>
            <td th:text="${customer.phone}">td>
            <td>
                <span th:if="${customer.status == 1}" class="badge bg-success">正常span>
                <span th:if="${customer.status == 0}" class="badge bg-danger">暂停span>
            td>
            <td>
                <button class="btn btn-sm btn-primary" th:onclick="|editCustomer(${customer.id})|">编辑button>
                <button class="btn btn-sm btn-danger" th:onclick="|deleteCustomer(${customer.id})|">删除button>
            td>
        tr>
    tbody>
table>

<script>
function editCustomer(id) {
    window.location.href = "/customer/edit/" + id;
}

function deleteCustomer(id) {
    if (confirm("确定要删除该客户吗?")) {
        fetch("/api/customer/" + id, {
            method: "DELETE"
        }).then(response => {
            if (response.ok) {
                alert("删除成功");
                location.reload();
            } else {
                alert("删除失败");
            }
        });
    }
}
script>

2. 销售机会跟踪页面


<div class="card">
    <div class="card-header">
        销售机会跟踪 - [[${opportunity.opportunityName}]]
    div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-6">
                <p>客户名称: [[${customer.customerName}]]p>
                <p>金额: [[${opportunity.amount}]]p>
                <p>阶段: 
                    <select id="stageSelect" class="form-select form-select-sm">
                        <option value="1" th:selected="${opportunity.stage == 1}">初步接触option>
                        <option value="2" th:selected="${opportunity.stage == 2}">需求分析option>
                        <option value="3" th:selected="${opportunity.stage == 3}">方案制定option>
                        <option value="4" th:selected="${opportunity.stage == 4}">商务谈判option>
                        <option value="5" th:selected="${opportunity.stage == 5}">签约成交option>
                        <option value="6" th:selected="${opportunity.stage == 6}">丢失option>
                    select>
                p>
                <p>预计成交日期: [[${#dates.format(opportunity.expectedClosingDate, 'yyyy-MM-dd')}]]p>
            div>
            <div class="col-md-6">
                <p>负责人: [[${opportunity.responsiblePerson}]]p>
                <p>成功率: [[${opportunity.probability}]]p>
                <p>创建时间: [[${#dates.format(opportunity.createTime, 'yyyy-MM-dd HH:mm')}]]p>
            div>
        div>
        
        <div class="mt-4">
            <h5>历史跟进记录h5>
            <ul class="list-group" id="followUpList">
                
            ul>
        div>
        
        <div class="mt-4">
            <h5>添加跟进记录h5>
            <textarea id="followUpContent" class="form-control" rows="3">textarea>
            <button class="btn btn-primary mt-2" onclick="addFollowUp()">提交button>
        div>
    div>
div>

五、系统部署与测试

1. 环境要求

  • JDK 1.8+
  • MySQL 8.0
  • Maven 3.6+

2. 部署步骤

  1. 创建数据库并执行初始化脚本
  2. 修改application.yml中的数据库连接配置
  3. 使用Maven打包项目:mvn clean package
  4. 运行应用:java -jar target/crm-system.jar
  5. 访问系统:http://localhost:8080

3. 测试用例

// CustomerServiceTest.java
@SpringBootTest
class CustomerServiceTest {
    
    @Autowired
    private CustomerService customerService;
    
    @Test
    void testSaveCustomer() {
        Customer customer = new Customer();
        customer.setCustomerName("测试客户");
        customer.setIndustry("信息技术");
        customer.setScale("中型企业");
        customer.setContactPerson("张三");
        customer.setPhone("13800138000");
        customer.setStatus(1);
        
        boolean result = customerService.saveCustomer(customer);
        assertTrue(result);
        
        Customer savedCustomer = customerService.getById(customer.getId());
        assertNotNull(savedCustomer);
        assertEquals("测试客户", savedCustomer.getCustomerName());
    }
    
    @Test
    void testGetCustomerPage() {
        Page<Customer> page = customerService.getCustomerPage(1, 10, null);
        assertNotNull(page);
        assertTrue(page.getTotal() >= 0);
    }
}

六、系统功能扩展

  1. 统计分析:基于客户数据和销售机会数据,实现销售额统计、客户分布分析等功能
  2. 合同管理:添加合同创建、审批、执行等管理功能
  3. 营销活动:集成营销活动管理,跟踪活动效果
  4. 工作流:实现客户分配、销售机会流转等自动化工作流

七、总结

本系统实现了CRM系统的核心功能,采用Java+MySQL技术栈,结合Spring Boot框架,具有良好的可扩展性和维护性。系统采用前后端分离的设计思想,提供了友好的用户界面和完善的API接口,可作为企业级CRM系统的基础框架进行二次开发。

你可能感兴趣的:(文末下载资源,spring,boot,mybatis,后端)