项目实战(P15)(Day 43)

2024-02-19 20:40
文章标签 实战 项目 43 day p15

本文主要是介绍项目实战(P15)(Day 43),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

学习目标:

学习内容:

        客户流失实现:

客户流失的思想:

dao层

service层

出现问题:

遇到问题:

解决方法:

        客户流失管理和暂缓流失列表:

思想:

DAO层

 service层

 Controller层

确认流失操作

service层:

 controller层:


学习目标:

客户流失实现和客户流失页面和客户暂缓流失列表的增删改查


学习内容:

        客户流失实现:

客户流失的思想:

流失客户定义:客户自创建起,超过六个月未域企业产生任何订单或者客户最后下单日期距离现在超过六个月的客户定义为流失客户规则分析:1.客户数据录入到系统的时间距离现在的时间超过六个月    (前提条件)2.客户与公司没有产生(已支付)订单记录||客户最后下单时间距离现在时间超过六个月2.1查询没有订单记录的客户2.2 查询最后的订单时间距离现在超过六个月的客户实现思路:查询2.1与2.2的客户并去除重复的记录具体实现:用反向查询来等级啊与查询2.1和2.2的记录查询产生过订单记录并且最后的下单时间距离现在时间不超过六个与的客户,然后再从客户表中排除这些客户ps:在(1)的基础上满足(2)的情况更新客户流失状态1.查询待流失的客户数据2.将流失客户数据批量提案极爱到客户流失表中3.批量更新客户的流失状态 0=正常客户 1=流失客户

dao层

根据思路来书写查询语句

//查询带流失的客户List<Customer> queryLossCustomers();
//通过客户ID批量跟新客户流失状态int updateCustomerStateByIds(List<Integer> lossCustomerIds);//通过客户ID查询最后一条订单记录CustomerOrder queryLossCustomerOrderByCustomerId(Integer id);
<!-- 查询待流失的客户 --><select id="queryLossCustomers" resultType="com.xxxx.crm.vo.Customer">SELECT*FROMt_customer cWHEREis_valid = 1AND state = 0AND DATE_ADD(c.create_date,INTERVAL 6 MONTH) &lt; NOW()AND c.id NOT IN (SELECT DISTINCTcus_idFROMt_customer_order oWHEREis_valid = 1AND state = 1AND DATE_ADD(o.order_date,INTERVAL 6 MONTH) &gt; NOW())</select>
<!-- 批量跟新客户流失状态 --><update id="updateCustomerStateByIds" >updatet_customersetstate = 1whereid in<foreach collection="list" item="item" open="(" close=")" separator=",">#{item}</foreach></update><!-- 查询指定客户最后一条订单记录 --><select id="queryLossCustomerOrderByCustomerId" parameterType="int" resultType="com.xxxx.crm.vo.CustomerOrder">SELECT*FROMt_customer_orderWHEREis_valid = 1AND cus_id = {customerId}ORDER BYorder_date DESCLIMIT 1</select><!-- 批量添加 --><insert id="insertBatch">insert intot_customer_loss (cus_no, cus_name, cus_manager, last_order_time, confirm_loss_time, state, loss_reason, is_valid, create_date, update_date)values<foreach collection="list" separator="," item="item">(#{item.cusNo},#{item.cusName},#{item.cusManager},#{item.lastOrderTime},#{item.confirmLossTime},#{item.state},#{item.lossReason},1,now(),now())</foreach></insert>

service层

