Crm-客户管理

Crm-客户管理

学习目标

客户管理表结构设计

客户信息管理模块表结构

这里主要涉及表:

​ t_customer 客户表、t_customer_contact 客户交往记录表、t_customer_linkman 客户联系人表、t_customer_order 客户订单表、t_order_details 订单详情表

t_customer 客户信息表
字段 字段类型 字段限制 字段描述
主键 id int(11) 自增 id主键
khno varchar(20) 可空 客户编号
name varchar(20) 可空 客户姓名
area varchar(20) 可空 客户所属地区
cus_manager varchar(20) 可空 客户经理
level varchar(30) 可空 客户级别
myd varchar(30) 可空 客户满意度
xyd varchar(30) 可空 客户信用度
address varchar(500) 可空 客户地址
post_code varchar(50) 可空 邮编
phone varchar(20) 可空 联系电话
fax varchar(20) 可空 传真
web_site varchar(20) 可空 网址
yyzzzch varchar(50) 可空 营业执照注册号
fr varchar(20) 可空 法人代表
zczj varchar(20) 可空 注册资金
nyye varchar(20) 可空 年营业额
khyh varchar(50) 可空 开户银行
khzh varchar(50) 可空 开户账号
dsdjh varchar(50) 可空 地税登记号
gsdjh varchar(50) 可空 国税登记号
state int(11) 可空 流失状态
is_valid int(4) 可空 有效状态
create_date datetime 可空 创建时间
update_date datetime 可空 更新时间
t_customer_contact 客户交往记录表
字段 字段类型 字段限制 字段描述
主键 id int(11) 自增 id主键
cus_id int(11) 可空 客户id
contact_time datetime 可空 交往时间
address varchar(500) 可空 交往地址
overview varchar(100) 可空 地址
create_date datetime 可空 创建时间
update_date datetime 可空 更新时间
is_valid int(4) 可空 有效状态
t_customer_linkman 客户联系人表
字段 字段类型 字段限制 字段描述
主键 id int(11) 自增 id主键
cus_id int(11) 可空 客户id
link_name varchar(20) 可空 联系人姓名
sex varchar(20) 可空 性别
zhiwei varchar(50) 可空 职位
office_phone varchar(50) 可空 办公电话
phone varchar(20) 可空 手机号
is_valid int(4) 可空 有效状态
ceate_date datetime 可空 创建时间
update_date datetime 可空 更新时间
t_customer_order 客户订单
字段 字段类型 字段限制 字段描述
主键 id int(11) 自增 id主键
cus_id int(11) 可空 客户id
order_no varchar(40) 可空 订单编号
order_date datetime 可空 下单时间
address varchar(200) 可空 地址
state int(11) 可空 状态
create_date datetime 可空 创建时间
update_date datetime 可空 更新时间
is_valid int(4) 可空 有效状态
t_order_details 订单详情表
字段 字段类型 字段限制 字段描述
主键 id int(11) 自增 id主键
order_id int(11) 可空 订单id
goods_name varchar(100) 可空 商品名称
goods_num int(11) 可空 商品数量
unit varchar(20) 可空 商品单位
price float 可空 单价
sum float 可空 总金额
is_valid int(4) 可空 有效状态
create_date datetime 可空 创建时间
update_date datetime 可空 更新时间

客户流失管理模块表结构

这里主要涉及表有

​ t_customer_loss 客户流失表

​ t_customer_reprieve 客户流失暂缓表

t_customer_loss 客户流失表
字段 字段类型 字段限制 字段描述
主键 id int(11) 自增 id主键
cus_no varchar(40) 可空 客户编号
cus_name varchar(20) 可空 客户姓名
cus_manager varchar(20) 可空 客户经理
last_order_time date 可空 最后下单时间
confirm_loss_time date 可空 确认流失时间
state int(11) 可空 流失状态
loss_reason varchar(1000) 可空 流失原因
is_valid tinyint(4) 可空 有效状态
create_date datetime 可空 创建时间
update_date datetime 可空 更新时间
t_customer_reprieve 客户流失暂缓表
字段 字段类型 字段限制 字段描述
主键 id int(11) 自增 id主键
loss_id int(11) 可空 流失id
measure varchar(500) 可空 措施
is_valid tinyint(4) 可空 有效状态
create_date datetime 可空 创建时间
update_date datetime 可空 更新时间

客户信息管理模块实现

客户信息管理查询

客户信息管理页面效果

客户信息查询后端代码实现

layui 框架通过表格展示后端表数据,数据格式见官网测试数据地址。

CustomerMapper 接口定义与Sql配置
  • CustomerMapper .java
