宅急送 项目第七天 取派业务模块

时间:2022-03-31 20:57:10

1. 取派模块业务分析

已经完成基础设置模块 : 收派标准管理、 取派员管理、 区域管理、分区管理、定区管理
基础设置模块的数据,实际上为后期业务模块提供支持

宅急送 项目第七天 取派业务模块

宅急送 项目第七天 取派业务模块

重点开发任务,业务受理模块

业务受理功能
受理环节,是宅急送业务的开始,作为服务前端,客户通过电话、网络等多种方式进行委托,业务受理员通过与客户交流,获取客户的服务需求和具体委托信息,将服务指令输入我司服务系统。

业务受理后,业务人员需要将客户信息,录入为 “业务通知单”(代表客户一份快递需要) ,系统进行自动分单,产生“工单” , 系统自动识别客户对应定区的负责人员, 由指定取派员到客户家取货

工作单功能 “工作单”
工作单基础信息的录入是信息的入口,并且也是本系统中人机交互最多的环节;基础信息录入的及时、准确、完整会直接影响后续的实物的运转配载、小件员交款、财务收入确认、成本垫付分摊,所以对数据的准确性、及时性、完整性要求比较高。

业务通知单: 客户快递申请
工单: 取派员取件任务
工作单 : 面向物流的数据,真实货物信息

宅急送 项目第七天 取派业务模块

查台转单
当自动下单成功,但是由于基础信息错误,而导致分配的取货人员错误,通过查台转单来解决此类失误。 (分错取派员,更换取派员 )

人工调度
在自动下单的时候,由于取件地址无法匹配取件人员,就转入人工调度。 (业务人员,手动为客户,指定取派员 )

2. 取派业务模块— 数据库设计

根据 “业务受理数据表.xls” 设计PowerDesigner PDM 物理数据模型
参考 “业务受理.pdm”

根据PDM生成SQL 脚本,将脚本导入 数据库

宅急送 项目第七天 取派业务模块

qp_noticebill 业务通知单
qp_workbill 工单
qp_workordermanage 工作单

根据数据表,使用MyEclipse 反转生成PO类和hbm映射

宅急送 项目第七天 取派业务模块

修改生成的类名

宅急送 项目第七天 取派业务模块

3. 业务受理功能

客户提交快递申请 ,业务人员将客户业务信息,录入到系统

宅急送 项目第七天 取派业务模块

3.1. 业务通知单页面编写

业务通知单录入页面 : /WEB-INF/pages/qupai/noticebill_add.jsp
页面存在大form 表单 ,使用EasyUI form 控件

validatebox (验证框) :非空、长度 
<input type="text" class="easyui-validatebox" required="true" />

numberbox (数字框) :对validatebox扩展,只允许输入数字 
<input type="text" class="easyui-numberbox" required="true" />

combobox (下拉框) : 可以对 <input> 元素,生成下拉列表 

datebox (日历控件) : 选择日期 
<input type="text" class="easyui-datebox" data-options="required:true,editable:false"/>

检查业务受理form表单,元素name属性和 NoticeBill 实体类是否一致
为form添加 点击提交事件

// 点击新单按钮,将业务通知单 保存
$('#save').click(function(){
    if($("#noticebillForm").form('validate')){
        $('#noticebillForm').submit();
    }else{
        $.messager.alert('警告','表单存在非法数据项!','warning');
    }
});

3.2. 编写业务受理服务器代码

3.2.1. 完成基本代码结构

public class NoticeBillAction extends BaseAction implements ModelDriven<NoticeBill> {
}
public interface NoticeBillService {
}
public class NoticeBillServiceImpl extends BaseService implements NoticeBillService {
}

将DAO 注入Service
将Service 注入 Action

在业务受理时,产生业务通知单的数据,在保存到数据库的时候,需要进行自动分单的操作

1、 用客户取货地址通过远程调用,访问CRM,比较CRM系统中客户地址 ,查找客户对应定区,再通过定区获得 取派员信息 
2、 如果不能在CRM中找到客户对应定区,使用当前发件人地址去匹配分区地址 ,就可以通过分区去管理定区,匹配到取派员 

如果自动分单成功,需要在数据表 qp_workbill 生成一条工单记录 !
如果自动分单失败,进入人工调度环节

3.2.2. 自动分单 — 去CRM匹配客户的地址

宅急送 项目第七天 取派业务模块