先把所有的流失客户查出来,然后再把这些数据放到流失表中,最后跟新客户状态为流失

 /*** 更新客户流失状态*   1.查询待流失的客户数据*   2.将流失客户数据批量提案极爱到客户流失表中*   3.批量更新客户的流失状态 0=正常客户 1=流失客户* @author QQ星** @param* @return void* @Date 2022/4/20 21:45*/@Transactional(propagation = Propagation.REQUIRED)public void updateCustomerState(){/* 1.查询待流失的客户数据 */List<Customer> lossCustomerList = customerMapper.queryLossCustomers();/* 2.将流失客户数据批量添加到客户流失表中 *///判断流失客户数据是否存在if (lossCustomerList !=null && lossCustomerList.size()>0){//定义集合 用来接受流失客户IDList<Integer> lossCustomerIds = new ArrayList<>();//定义流失客户列表List<CustomerLoss> customerLossList = new ArrayList<>();//遍历查询到的流失客户数据lossCustomerList.forEach(customer -> {// 定义流失客户对象CustomerLoss customerLoss = new CustomerLoss();// 创建时间  系统当前时间customerLoss.setCreateDate(new Date());// 客户经理customerLoss.setCusManager(customer.getCusManager());// 客户名称customerLoss.setCusName(customer.getName());// 客户编号customerLoss.setCusNo(customer.getKhno());// 是否有效  1=有效customerLoss.setIsValid(1);// 修改时间  系统当前时间customerLoss.setUpdateDate(new Date());// 客户流失状态   0=暂缓流失状态  1=确认流失状态customerLoss.setState(0);//客户最后下单时间//通过客户ID查询最后的订单记录(最后一条订单记录)CustomerOrder customerOrder = customerOrderMapper.queryLossCustomerOrderByCustomerId(customer.getId());//判断客户订单是否存在,如果存在,则设置最后下单时间if(customerOrder !=null){customerLoss.setLastOrderTime(customerOrder.getOrderDate());}//把流失客户对象设置到对应的集合中customerLossList.add(customerLoss);//将流失客户id设置到哦对应的集合里lossCustomerIds.add(customer.getId());});//批量添加流失客户记录AssertUtil.isTrue(customerLossMapper.inserBatch(customerLossList) !=customerLossList.size(),"客户流失数据转移失败");/* 3.批量更新客户的流失状态 */AssertUtil.isTrue(customerMapper.updateCustomerStateByIds(lossCustomerIds)!=lossCustomerIds.size(),"更新客户失败");}}
}

出现问题:

每次都需要把数据放到loss表中,那万一有傻呗天天刷新天天放数据效率太低,根据流失客户比较久的这个特性来设置一个任务来定时任务

/*** 定时任务* @author QQ星** @Date 2022/4/21 23:30*/
@Component
public class JobTask {@Resourceprivate CustomerService customerService;/*** 每月最后一天的23:42分执行* @author QQ星** @param* @return void* @Date 2022/4/21 23:44*///测试是否有效//@Scheduled(cron = "0/2 * * * * ?")@Scheduled(cron = "0 42 23 L * ? ")public void job(){System.out.println("定时任务开始执行 --> " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));//调用需要被定时执行的方法customerService.updateCustomerState();}
}

遇到问题:

Spring默认定时@Scheduled不支持L关键字

解决方法:

        客户流失管理和暂缓流失列表:

思想:

添加暂缓数据1. 参数校验流失客户ID  lossId非空,数据存在暂缓措施内容 measure非空2. 设置参数的默认值是否有效默认有效,1创建时间系统当前时间修改时间系统当前时间3. 执行添加操作,判断受影响的行数修改暂缓数据1. 参数校验主键ID    id非空,数据存在流失客户ID  lossId非空,数据存在暂缓措施内容 measure非空2. 设置参数的默认值修改时间系统当前时间3. 执行修改操作,判断受影响的行数删除暂缓数据1. 判断id是否为空,且数据存在2. 设置isvalid为03. 执行更新操作,判断受影响的行数更新流失客户的流失状态1. 参数校验判断id非空且对应的数据存在流失原因非空2. 设置参数的默认值设置流失状态  state=1  0=暂缓流失,1=确认流失流失原因客户流失时间  系统当前时间更新时间     系统当前时间3. 执行更新操作,判断受影响的行数

可参考客户的开发计划

DAO层

加入两个多条件查询,用来查询列表

<!-- 多条件查询 --><select id="selectByParams" parameterType="com.xxxx.crm.query.CustomerLossQuery" resultType="com.xxxx.crm.vo.CustomerLoss">select<include refid="Base_Column_List"></include>fromt_customer_loss<where>is_valid = 1<if test="null != customerNo and customerNo != ''">and cus_no = #{customerNo}</if><if test="null != customerName and customerName != ''">and cus_name like concat('%',#{customerName},'%')</if><if test="null != state">and state = #{state}</if></where></select><!-- 多条件查询 --><select id="selectByParams" parameterType="com.xxxx.crm.query.CustomerReprieveQuery" resultType="com.xxxx.crm.vo.CustomerReprieve">select<include refid="Base_Column_List"></include>fromt_customer_reprieve<where>is_valid = 1<if test="null != lossId">and loss_id = #{lossId}</if></where></select>

 service层