public interface CustomerMapper  extends BaseMapper<Customer,Integer> {
    /*
      由于考虑到多个模块均涉及多条件查询
      这里对于多条件分页查询方法由父接口BaseMapper定义
    */
}

  • CustomerMapper .xml
    <select id="selectByParams" parameterType="com.xxxx.crm.query.CustomerQuery" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from t_customer
        <where>
            is_valid=1
            <!--
               state  流失状态
                  0  未流失
                  1  已流失
            -->
            and state =0
            <if test="null !=cusName and cusName !=''">
                and name like concat('%',#{cusName},'%')
            </if>
            <if test="null !=cusNo and cusNo !=''">
                and khno =#{cusNo}
            </if>
            <if test="null !=level and level !=''">
                and level =#{level}
            </if>
            <if test="null !=myd and myd !=''">
                and myd =#{myd}
            </if>

        </where>
    </select>

  • CustomerQuery.java

在crm.query 包下创建CustomerQuery.java 查询类,设置查询条件

public class CustomerQuery extends BaseQuery {
    private String cusName;

    private String cusNo;

    private String level;
}

CustomerService 定义
  • CustomerService .java
@Service
public class CustomerService  extends BaseService<Customer, Integer> {
    /*
      由于考虑到多个模块均涉及多条件查询
      这里对于多条件分页查询方法由父类BaseService定义并实现
    */
}

  • BaseService.java 分页查询方法定义与实现
    public Map<String, Object> queryByParamsForTable(BaseQuery baseQuery) {
        Map<String,Object> result = new HashMap<String,Object>();
        PageHelper.startPage(baseQuery.getPage(),baseQuery.getLimit());
        PageInfo<T> pageInfo =new PageInfo<T>(selectByParams(baseQuery));
        result.put("count",pageInfo.getTotal());
        result.put("data",pageInfo.getList());
        result.put("code",0);
        result.put("msg","");
        return result;
    }

CustomerController.java
@Controller
@RequestMapping("customer")
public class CustomerController extends BaseController {

    @Resource
    private CustomerService customerService;

    @Resource
    private CustomerOrderService orderService;

    @RequestMapping("index")
    public String index(){
        return "customer/customer";
    }

    @RequestMapping("list")
    @ResponseBody
    public Map<String,Object> queryCustomersByParams(CustomerQuery customerQuery){
        return customerService.queryByParamsForTable(customerQuery);
    }

客户信息管理前端核心代码

客户信息管理主页面模板

resources/views/customer目录创建customer.ftl 模块文件,模板内容如下(模板依赖的layui文件由common.ftl 文件提供),layui表格数据展示模板文件实现参考该地址

  • customer.ftl
<!DOCTYPE html>
<html>
<head>
    <title>客户管理</title>
    <#include "../common.ftl">
</head>
<body class="childrenBody">

<form class="layui-form" >
    <blockquote class="layui-elem-quote quoteBox">
        <form class="layui-form">
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <input type="text" name="name"
                           class="layui-input
                    searchVal" placeholder="客户名" />
                </div>
                <div class="layui-input-inline">
                    <input type="text" name="khno" class="layui-input
                    searchVal" placeholder="客户编号" />
                </div>
                <div class="layui-input-inline">
                    <select name="level"  id="level">
                        <option value="">请选择...</option>
                        <option value="战略合作伙伴">战略合作伙伴</option>
                        <option value="大客户">大客户</option>
                        <option value="重点开发客户">重点开发客户</option>
                    </select>
                </div>
                <a class="layui-btn search_btn" data-type="reload"><i
                            class="layui-icon">&#xe615;</i> 搜索</a>
            </div>
        </form>
    </blockquote>
    <table id="customerList" class="layui-table"  lay-filter="customers"></table>

    <!--操作-->
    <script id="customerListBar" type="text/html">
        <a class="layui-btn layui-btn-xs" id="edit" lay-event="edit">编辑</a>
        <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</a>
    </script>

</form>
<script type="text/javascript" src="${ctx}/static/js/customer/customer.js"></script>

</body>
</html>

客户信息管理主页面模板核心js

static/js/customer目录下创建customer.js 文件,初始化layui表格数据,layui表格数据展示模板文件实现参考该地址

  • customer.js
layui.use(['table','layer',"form"],function(){
    var layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery,
        table = layui.table,
        form = layui.form;

    //客户列表展示
    var  tableIns = table.render({
        elem: '#customerList',
        url : ctx+'/customer/list',
        cellMinWidth : 95,
        page : true,
        height : "full-125",
        limits : [10,15,20,25],
        limit : 10,
        toolbar: "#toolbarDemo",
        id : "customerListTable",
        cols : [[
            {type: "checkbox", fixed:"center"},
            {field: "id", title:'编号',fixed:"true"},
            {field: 'name', title: '客户名',align:"center"},
            {field: 'fr', title: '法人',  align:'center'},
            {field: 'khno', title: '客户编号', align:'center'},
            {field: 'area', title: '地区', align:'center'},
            {field: 'cusManager', title: '客户经理',  align:'center'},
            {field: 'myd', title: '满意度', align:'center'},
            {field: 'level', title: '客户级别', align:'center'},
            {field: 'xyd', title: '信用度', align:'center'},
            {field: 'address', title: '详细地址', align:'center'},
            {field: 'postCode', title: '邮编', align:'center'},
            {field: 'phone', title: '电话', align:'center'},
            {field: 'webSite', title: '网站', align:'center'},
            {field: 'fax', title: '传真', align:'center'},
            {field: 'zczj', title: '注册资金', align:'center'},
            {field: 'yyzzzch', title: '营业执照', align:'center'},
            {field: 'khyh', title: '开户行', align:'center'},
            {field: 'khzh', title: '开户账号', align:'center'},
            {field: 'gsdjh', title: '国税', align:'center'},
            {field: 'dsdjh', title: '地税', align:'center'},
            {field: 'createDate', title: '创建时间', align:'center'},
            {field: 'updateDate', title: '更新时间', align:'center'},
            {title: '操作', templet:'#customerListBar',fixed:"right",align:"center", minWidth:150}
        ]]
    });

});

客户信息多条件查询事件添加

​ 客户信息数据表格数据展示成功后,接下来考虑添加多条件查询点击事件,这里使用layui表格reload重载基础方法实现,点击这里参考官网介绍

<script type="text/html" id="toolbarDemo">
        <div class="layui-btn-container">
            <a class="layui-btn layui-btn-normal addNews_btn" lay-event="add">
                <i class="layui-icon">&#xe608;</i>
                添加
            </a>
            <a class="layui-btn layui-btn-normal  " lay-event="link">
                <i class="layui-icon">&#xe608;</i>
                联系人管理
            </a>
            <a class="layui-btn layui-btn-normal addNews_btn" lay-event="recode">
                <i class="layui-icon">&#xe608;</i>
                交往记录
            </a>
            <a class="layui-btn layui-btn-normal addNews_btn" lay-event="order">
                <i class="layui-icon">&#xe608;</i>
                订单查看
            </a>
        </div>
    </script>

  • customer.js 添加搜索点击事件
    // 多条件搜索
    $(".search_btn").on("click",function(){
        table.reload("customerListTable",{
            page: {
                curr: 1 //重新从第 1 页开始
            },
            where: {
                cusName: $("input[name='name']").val(),  //客户名
                cusNo: $("input[name='khno']").val(),  //客户编号
                level: $("#level").val()  //客户等级
            }
        })
    });

  • 搜索效果

客户管理数据添加

机会管理后端添加

机会数据添加实现思路
/**
         * 1.参数校验
         *    客户名称 name 非空  不可重复
         *    phone 联系电话  非空  格式符合规范
         *    法人  非空
         * 2.默认值设置
         *     isValid  state  cteaetDate  updadteDate
         *      khno 系统生成 唯一  (uuid| 时间戳 | 年月日时分秒  雪花算法)
         *3.执行添加  判断结果
         */

机会数据添加核心代码
@Transactional(propagation = Propagation.REQUIRED)
public void saveCustomer(Customer customer){
    checkParams(customer.getName(),customer.getPhone(),customer.getFr());
    AssertUtil.isTrue(null!=customerMapper.queryCustomerByName(customer.getName()),"该客户已存在!");
    customer.setIsValid(1);
    customer.setState(0);
    customer.setCreateDate(new Date());
    customer.setUpdateDate(new Date());
    String khno = "KH_"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
    customer.setKhno(khno);
    AssertUtil.isTrue(insertSelective(customer)<1,"客户添加失败!");
}
private void checkParams(String name, String phone, String fr) {
    AssertUtil.isTrue(StringUtils.isBlank(name),"请指定客户名称!");
    AssertUtil.isTrue(!(PhoneUtil.isMobile(phone)),"手机号格式非法!");
    AssertUtil.isTrue(StringUtils.isBlank(fr),"请指定公司法人!");
}

客户管理数据添加后端页面转发&添加方法调用

​ 对于机会数据添加与更新表单页可以实现共享,这里在转发机会数据添加与更新页面时共用一套代码即可(考虑更新时涉及到机会数据显示操作,这里根据机会id查询机会记录并放入到请求域中)。

@RequestMapping("addOrUpdateSaleChancePage")
public String addOrUpdateSaleChancePage(Integer id,Model model){
    model.addAttribute("customer",customerService.selectByPrimaryKey(id)) ;
    return "customer/add_update";
}

@RequestMapping("save")
@ResponseBody
public ResultInfo saveCustomer(Customer customer){
    customerService.saveCustomer(customer);
    return success("客户添加成功");
}

客户管理添加前台模板与核心js

客户数据添加工具栏事件

这里对于对话框打开方法代码请参考官网页面

    //头工具栏事件
    table.on('toolbar(customers)', function(obj){
        var checkStatus = table.checkStatus(obj.config.id);
        switch(obj.event){
            case "add":
                openAddOrUpdateCustomerDialog();
                break;
        };
    });

 // 打开添加客户数据页面
    function openAddOrUpdateCustomerDialog(id){
        var url  =  ctx+"/customer/addOrUpdateSaleChancePage";
        var title="客户管理-添加";
        if(id){
            url = url+"?id="+id;
            title="客户管理-更新";
        }
        layui.layer.open({
            title : title,
            type : 2,
            area:["700px","500px"],
            maxmin:true,
            content : url
        });
    }

add_update.ftl 表单模板文件

views/customer目录下添加add_update.ftl 表单模板

<!DOCTYPE html>
<html>
<head>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
<form class="layui-form" style="width:80%;">
    <input name="id" type="hidden" value="${(customer.id)!}"/>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">客户名称</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="name" id="name"  lay-verify="required" value="${(customer.name)!}" placeholder="请输入客户名">
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">法人</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="fr" id="fr" lay-verify="required" value="${(customer.fr)!}" placeholder="请输入法人">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">区域</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="area"  lay-verify="required"  value="${(customer.area)!}" placeholder="请输入区域">
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">客户经理</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="cusManager" value="${(customer.cusManager)!}" placeholder="请输入客户经理">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">客户级别</label>
            <div class="layui-input-block">
                <select name="level"  id="level">
                    <option value="" >请选择</option>
                    <option value="普通客户">普通客户</option>
                    <option value="重点开发客户" >重点开发客户</option>
                    <option value="大客户" >大客户</option>
                    <option value="合作伙伴" >合作伙伴</option>
                </select>
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">信用度</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="xyd" value="${(customer.xyd)!}"  placeholder="请输入客户信用级别">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">邮编</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="postCode" value="${(customer.postCode)!}"  placeholder="请输入客户邮编">
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">联系电话</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="phone" value="${(customer.phone)!}"  placeholder="请输入客户联系电话">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">客户地址</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="address" value="${(customer.address)!}"  placeholder="请输入客户地址">
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">传真</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="fax" value="${(customer.fax)!}"  placeholder="请输入客户传真">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">网站</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="webSite" value="${(customer.webSite)!}"  placeholder="请输入客户网站地址">
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">注册资金</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="zczj" value="${(customer.zczj)!}"  placeholder="请输入注册资金">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">开户行</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="khyh" value="${(customer.khyh)!}"  placeholder="请输入客户开户行">
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">开户账号</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="khzh" value="${(customer.khzh)!}"  placeholder="请输入开户账号">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">
        <div class="layui-col-xs6">
            <label class="layui-form-label">国税</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="gsdjh" value="${(customer.gsdjh)!}"  placeholder="请输入国税">
            </div>
        </div>
        <div class="layui-col-xs6">
            <label class="layui-form-label">地税</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="dsdjh" value="${(customer.dsdjh)!}"  placeholder="请输入地税">
            </div>
        </div>
    </div>

    <div class="layui-form-item layui-row">

        <div class="layui-col-xs6">
            <label class="layui-form-label">年营业额</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input"
                       name="nyye" value="${(customer.nyye)!}"  placeholder="请输入客户年营业额">
            </div>
        </div>
    </div>

    <br/>
    <div class="layui-form-item layui-row layui-col-xs12">
        <div class="layui-input-block">
            <button class="layui-btn layui-btn-lg" lay-submit=""
                    lay-filter="addOrUpdateCustomer">确认
            </button>
            <button class="layui-btn layui-btn-lg layui-btn-normal">取消</button>
        </div>
    </div>
</form>
<script type="text/javascript" src="${ctx}/static/js/customer/add.update.js"></script>
</body>
</html>

add.update.js 文件添加

​ js/customer目录下添加add.update.js 文件,完成机会数据添加与更新表单提交操作

layui.use(['form', 'layer'], function () {
    var form = layui.form,
        layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery;

    form.on("submit(addOrUpdateCustomer)", function (data) {
        var index = top.layer.msg('数据提交中,请稍候', {icon: 16, time: false, shade: 0.8});
        //弹出loading
        var url=ctx + "/customer/save";
        if($("input[name='id']").val()){
            url=ctx + "/customer/update";
        }
        $.post(url, data.field, function (res) {
            if (res.code == 200) {
                setTimeout(function () {
                    top.layer.close(index);
                    top.layer.msg("操作成功!");
                    layer.closeAll("iframe");
                    //刷新父页面
                    parent.location.reload();
                }, 500);
            } else {
                layer.msg(
                    res.msg, {
                        icon: 5
                    }
                );
            }
        });
        return false;
    });
});

客户信息数据更新

客户信息数据后端更新

客户信息后端更新实现思路
/**
         * 1.参数校验
         *    记录存在校验
         *    客户名称 name 非空  不可重复
         *    phone 联系电话  非空  格式符合规范
         *    法人  非空
         * 2.默认值设置
         *      updadteDate
         *3.执行更新  判断结果
         */

客户信息数据更新核心代码
 @Transactional(propagation = Propagation.REQUIRED)
    public void updateCustomer(Customer customer){ AssertUtil.isTrue(null==customer.getId()||null==selectByPrimaryKey(customer.getId()),"待更新记录不存在!");
        checkParams(customer.getName(),customer.getPhone(),customer.getFr());
        Customer temp =customerMapper.queryCustomerByName(customer.getName());
        AssertUtil.isTrue(null !=temp && !(temp.getId().equals(customer.getId())),"该客户已存在!");
        customer.setUpdateDate(new Date());
        AssertUtil.isTrue(updateByPrimaryKeySelective(customer)<1,"客户更新失败!");
    }

客户信息数据更新后端页面转发 & 更新方法调用

这里客户信息数据更新页面视图与添加操作实现视图代码共用。

    @RequestMapping("update")
    @ResponseBody
    public ResultInfo updateCustomer(Customer customer){
        customerService.updateCustomer(customer);
        return success("客户更新成功");
    }

客户信息数据前端模板与核心js

客户信息数据表格行监听事件添加
  • sale.chance.js

表格添加行监听事件,行监听事件参考这里

 /**
     * 行监听
     */
    table.on("tool(customers)", function(obj){
        var layEvent = obj.event;
        if(layEvent === "edit") {
            openAddOrUpdateCustomerDialog(obj.data.id);
        }
    });

 // 打开添加客户数据页面
    function openAddOrUpdateCustomerDialog(id){
        var url  =  ctx+"/customer/addOrUpdateSaleChancePage";
        var title="客户管理-添加";
        if(id){
            url = url+"?id="+id;
            title="客户管理-更新";
        }
        layui.layer.open({
            title : title,
            type : 2,
            area:["700px","500px"],
            maxmin:true,
            content : url
        });
    }

客户信息管理数据删除

客户信息数据后端删除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EEjdZB6v-1612166184684)(images\image-20200227221545509.png)]