修改CRM业务接口 CustomerService

// 根据 客户地址 查询 定区编码
public String findDecidedZoneIdByCustomerAddress(String address);

宅急送 项目第七天 取派业务模块

将修改后的CustomerService 接口,复制BOS系统 !!!
编写 CustomerServiceTest测试用例 ,完成接口调试

3.2.3. 自动分单 —- 匹配分区的关键字

<query name="Subarea.findByAddresskey">
        <![CDATA[from Subarea where addresskey = ?]]>
</query>
public void saveNoticeBill(NoticeBill noticeBill) {
    // 将业务通知单数据保存到数据库
    noticeBillDAO.save(noticeBill);

    // 自动分单
    // 1 、使用当前取件地址,去查询CRM系统 定区编码
    String decidedZoneId = customerService.findDecidedZoneIdByCustomerAddress(noticeBill.getPickaddress());
    if (decidedZoneId == null) {
        // 未查到
        // 2、匹配分区 关键字
        String[] addressArray = noticeBill.getPickaddress().split(" "); // 北京市海淀区 xxx路 1号楼
        if (addressArray.length >= 2) {
            String addresskey = addressArray[1]; // 取第二个元素 作为关键字
            List<Subarea> list = subareaDAO.findByNamedQuery("Subarea.findByAddresskey",addresskey);

            // 只匹配到唯一的一个分区,而且这个分区已经关联到定区
            if (list.size() == 1 && list.get(0).getDecidedZone() != null) {
                // 自动分单成功
                // 查到 (自动分单成功)
                DecidedZone decidedZone = list.get(0).getDecidedZone();

                // 通知单
                noticeBill.setStaff(decidedZone.getStaff());
                noticeBill.setOrdertype("自动");

                // 工单信息
                WorkBill workBill = new WorkBill();
                workBill.setNoticeBill(noticeBill);
                workBill.setStaff(decidedZone.getStaff());
                workBill.setType("新");
                workBill.setPickstate("新单");
                workBill.setBuildtime(new Timestamp(System.currentTimeMillis()));
                workBill.setAttachbilltimes(0);
                workBill.setRemark(noticeBill.getRemark());
                workBillDAO.save(workBill);
            } else {
                // 人工调度
                noticeBill.setOrdertype("人工");
            }
        } else {
            // 人工调度
            noticeBill.setOrdertype("人工");

        }
    } else {
        // 查到 (自动分单成功)
        DecidedZone decidedZone = decidedZoneDAO.findById(decidedZoneId);

        // 通知单
        noticeBill.setStaff(decidedZone.getStaff());
        noticeBill.setOrdertype("自动");

        // 工单信息
        WorkBill workBill = new WorkBill();
        workBill.setNoticeBill(noticeBill);
        workBill.setStaff(decidedZone.getStaff());
        workBill.setType("新");
        workBill.setPickstate("新单");
        workBill.setBuildtime(new Timestamp(System.currentTimeMillis()));
        workBill.setAttachbilltimes(0);
        workBill.setRemark(noticeBill.getRemark());
        workBillDAO.save(workBill);
    }
}

3.2.4. 配置添加通知单Action

<!-- 业务通知单受理 -->
<action name="noticebill_*" class="noticeBillAction" method="{1}">
    <result name="saveSUCCESS">
        /WEB-INF/pages/qupai/noticebill_add.jsp
    </result>
</action

>

4. 业务受理 未实现的功能

工单操作

宅急送 项目第七天 取派业务模块

查询出系统的所有工单 qp_workbill

追单 : 客户已经提交业务申请,取派员很多都没有去客户家中取件,再次进行追单操作, 系统只需要在 workbill表中 attachbilltimes 追单次数+1 (实际业务中,需要通过短信平台,催促取派员 )

销单 : 在取派员去取件的过程中,客户选择进行销单,不进行快递业务 ,在workbill 表type 设置 “销”, pickstack 设置为 “已取消”

查台转单
业务通知单 自动分单 成功 ,但是分错人了, 需要为通知单 重新手动更换 取派员 !

人工调度
业务通知单 自动分单失败,进入人工调度,需要手动为通知单指定取派员,生成工单!

结果: 已经将客户的货物,取回了物流公司!!!

5. 工作单管理