CustomerLoss层只有一个分页查询与CustomerReprieve中分页查询一样,创建两个query供前端传给后端即可


public class CustomerLossQuery extends BaseQuery {// 客户编号private String customerNo;// 客户名称private String customerName;// 流失状态  0=暂缓流失状态  1=确认流失状态private Integer state;public String getCustomerNo() {return customerNo;}public void setCustomerNo(String customerNo) {this.customerNo = customerNo;}public String getCustomerName() {return customerName;}public void setCustomerName(String customerName) {this.customerName = customerName;}public Integer getState() {return state;}public void setState(Integer state) {this.state = state;}
}

public class CustomerReprieveQuery extends BaseQuery {//流失客户Idprivate Integer lossId;public Integer getLossId() {return lossId;}public void setLossId(Integer lossId) {this.lossId = lossId;}
}
@Service
public class CustomerReprieveService extends BaseService<CustomerReprieve,Integer> {@Resourceprivate CustomerReprieveMapper customerReprieveMapper;@Resourceprivate CustomerLossMapper customerLossMapper;/*** 分页查询流失客户暂缓操作的列表* @author QQ星** @param customerReprieveQuery* @return java.util.Map<java.lang.String,java.lang.Object>* @Date 2022/4/26 22:06*/public Map<String, Object> queryCustomerReprieveByParams(CustomerReprieveQuery customerReprieveQuery) {Map<String, Object> map = new HashMap<>();// 开启分页PageHelper.startPage(customerReprieveQuery.getPage(), customerReprieveQuery.getLimit());// 得到对应分页对象PageInfo<CustomerReprieve> pageInfo = new PageInfo<>(customerReprieveMapper.selectByParams(customerReprieveQuery));// 设置map对象map.put("code",0);map.put("msg","success");map.put("count",pageInfo.getTotal());// 设置分页好的列表map.put("data",pageInfo.getList());return map;}/*** 添加暂缓数据*   1. 参数校验*       流失客户ID  lossId*           非空,数据存在*       暂缓措施内容 measure*           非空*   2. 设置参数的默认值*       是否有效*           默认有效,1*       创建时间*           系统当前时间*       修改时间*           系统当前时间*   3. 执行添加操作,判断受影响的行数* @author QQ星** @param customerReprieve* @return void* @Date 2022/4/26 22:55*/@Transactional(propagation = Propagation.REQUIRED)public void addCustomerRepr(CustomerReprieve customerReprieve) {/* 1. 参数校验 */checkParams(customerReprieve.getLossId(), customerReprieve.getMeasure());/* 2. 设置参数的默认值 */customerReprieve.setIsValid(1);customerReprieve.setCreateDate(new Date());customerReprieve.setUpdateDate(new Date());/* 3. 执行添加操作,判断受影响的行数 */AssertUtil.isTrue(customerReprieveMapper.insertSelective(customerReprieve) < 1, "添加暂缓数据失败!");}/*** 参数校检* @author QQ星** @param lossId* @param measure* @return void* @Date 2022/4/26 23:01*/private void checkParams(Integer lossId, String measure) {// 流失客户ID lossId    非空,数据存在AssertUtil.isTrue(null == lossId|| customerLossMapper.selectByPrimaryKey(lossId) == null, "流失客户记录不存在!");// 暂缓措施内容 measure   非空AssertUtil.isTrue(StringUtils.isBlank(measure), "暂缓措施内容不能为空!");}/*** 修改暂缓数据* 1. 参数校验*      主键ID    id*          非空,数据存在*      流失客户ID  lossId*          非空,数据存在*      暂缓措施内容 measure*          非空*  2. 设置参数的默认值*      修改时间*          系统当前时间*  3. 执行修改操作,判断受影响的行数* @author QQ星** @param customerReprieve* @return void* @Date 2022/4/26 23:14*/@Transactional(propagation = Propagation.REQUIRED)public void updateCustomerRepr(CustomerReprieve customerReprieve) {/* 1. 参数校验 */// 主键ID    idAssertUtil.isTrue(null == customerReprieve.getId()|| customerReprieveMapper.selectByPrimaryKey(customerReprieve.getId()) == null, "待更新记录不存在!");// 参数校验checkParams(customerReprieve.getLossId(), customerReprieve.getMeasure());/* 2. 设置参数的默认值 */customerReprieve.setUpdateDate(new Date());/* 3. 执行修改操作,判断受影响的行数 */AssertUtil.isTrue(customerReprieveMapper.updateByPrimaryKeySelective(customerReprieve) < 1, "修改暂缓数据失败!");}/*** 删除暂缓数据*  1. 判断id是否为空,且数据存在*  2. 设置isvalid为0*  3. 执行更新操作,判断受影响的行数* @author QQ星** @param id* @return void* @Date 2022/4/26 23:35*/@Transactional(propagation = Propagation.REQUIRED)public void deleteCustomerRepr(Integer id) {// 判断id是否为空AssertUtil.isTrue(null == id, "待删除记录不存在!");// 通过id查询暂缓数据CustomerReprieve customerReprieve = customerReprieveMapper.selectByPrimaryKey(id);// 判断数据是否存在AssertUtil.isTrue(null == customerReprieve, "待删除记录不存在!");// 设置isValidcustomerReprieve.setIsValid(0);customerReprieve.setUpdateDate(new Date());// 执行更新操作,判断受影响的行数AssertUtil.isTrue(customerReprieveMapper.updateByPrimaryKeySelective(customerReprieve) < 1, "删除暂缓数据失败!");}
}