客户信息后端删除核心代码
  • CustomerService.java
@Transactional(propagation = Propagation.REQUIRED)
public void deleteCustomer(Integer cid){
    Customer customer= selectByPrimaryKey(cid);
    AssertUtil.isTrue(null==cid||null==cid,"待删除记录不存在!");
    /**
         * 如果客户被删除
         *     级联 客户联系人 客户交往记录 客户订单  被删除
         *
         * 如果客户被删除
         *     如果子表存在记录  不支持删除
         */
    customer.setIsValid(0);
    AssertUtil.isTrue(updateByPrimaryKeySelective(customer)<1,"客户删除失败!");
}

  • CustomerController.java
@RequestMapping("delete")
@ResponseBody
public ResultInfo deleteCustomer(Integer id){
    customerService.deleteCustomer(id);
    return success("客户删除成功");
}

客户信息删除前端核心js

行监听添加删除事件

    /**
     * 行监听
     */
    table.on("tool(customers)", function(obj){
        var layEvent = obj.event;
        if(layEvent === "edit") {
            openAddOrUpdateCustomerDialog(obj.data.id);
        }else if(layEvent === "del") {
            layer.confirm('确定删除当前数据?', {icon: 3, title: "客户管理"}, function (index) {
                $.post(ctx+"/customer/delete",{id:obj.data.id},function (data) {
                    if(data.code==200){
                        layer.msg("操作成功!");
                        tableIns.reload();
                    }else{
                        layer.msg(data.msg, {icon: 5});
                    }
                });
            })
        }
    });