工作单基础信息的录入是信息的入口,并且也是本系统中人机交互最多的环节;基础信息录入的及时、准确、完整会直接影响后续的实物的运转配载、小件员交款、财务收入确认、成本垫付分摊,所以对数据的准确性、及时性、完整性要求比较高。

5.1. 工作单快速录入

提供操作人员在货量大单子多时简洁快速录入工作单的途径,快速录入中的信息主要是为了满足配载而设置的功能界面。
当业务高峰期,业务人员,只需要录入工作单基本信息(为了满足物流),就可以开始物流配送, 在业务闲暇时,再手动完善工作单信息。

宅急送 项目第七天 取派业务模块

5.1.1. easy ui 提供 datagrid的 行编辑效果

使用 datagrid 的列属性

宅急送 项目第七天 取派业务模块

步骤一: 在可编辑 列信息上,添加editor 属性

宅急送 项目第七天 取派业务模块

步骤二: 如果想编辑数据,开启某行数据编辑的状态

宅急送 项目第七天 取派业务模块

开启编辑

$('#grid').datagrid('beginEdit',1);// 索引1 代表第二行 

结束编辑

$('#grid').datagrid('endEdit',1);// 索引1 代表第二行

取消编辑 (将该行数据,还原到编辑之前的效果 )

$('#grid').datagrid('cancelEdit',1);// 索引1 代表第二行

步骤三: 如何向表格插入新的一行
Easy ui 提供 appendRow 和 insertRow 用于表格数据插入
appendRow 在表格末尾追加一行
insertRow 在指定位置插入一行

在表格第一行,插入一个空白行 :

$('#grid').datagrid('insertRow',{
    index : 0 , // 在第一行进行插入
    row : {}
});

步骤四: 在编辑结束后 或者 取消编辑后,执行事件函数

宅急送 项目第七天 取派业务模块

在行编辑后,onAfterEditor 事件函数就会执行,在取消编辑后 onCancelEdit 会执行

onAfterEdit : function(rowIndex, rowData, changes){
    alert('编辑完成');
}

5.1.2. 工作单快速录入页面

/WEB-INF/pages/qupai/quickworkorder.jsp 
// 点击新增一行
function doAdd(){
    // 判断当前是否正在编辑 
    if(editIndex != undefined){
        $("#grid").datagrid('endEdit',editIndex); // 结束当前行编辑
        // 触发onAfterEdit 函数
    }
    // 判断当前已经没有编辑行
    if(editIndex==undefined){ 
        // 在数据表格第一行 ,插入一个空行
        $("#grid").datagrid('insertRow',{
            index : 0,
            row : {}
        });
        // 打开第一行编辑状态
        $("#grid").datagrid('beginEdit',0);
        // 将编辑的行号,保存成员变量
        editIndex = 0;
    }
}

datagrid 行编辑,如果该行数据项不符合form的校验,无法执行 endEdit方法

在datagrid的每行结束编辑时,触发doAfterEdit 事件,在事件中提交ajax请求,提交结束编辑行数据给服务器端,完成数据保存

onAfterEdit : function(rowIndex, rowData, changes){
editIndex = undefined; // 将当前正在编辑行 设置undefined
// 提交ajax请求,将编辑行数据,以ajax方式,发送到服务器,完成保 
$.post("${pageContext.request.contextPath}/workordermanage_saveOrUpdate.action",rowData , function(data){
                });
}

编写服务器代码

public class WorkOrderManageAction extends BaseAction implements ModelDriven<WorkOrderManage> {
}
public interface WorkOrderManageService {
}
public class WorkOrderManageServiceImpl extends BaseService implements WorkOrderManageService {
}

将DAO 注入 BaseService
将Service 注入BaseAction

Action 处理ajax返回结果

宅急送 项目第七天 取派业务模块

配置struts.xml 拆分

<include file="struts-user.xml"></include>
<include file="struts-bc.xml"></include> 
<include file="struts-qp.xml"></include>

在 struts-qp.xml 配置工作单

<!-- 工作单管理 -->
<action name="workordermanage_*" class="workOrderManageAction" method="{1}">
    <!-- 保存 -->
    <result name="saveOrUpdateSUCCESS" type="json">
        <param name="root">map</param>
    </result>
</action>

5.2. 工作单快速录入 分页列表查询

为datagrid 添加url

url :  "${pageContext.request.contextPath}/workordermanage_pageQuery.action",

在服务器 WorkOrderManageAction 添加 pageQuery 方法