 Controller层

在CustomerLossController层中书写首页和流失客户列表加上打开添加暂缓按钮的界面   


@RequestMapping("customer_loss")
@Controller
public class CustomerLossController extends BaseController {@ResourceCustomerLossService customerLossService;/*** 进入客户流失管理页面* @author QQ星** @param* @return java.lang.String* @Date 2022/4/26 0:27*/@RequestMapping("index")public String index(){return "customerLoss/customer_loss";}/*** 分页条件查询流失客户列表* @author QQ星** @param customerLossQuery* @return java.util.Map<java.lang.String,java.lang.Object>* @Date 2022/4/26 1:14*/@RequestMapping("list")@ResponseBodypublic Map<String, Object> queryCustomerLossByParams(CustomerLossQuery customerLossQuery) {return customerLossService.queryCustomerLossByParams(customerLossQuery);}/*** 打开添加暂缓页面* @author QQ星** @param lossId* @param model* @return java.lang.String* @Date 2022/4/26 21:49*/@RequestMapping("toCustomerLossPage")public String toCustomerLossPage(Integer lossId, Model model) {// 通过流失客户的ID查询对应流失客户的记录CustomerLoss customerLoss = customerLossService.selectByPrimaryKey(lossId);// 将流失客户对应的数据存到请求域中model.addAttribute("customerLoss", customerLoss);return "customerLoss/customer_rep";}
}

在CustomerReprieveController层中书写增删改查操做以及打开添加删除页面


@RequestMapping("customer_rep")
@Controller
public class CustomerReprieveController extends BaseController {@Resourceprivate CustomerReprieveService customerReprieveService;/*** 分页查询流失客户暂缓操作列表* @author QQ星** @param customerReprieveQuery* @return java.util.Map<java.lang.String,java.lang.Object>* @Date 2022/4/26 22:05*/@RequestMapping("list")@ResponseBodypublic Map<String, Object> queryCustomerReprieveByParams(CustomerReprieveQuery customerReprieveQuery) {return customerReprieveService.queryCustomerReprieveByParams(customerReprieveQuery);}/*** 添加暂缓数据* @author QQ星** @param customerReprieve* @return com.xxxx.crm.base.ResultInfo* @Date 2022/4/26 22:51*/@PostMapping("add")@ResponseBodypublic ResultInfo addCustomerRepr(CustomerReprieve customerReprieve) {customerReprieveService.addCustomerRepr(customerReprieve);return success("添加暂缓数据成功!");}/*** 跟新暂缓数据* @author QQ星** @param customerReprieve* @return com.xxxx.crm.base.ResultInfo* @Date 2022/4/26 23:11*/@PostMapping("update")@ResponseBodypublic ResultInfo updateCustomerRepr(CustomerReprieve customerReprieve) {customerReprieveService.updateCustomerRepr(customerReprieve);return success("修改暂缓数据成功!");}/*** 打开添加/修改暂缓数据的页面* @author QQ星** @param lossId* @param request* @param id* @return java.lang.String* @Date 2022/4/26 23:25*/@RequestMapping("toAddOrUpdateCustomerReprPage")public String toAddOrUpdateCustomerReprPage(Integer lossId, HttpServletRequest request, Integer id) {// 将流失客户ID存到作用域中request.setAttribute("lossId", lossId);// 判断ID是否为空if (id != null) {// 通过主键ID查询暂缓数据CustomerReprieve customerRep = customerReprieveService.selectByPrimaryKey(id);// 设置到请求域中request.setAttribute("customerRep", customerRep);}return "customerLoss/customer_rep_add_update";}/*** 删除暂缓数据* @author QQ星** @param id* @return com.xxxx.crm.base.ResultInfo* @Date 2022/4/26 23:28*/@PostMapping("delete")@ResponseBodypublic ResultInfo updateCustomerRepr(Integer id) {customerReprieveService.deleteCustomerRepr(id);return success("删除暂缓数据成功!");}
}

确认流失操作

service层:

    /*** 更新流失客户的流失状态*  1. 参数校验*      判断id非空且对应的数据存在*      流失原因非空*  2. 设置参数的默认值*      设置流失状态  state=1  0=暂缓流失,1=确认流失*      流失原因*      客户流失时间  系统当前时间*      更新时间     系统当前时间*  3. 执行更新操作,判断受影响的行数* @author QQ星** @param id* @param lossReason* @return void* @Date 2022/4/26 00:30*/@Transactional(propagation = Propagation.REQUIRED)public void updateCustomerLossStateById(Integer id, String lossReason) {/* 1. 参数校验 */// 判断id非空AssertUtil.isTrue(null == id, "待确认流失的客户不存在!");// 通过id查询流失客户的记录CustomerLoss customerLoss = customerLossMapper.selectByPrimaryKey(id);// 判断流失客户记录是否存在AssertUtil.isTrue(null == customerLoss, "待确认流失的客户不存在!");// 流失原因非空AssertUtil.isTrue(StringUtils.isBlank(lossReason), "流失原因不能为空!");/* 2. 设置参数的默认值 */// 设置流失状态  state=1  0=暂缓流失,1=确认流失customerLoss.setState(1);// 设置流失原因customerLoss.setLossReason(lossReason);// 客户流失时间  系统当前时间customerLoss.setConfirmLossTime(new Date());// 更新时间     系统当前时间customerLoss.setUpdateDate(new Date());/* 3. 执行更新操作,判断受影响的行数 */AssertUtil.isTrue(customerLossMapper.updateByPrimaryKeySelective(customerLoss) < 1, "确认流失失败!");}

 controller层:

/*** 确认流失* @author QQ星** @param id* @param lossReason* @return com.xxxx.crm.base.ResultInfo* @Date 2022/4/26 00:11*/@PostMapping("updateCustomerLossStateById")@ResponseBodypublic ResultInfo updateCustomerLossStateById(Integer id, String lossReason) {customerLossService.updateCustomerLossStateById(id, lossReason);return success("确认流失成功!");}

这篇关于项目实战(P15)(Day 43)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/725904

相关文章

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

sky-take-out项目中Redis的使用示例详解

《sky-take-out项目中Redis的使用示例详解》SpringCache是Spring的缓存抽象层,通过注解简化缓存管理,支持Redis等提供者,适用于方法结果缓存、更新和删除操作,但无法实现... 目录Spring Cache主要特性核心注解1.@Cacheable2.@CachePut3.@Ca

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

Java 正则表达式的使用实战案例

《Java正则表达式的使用实战案例》本文详细介绍了Java正则表达式的使用方法,涵盖语法细节、核心类方法、高级特性及实战案例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、正则表达式语法详解1. 基础字符匹配2. 字符类([]定义)3. 量词(控制匹配次数)4. 边

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

SpringBoot通过main方法启动web项目实践

《SpringBoot通过main方法启动web项目实践》SpringBoot通过SpringApplication.run()启动Web项目,自动推断应用类型,加载初始化器与监听器,配置Spring... 目录1. 启动入口:SpringApplication.run()2. SpringApplicat