客户订单查看

客户订单数据查询

客户订单页面效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPYIee2m-1612166184685)(images\image-20200227222856436.png)]

客户订单展示前端js 事件添加

当点击订单查看时,此时添加客户信息表头工具栏事件

//头工具栏事件
table.on('toolbar(customers)', function(obj){
    var checkStatus = table.checkStatus(obj.config.id);
    switch(obj.event){
        case "add":
            openAddOrUpdateCustomerDialog();
            break;
        case "order":
            openOrderInfoDialog(checkStatus.data);
            break;
    };
});
// 打开订单查看页面
function openOrderInfoDialog(datas) {
    if(datas.length==0){
        layer.msg("请选择待查看订单对应客户!", {icon: 5});
        return;
    }
    if(datas.length>1){
        layer.msg("暂不支持批量查看!", {icon: 5});
        return;
    }
    var url  =  ctx+"/customer/orderInfoPage?cid="+datas[0].id;
    layui.layer.open({
        title : "客户管理-订单信息展示",
        type : 2,
        area:["700px","500px"],
        maxmin:true,
        content : url
    });
}

  • 客户信息管理控制器添加页面转发方法
@RequestMapping("orderInfoPage")
public String showOrderInfo(Integer cid, Model model){
    model.addAttribute("customer",customerService.selectByPrimaryKey(cid));
    return "customer/customer_order";
}