<!-- 分页查询 -->
<result name="pageQuerySUCCESS" type="json">
    <param name="root">pageResponseBean</param>
    <param name="includeProperties">
        total,
        rows\[\d+\]\.id,
        rows\[\d+\]\.arrivecity,
        rows\[\d+\]\.product,
        rows\[\d+\]\.num,
        rows\[\d+\]\.weight,
        rows\[\d+\]\.floadreqr
    </param>
</result>

5.3. 工作单搜索功能

对工作单使用 like模糊查询时,实际上 数据库内部索引无法使用 ,需要逐条比较查询内容,效率比较低
在数据量很多情况下, 提供模糊查询性能,使用lucene全文索引库技术
—- 最适合 场景 : 论坛和贴吧

Lucene检索原理, 会针对目标内容,先进行分词建立全文索引 ,在用户查找时,先查询索引库中词条,根据词条找到数据记录id ,再根据id 查找数据库记录 !

5.3.1. 在添加工作单,为工作单数据建立索引

直接引入框架 Hibernate Search (用来整合 Hibernate + Lucene)

HibernateSearch是在apacheLucene的基础上建立的主要用于Hibernate的持久化模型的全文检索工具。

1、 在项目导入jar包
导入 hibernate 的jar 、 导入 lucene的jar 、导入hibernate search的jar

宅急送 项目第七天 取派业务模块

dist 存放jar包
docs 文档

project 项目源码
hibernate-search-3.4.2.Final.jar : hibernate Search 核心jar包
hibernate-core-3.6.10.Final.jar : hibernate 核心jar包
lucene-core-3.1.0.jar : lucene核心jar包

使用maven坐标 导入 hibernate search

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-search</artifactId>
    <version>3.4.2.Final</version>
</dependency>

2、 导入 IK 分词器
将 IKAnalyzer2012_u6.jar 导入项目
将 IKAnalyzer.cfg.xml、stopword.dic 两个文件 复制 classpath

将jar复制到 项目src/main/webapp/WEB-INF/lib 下

<!-- 导入 IK分词器 -->
<dependency>
    <groupId>ik</groupId>
    <artifactId>ik</artifactId>
    <version>1.0</version>      <systemPath>D:\work\sh_javaee20130731\mavenbos\src\main\webapp\WEB-INF\lib\IKAnalyzer2012_u6.jar</systemPath>
    <scope>system</scope>
</dependency>

3、 配置hibernate search
在applicationContext-common.xml 中配置 索引库位置

<prop key="hibernate.search.default.indexBase">d:/index</prop>

在实体类上面使用注解配置对哪些数据建立索引(只支持注解)

宅急送 项目第七天 取派业务模块

自动完成索引库 创建、 更新、删除 !!!!

使用 Luke 工具,查询索引文件内容 !!

宅急送 项目第七天 取派业务模块

5.3.2. 结合lucene索引库完成模糊查找功能

使用 EasyUI 提供 searchbox 完成搜索框制作

<!-- prompt 默认提示内容 menu 搜索条件下拉选项 searcher 点击搜索按钮执行js函数名称 -->
<input id="ss" class="easyui-searchbox" style="width:300px" data-options="prompt:'请输入您的查询内容',menu:'#mm',searcher:doSearch"/>

<div id="mm">
    <div data-options="name:'arrivecity'">按照到达地搜索</div>
    <div data-options="name:'product'">按照货物名称搜索</div>
</div>

// 搜索函数
function doSearch(value,name){
alert("搜索项:"+name + ", 搜索内容:" + value);
}

将搜索内容,提交给服务器

// 将查询条件 缓存到 datagrid
$('#grid').datagrid('load',{
    conditionName : name,
    conditionValue : value
});

修改 WorkOrderManageAction的 pageQuery 方法,结合条件查询

在GenericDAO 添加查询lucene索引库的方法

/** * 结合lucene索引库 进行分页查询 * * @param conditionName * @param conditionValue * @param page * @param rows * @return */
public PageResponseBean queryByLucene(String conditionName, String conditionValue, int page, int rows);

宅急送 项目第七天 取派业务模块

其它

课前资料

宅急送 项目第七天 取派业务模块

hibernate search使用

宅急送 项目第七天 取派业务模块

课后资料

宅急送 项目第七天 取派业务模块

课程视频内容

宅急送 项目第七天 取派业务模块

宅急送 项目第七天 取派业务模块