客户订单列表查询后端实现

  • CustomerOrderMapper.xml
<select id="selectByParams" parameterType="com.xxxx.crm.query.CustomerOrderQuery"
        resultType="com.xxxx.crm.vo.CustomerOrder">
    select
    <include refid="Base_Column_List"/>
    from t_customer_order
    <where>
        is_valid=1 and cus_id=#{cid}
        <if test="null != orderNo and orderNo !=''">
            and order_no=#{orderNo}
        </if>
        <if test="null !=state">
            and state=#{state}
        </if>
    </where>
</select>

  • CustomerOrderController.java
@RequestMapping("list")
@ResponseBody
public Map<String,Object>  queryCustomerOrdersByParams(CustomerOrderQuery customerOrderQuery){
    return customerOrderService.queryByParamsForTable(customerOrderQuery);

客户订单页面展示前端模板 & 核心js

  • customer_order.ftl 模板文件添加

    views/customer 目录下创建customer_order.ftl 文件

<!DOCTYPE html>
<html>
<head>
    <title>客户订单查看</title>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
    <div class="layui-col-md12">
        <div class="layui-card">
            <div class="layui-card-body">
                <form class="layui-form" >
                    <input name="id" type="hidden" value="${(customer.id)!}"/>
                    <div class="layui-form-item layui-row">
                        <div class="layui-col-xs6">
                            <label class="layui-form-label">客户名称</label>
                            <div class="layui-input-block">
                                <input type="text" class="layui-input"
                                       name="name" id="name"  value="${(customer.name)!}" readonly="readonly">
                            </div>
                        </div>
                        <div class="layui-col-xs6">
                            <label class="layui-form-label">法人</label>
                            <div class="layui-input-block">
                                <input type="text" class="layui-input"
                                       name="fr" id="fr" value="${(customer.fr)!}" readonly="readonly">
                            </div>
                        </div>
                    </div>

                    <div class="layui-form-item layui-row">
                        <div class="layui-col-xs6">
                            <label class="layui-form-label">客户地址</label>
                            <div class="layui-input-block">
                                <input type="text" class="layui-input"
                                       name="address"   value="${(customer.address)!}" readonly="readonly">
                            </div>
                        </div>
                        <div class="layui-col-xs6">
                            <label class="layui-form-label">联系电话</label>
                            <div class="layui-input-block">
                                <input type="text" class="layui-input"
                                        name="phone" value="${(customer.customer)!}" id="phone" readonly="readonly">
                            </div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>

    <div class="layui-col-md12">
        <table id="customerOrderList" class="layui-table"  lay-filter="customerOrders"></table>
    </div>
    <!--操作-->
    <script id="customerOrderListBar" type="text/html">
        <a class="layui-btn layui-btn-xs"  lay-event="info">订单详情</a>
    </script>
    <script type="text/javascript" src="${ctx}/static/js/customer/customer.order.js"></script>
</body>
</html>

  • customer.order.js 核心js
layui.use(['table','layer'],function(){
    var layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery,
        table = layui.table;
    //订单列表展示
    var  tableIns = table.render({
        elem: '#customerOrderList',
        url : ctx+'/order/list?cid='+$("input[name='id']").val(),
        cellMinWidth : 95,
        page : true,
        height : "full-125",
        limits : [10,15,20,25],
        limit : 10,
        toolbar: "#toolbarDemo",
        id : "customerOrderListTable",
        cols : [[
            {type: "checkbox", fixed:"center"},
            {field: "id", title:'编号',fixed:"true"},
            {field: 'orderNo', title: '订单编号',align:"center"},
            {field: 'orderDate', title: '下单日期',align:"center"},
            {field: 'address', title: '收货地址',align:"center"},
            {field: 'state', title: '支付状态',align:"center",templet:function (d) {
                    if(d.state==1){
                        return "已支付;"
                    }else{
                        return "未支付";
                    }
                }},
            {field: 'createDate', title: '创建时间',align:"center"},
            {field: 'updateDate', title: '更新时间',align:"center"},
            {title: '操作',fixed:"right",align:"center", minWidth:150,templet:"#customerOrderListBar"}
        ]]
    });
});

客户订单详情查看

客户订单详情效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-65aT5Rbe-1612166184687)(images\image-20200227231656266.png)]

订单详情页展示前端js

​ 客户订单页添加行监听事件,customer.order.js

/**
 * 行监听
 */
table.on("tool(customerOrders)", function(obj){
    var layEvent = obj.event;
    if(layEvent == "info") {
        openOrderDetailDialog(obj.data.id);
    }

});
// 打开添加计划项数据页面
function openOrderDetailDialog(id){
    var url  =  ctx+"/customer/orderDetailPage?orderId="+id;
    layui.layer.open({
        title : "订单详情查看",
        type : 2,
        area:["700px","400px"],
        maxmin:true,
        content : url
    });
}

订单详情模板页面转发与订单详情列表查询后端

  • CustomerController.java
@RequestMapping("orderDetailPage")
public String orderDetailPage(Integer orderId, Model model){
    model.addAttribute("order",orderService.queryOrderDetailByOrderId(orderId));
    return "customer/customer_order_detail";
}

  • OrderDetailsController.java
@Controller
@RequestMapping("order_details")
public class OrderDetailsController extends BaseController {

    @Resource
    private OrderDetailsService orderDetailsService;

    @RequestMapping("list")
    @ResponseBody
    public Map<String,Object> queryOrderDetailsByParams(OrderDetailsQuery orderDetailsQuery){
        return orderDetailsService.queryByParamsForTable(orderDetailsQuery);
    }
}

订单详情前端模板 & 核心js

views/customer 目录下新建customer_order_detail.ftl 文件

<!DOCTYPE html>
<html>
<head>
    <title>订单详情查看</title>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
<div class="layui-col-md12">
    <div class="layui-card">
        <div class="layui-card-body">
            <form class="layui-form" >
                <input name="id" type="hidden" value="${(order.id)!}"/>
                <div class="layui-form-item layui-row">
                    <div class="layui-col-xs6">
                        <label class="layui-form-label">订单编号</label>
                        <div class="layui-input-block">
                            <input type="text" class="layui-input"
                                   name="orderNo" id="orderNo"  value="${(order.orderNo)!}" readonly="readonly">
                        </div>
                    </div>
                    <div class="layui-col-xs6">
                        <label class="layui-form-label">总金额(¥)</label>
                        <div class="layui-input-block">
                            <input type="text" class="layui-input"
                                   name="total" id="total" value="${(order.total)!}" readonly="readonly">
                        </div>
                    </div>
                </div>

                <div class="layui-form-item layui-row">
                    <div class="layui-col-xs6">
                        <label class="layui-form-label">物流地址</label>
                        <div class="layui-input-block">
                            <input type="text" class="layui-input"
                                   name="address"  value="${(order.address)!}" readonly="readonly">
                        </div>
                    </div>
                    <div class="layui-col-xs6">
                        <label class="layui-form-label">支付状态</label>
                        <div class="layui-input-block">
                            <input type="text" class="layui-input"
                                    name="status" value="${(order.status)!}"  readonly="readonly">
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

<div class="layui-col-md12">
    <table id="orderDetailList" class="layui-table"  lay-filter="orderDetails"></table>
</div>

<script type="text/javascript" src="${ctx}/static/js/customer/customer.order.details.js"></script>
</body>
</html>

js/customer 目录下新建customer.order.details.js

layui.use(['table','layer',"form"],function(){
    var layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery,
        table = layui.table;
    //订单列表展示
    var  tableIns = table.render({
        elem: '#orderDetailList',
        url : ctx+'/order_details/list?orderId='+$("input[name='id']").val(),
        cellMinWidth : 95,
        page : true,
        height : "full-125",
        limits : [10,15,20,25],
        limit : 10,
        toolbar: "#toolbarDemo",
        id : "customerOrderListTable",
        cols : [[
            {type: "checkbox", fixed:"center"},
            {field: "id", title:'编号',fixed:"true"},
            {field: 'goodsName', title: '商品名称',align:"center"},
            {field: 'goodsNum', title: '商品数量',align:"center"},
            {field: 'unit', title: '单位',align:"center"},
            {field: 'price', title: '单价(¥)',align:"center"},
            {field: 'sum', title: '总价(¥)',align:"center"},
            {field: 'createDate', title: '创建时间',align:"center"},
            {field: 'updateDate', title: '更新时间',align:"center"}
        ]]
    });
});

对于客户联系人管理,客户交往记录管理两个模块功能实现,这里不再提现,大家可以私下自己模仿前面模块实现。

客户流失管理

客户流失管理页面效果

客户流失管理后端分析 & 实现

客户流失规则定义

​ 客户自创建超过六个月以来未与企业产生任何订单或者客户最后下单日期距离现在超过六个月的客户定义为流失客户

流失客户数据sql查询分析与实现

这里对于客户流失规则的定义包含两种情况

  • 创建的客户数据距离当前时间超过了6个月

  • 客户没有下单 或者最后一次下单时间距离当前时间超过6个月

在实现流失客户数据查询时采用逆向查询方式 先将有效客户查出来然后按照排除法获取无效客户

SELECT
    *
FROM
    t_customer c
WHERE
    c.is_valid = 1
    AND c.state = 0
    AND DATE_ADD(c.create_date,INTERVAL 6 MONTH) &lt;NOW()
    AND c.id NOT IN (
        SELECT
            o.cus_id
        FROM
            t_customer_order o
        WHERE
            o.state = 1
            AND o.is_valid = 1
            AND DATE_ADD(o.order_date,INTERVAL 6 MONTH) &gt;NOW()
    )

客户流失关系后端service 实现

@Transactional(propagation = Propagation.REQUIRED)
public void updateCustomerState(){
    List<Customer> lossCutomers = customerMapper.queryLossCustomers();
    if(null !=lossCutomers && lossCutomers.size()>0){
        List<CustomerLoss> customerLosses= new ArrayList<CustomerLoss>();
        List<Integer> lossCusIds=new ArrayList<Integer>();
        lossCutomers.forEach(customer->{
            CustomerLoss customerLoss=new CustomerLoss();
            // 设置最后下单时间
            CustomerOrder lastCustomerOrder = customerOrderMapper.queryLastCustomerOrderByCusId(customer.getId());
            if(null !=lastCustomerOrder){
                customerLoss.setLastOrderTime(lastCustomerOrder.getOrderDate());
            }
            customerLoss.setCreateDate(new Date());
            customerLoss.setCusManager(customer.getCusManager());
            customerLoss.setCusName(customer.getName());
            customerLoss.setCusNo(customer.getKhno());
            customerLoss.setIsValid(1);
            //  设置客户流失状态为暂缓流失状态
            customerLoss.setState(0);
            customerLoss.setUpdateDate(new Date());
            customerLosses.add(customerLoss);
            lossCusIds.add(customer.getId());
        });
        AssertUtil.isTrue(customerLossMapper.insertBatch(customerLosses)<customerLosses.size(),"客户流失数据流转失败!");
        AssertUtil.isTrue(customerMapper.updateCustomerStateByIds(lossCusIds)<lossCusIds.size(),"客户流失数据流转失败!");
    }
}

流失客户定时任务处理添加

​ 当实现了客户数据转移业务逻辑代码后,这里需要思考一个问题:客户数据量的问题随着时间的积累,流失的客户数据可能就比较大,如果数据的获取在用户查询时进行,此时后端对于数据的查询就会变得很慢,此时可以使用我们之前讲到的定时任务来处理,后台通过定时器来对流失客户数据定时进行转移处理,从而当前端用户查询时只需到客户流失表查询流失数据即可。

@Service
public class TaskService {

    @Resource
    private CustomerService customerService;

    @Scheduled(cron = "0/2 * * * * ?")
    public void job(){
        System.out.println("定时任务开始执行-->"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        customerService.updateCustomerState();
    }
}

Starter开启定时任务环境配置

@SpringBootApplication
@MapperScan("com.xxxx.crm.dao")
@EnableScheduling
public class Starter {

    public static void main(String[] args) {
        SpringApplication.run(Starter.class);
    }
}

客户流失管理前端页面展示

  • CustomerLossController.java
@Controller
@RequestMapping("customer_loss")
public class CustomerLossController extends BaseController {
    @Resource
    private CustomerLossService customerLossService;

    @RequestMapping("index")
    private String index(){
        return "customerLoss/customer_loss";
    }

    @RequestMapping("list")
    @ResponseBody
    public Map<String,Object> queryCustomerLossByParams(CustomerLossQuery customerLossQuery){
        return customerLossService.queryByParamsForTable(customerLossQuery);
    }

  • customer_loss.ftl 模板文件

views/customerLoss 目录下添加cutomer_loss.ftl文件,显示流失客户记录数据

<!DOCTYPE html>
<html>
<head>
   <title>流失客户管理</title>
   <#include "../common.ftl">
</head>
<body class="childrenBody">

<form class="layui-form" >
   <blockquote class="layui-elem-quote quoteBox">
      <form class="layui-form">
         <div class="layui-inline">
            <div class="layui-input-inline">
               <input type="text" name="cusNo"
                     class="layui-input
               searchVal" placeholder="客户编号" />
            </div>
            <div class="layui-input-inline">
               <input type="text" name="cusName" class="layui-input
               searchVal" placeholder="客户名" />
            </div>
            <div class="layui-input-inline">
                    <select name="state"  id="state"  >
                        <option value="" >请选择</option>
                        <option value="0">暂缓流失</option>
                        <option value="1" >确认流失</option>
                    </select>
            </div>
            <a class="layui-btn search_btn" data-type="reload"><i
                     class="layui-icon">&#xe615;</i> 搜索</a>
         </div>
      </form>
   </blockquote>
   <table id="customerLossList" class="layui-table"  lay-filter="customerLosses"></table>

   <script type="text/html" id="toolbarDemo">
   </script>

   <!--操作-->
   <script id="op" type="text/html" >
      {{# if (d.state=== 0 ) { }}
         <a href="javascript:;"  class="layui-btn layui-btn-warm layui-btn-xs"  lay-event="add">添加暂缓</a>
      {{# } else { }}
         <a href="javascript:;" class="layui-btn layui-btn-normal layui-btn-xs"  lay-event="info">详情</a>
      {{# } }}
   </script>

</form>
<script type="text/javascript" src="${ctx}/static/js/customerLoss/customer.loss.js"></script>

</body>
</html>

  • customer.loss.js 文件添加

js/customerLoss 目录下添加customer.loss.js 文件

layui.use(['table','layer',"form"],function(){
    var layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery,
        table = layui.table;
    //用户列表展示
    var  tableIns = table.render({
        elem: '#customerLossList',
        url : ctx+'/customer_loss/list',
        cellMinWidth : 95,
        page : true,
        height : "full-125",
        limits : [10,15,20,25],
        limit : 10,
        toolbar: "#toolbarDemo",
        id : "customerLossListTable",
        cols : [[
            {type: "checkbox", fixed:"center"},
            {field: "id", title:'编号',fixed:"true"},
            {field: 'cusNo', title: '客户编号',align:"center"},
            {field: 'cusName', title: '客户名称',align:"center"},
            {field: 'cusManager', title: '客户经理',align:"center"},
            {field: 'lastOrderTime', title: '最后下单时间',align:"center"},
            {field: 'lossReason', title: '流失原因',align:"center"},
            {field: 'confirmLossTime', title: '确认流失时间',align:"center"},
            {field: 'createDate', title: '创建时间',align:"center"},
            {field: 'updateDate', title: '更新时间',align:"center"},
            {title: '操作',fixed:"right",align:"center", minWidth:150,templet:"#op"}
        ]]
    });

    $(".search_btn").on("click",function(){
        table.reload("customerLossListTable",{
            page: {
                curr: 1 //重新从第 1 页开始
            },
            where: {
                cusNo: $("input[name='cusNo']").val(),  //用户名
                cusName: $("input[name='cusName']").val(),  //邮箱
                state: $("#state").val()  //手机号
            }
        })
    });
});

流失客户暂缓处理

​ 这里流失客户数据展示成功后,并不代表客户已确认流失,需要作下一步流失暂缓处理,所以这里在展示客户数据时有显示详情展示的,有显示添加暂缓的处理,对流失客户做进一步确认操作实现过程可以参看计划数据开发操作这里不再提供具体实现流程,页面效果如下:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Crm-客户管理 客户管理表结构设计 客户信息管理模块表结构 这里主要涉及表: ​ t_custome...
    yy666777阅读 558评论 0 0
  • 服务管理表结构设计 t_customer_serve客户服务表字段字段类型字段限制字段描述主键idint(11)自...
    yy666777阅读 478评论 0 0
  • account 平台账号表(公众号、小程序、PC等) 字段名数据类型说明acidint(10)主键uniacidi...
    zlchen阅读 717评论 0 0
  • 1.数据库设计 1.1系统用户表 1.2客户信息表 1.3数据字典表 2.系统环境搭建 2.1准备需要的JAR包 ...
    Z_PS阅读 5,018评论 1 3
  • 业务流程:eseller = bd cs (我们公司的管理);client = 代理商;() 我们的代理商不是代...
    陆遥远阅读 1,699评论 0 1