新BOS2.0物流业务逻辑

BOS2.0业务逻辑

-第二阶段
13.✔★angularJS点击按钮,校验手机,发送验证码
一.页面导入angular.js在div应用模块和控制器
二.对页面获取验证码按钮,添加click事件
三.编写控制器,事件方法代码

<script type="text/javascript">
            // 模块定义 
            var signupApp = angular.module("signupApp", []);
            // 控制器定义
            signupApp.controller("signupCtrl", ["$scope","$http",function($scope,$http) {
                $scope.btnMsg = "发送验证码";
                var active = true;
                var second = 60; // 倒计时60秒
                var secondInterval;

                $scope.getCheckCode = function(telephone) {
                    if(active == false) {
                        return;
                    }
                    // 1 发送一个HTTP请求,通知服务器 发送短信给目标用户 
                    var regex = /^1(3|5|7|8)\d{9}$/;
                    if(regex.test(telephone)) {
                        // 校验通过
                        $http({
                            method: 'GET',
                            url: 'customer_sendSms.action',
                            params : {
                                telephone : telephone
                            }
                        }).error(function(data, status, headers, config) {
                            // 当响应以错误状态返回时调用
                            alert("发送短信出错,请联系管理员");
                        });
                    } else {
                        // 校验失败 
                        alert("手机号非法,请重新输入 ");
                        return;
                    }

                    // 2 显示倒计时按钮,60秒后,允许重新发送 
                    active = false;
                    secondInterval = setInterval(function() {
                        if(second < 0) {
                            // 倒计时结束,允许重新发送 
                            $scope.btnMsg = "重发验证码";
                            // 强制更新视图
                            $scope.$digest();
                            active = true;
                            second = 60;
                            // 关闭定时器
                            clearInterval(secondInterval);
                            secondInterval = undefined;
                        } else {
                            // 继续计时
                            $scope.btnMsg = second + "秒后重发";
                            // 强制更新视图
                            $scope.$digest();
                            second--;
                        }
                    }, 1000);
                }
            }]);
        </script>

14.发送短信验证码
一.查看接口服务
二.整合案例代码,编写SmsUtils工具类

ublic class SmsUtils {
    private static String userid = "写你申请的用户名";
    private static String pass = "密码";

    /**
     * 调用HTTP 协议方式发送短信
     * 
     * @param mobile
     * @param content
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String sendSmsByHTTP(String mobile, String content)
            throws UnsupportedEncodingException {
        HttpURLConnection httpconn = null;
        String result = "Error";
        StringBuilder sb = new StringBuilder();
        sb.append("http://service.winic.org:8009/sys_port/gateway/index.asp?");

        // 以下是参数
        sb.append("id=").append(URLEncoder.encode(userid, "gb2312"));
        sb.append("&pwd=").append(pass);
        sb.append("&to=").append(mobile);
        sb.append("&content=").append(URLEncoder.encode(content, "gb2312"));
        sb.append("&time=").append("");
        try {
            URL url = new URL(sb.toString());
            httpconn = (HttpURLConnection) url.openConnection();
            BufferedReader rd = new BufferedReader(new InputStreamReader(
                    httpconn.getInputStream()));
            result = rd.readLine();
            rd.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (httpconn != null) {
                httpconn.disconnect();
                httpconn = null;
            }
        }
        return result;
    }

    /**
     * 调用 WebService 协议方式发送短信
     * 
     * @param mobiles
     * @param msg
     * @return
     */
    public static String sendSmsByWebService(String mobiles, String msg) {
        String result = "-12";
        try {
            Document doc;
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            InputStream is = getSoapInputStream(userid, pass, mobiles, msg, "");
            if (is != null) {
                doc = db.parse(is);
                NodeList nl = doc.getElementsByTagName("SendMessagesResult");
                Node n = nl.item(0);
                result = n.getFirstChild().getNodeValue();
                is.close();
            }
            return result;
        } catch (Exception e) {
            System.out.print("SmsSoap.sendSms error:" + e.getMessage());
            return "-12";
        }
    }

    private static String getSoapSmssend(String userid, String pass,
            String mobiles, String msg, String time) {
        try {
            String soap = "";
            soap = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                    + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                    + "<soap:Body>"
                    + "<SendMessages xmlns=\"http://tempuri.org/\">" + "<uid>"
                    + userid + "</uid>" + "<pwd>" + pass + "</pwd>" + "<tos>"
                    + mobiles + "</tos>" + "<msg>" + msg + "</msg>" + "<otime>"
                    + time + "</otime>" + "</SendMessages>" + "</soap:Body>"
                    + "</soap:Envelope>";
            return soap;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private static InputStream getSoapInputStream(String userid, String pass,
            String mobiles, String msg, String time) throws Exception {
        URLConnection conn = null;
        InputStream is = null;
        try {
            String soap = getSoapSmssend(userid, pass, mobiles, msg, time);
            if (soap == null) {
                return null;
            }
            try {

                URL url = new URL("http://service2.winic.org:8003/Service.asmx");

                conn = url.openConnection();
                conn.setUseCaches(false);
                conn.setDoInput(true);
                conn.setDoOutput(true);
                conn.setRequestProperty("Content-Length",
                        Integer.toString(soap.length()));
                conn.setRequestProperty("Content-Type",
                        "text/xml; charset=utf-8");
                conn.setRequestProperty("HOST", "service2.winic.org");
                conn.setRequestProperty("SOAPAction",
                        "\"http://tempuri.org/SendMessages\"");

                OutputStream os = conn.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");
                osw.write(soap);
                osw.flush();
            } catch (Exception ex) {
                System.out.print("SmsSoap.openUrl error:" + ex.getMessage());
            }
            try {
                is = conn.getInputStream();
            } catch (Exception ex1) {
                System.out.print("SmsSoap.getUrl error:" + ex1.getMessage());
            }

            return is;
        } catch (Exception e) {
            System.out.print("SmsSoap.InputStream error:" + e.getMessage());
            return null;
        }
    }

    public static void main(String[] args) throws IOException {
        String randomCode = RandomStringUtils.randomNumeric(4);
        // System.out.println(sendSmsByHTTP("xxx", randomCode));
        // System.out.println(sendSmsByHTTP("xxx", "尊敬的用户您好,本次获取的验证码为:"
        // + randomCode + ",服务电话:4006184000"));
        System.out.println(sendSmsByWebService("xxx", "尊敬的用户您好,本次获取的验证码为:"
                + randomCode + ",服务电话:4006184000"));
    }
}

15.✔★注册用户,并发送激活邮件


@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class CustomerAction extends BaseAction<Customer> {
    @Autowired
    @Qualifier("jmsQueueTemplate")
    private JmsTemplate jmsTemplate;

    @Action(value = "customer_sendSms")
    public String sendSms() throws IOException {
        // 手机号保存在Customer对象
        // 生成短信验证码
        String randomCode = RandomStringUtils.randomNumeric(4);
        // 将短信验证码 保存到session
        ServletActionContext.getRequest().getSession()
                .setAttribute(model.getTelephone(), randomCode);

        System.out.println("生成手机验证码为:" + randomCode);
        // 编辑短信内容
        final String msg = "尊敬的用户您好,本次获取的验证码为:" + randomCode
                + ",服务电话:4006184000";

        // 调用MQ服务,发送一条消息
        jmsTemplate.send("bos_sms", new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                MapMessage mapMessage = session.createMapMessage();
                mapMessage.setString("telephone", model.getTelephone());
                mapMessage.setString("msg", msg);
                return mapMessage;
            }
        });
        return NONE;

    }

    // 属性驱动
    private String checkcode;

    public void setCheckcode(String checkcode) {
        this.checkcode = checkcode;
    }

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Action(value = "customer_regist", results = {
            @Result(name = "success", type = "redirect", location = "signup-success.html"),
            @Result(name = "input", type = "redirect", location = "signup.html") })
    public String regist() {
        // 先校验短信验证码,如果不通过,调回注册页面
        // 从session获取 之前生成验证码
        String checkcodeSession = (String) ServletActionContext.getRequest()
                .getSession().getAttribute(model.getTelephone());
        if (checkcodeSession == null || !checkcodeSession.equals(checkcode)) {
            System.out.println("短信验证码错误...");
            // 短信验证码错误
            return INPUT;
        }
        // 调用webService 连接CRM 保存客户信息
        WebClient
                .create(Constants.CRM_MANAGEMENT_URL + "/services"
                        + "/customerService/customer")
                .type(MediaType.APPLICATION_JSON).post(model);
        System.out.println("客户注册成功...");

        // 发送一封激活邮件
        // 生成激活码
        String activecode = RandomStringUtils.randomNumeric(32);

        // 将激活码保存到redis,设置24小时失效
        redisTemplate.opsForValue().set(model.getTelephone(), activecode, 24,
                TimeUnit.HOURS);

        // 调用MailUtils发送激活邮件
        String content = "尊敬的客户您好,请于24小时内,进行邮箱账户的绑定,点击下面地址完成绑定:<br/><a href='"
                + MailUtils.activeUrl + "?telephone=" + model.getTelephone()
                + "&activecode=" + activecode + "'>速运快递邮箱绑定地址</a>";
        MailUtils.sendMail("速运快递激活邮件", content, model.getEmail());

        return SUCCESS;
    }

    // 属性驱动
    private String activecode;

    public void setActivecode(String activecode) {
        this.activecode = activecode;
    }

16.✔★点击邮件中的激活链接,绑定邮箱
一.用户注册时,输入邮件
二.Javamail技术,向用户邮箱发送一封激活邮件,含有激活码
1.CustomerAction提供发送含激活码的邮件的方法,激活码保存在Redis中
三.用户在24小时内,可以点击激活邮件链接,绑定邮箱
1.CustomerAction提供activeMail方法,接收客户手机号和激活码
2.先判断激活码是否有效,如果激活码无效,提示用户
3.如果激活码有效,判断是否在重复绑定(T_CUSTOMER表type字段为1,绑定)
4.如果用户没有绑定过邮箱,完成绑定

    @Action("customer_activeMail")
    public String activeMail() throws IOException {
        ServletActionContext.getResponse().setContentType(
                "text/html;charset=utf-8");
        // 判断激活码是否有效
        String activecodeRedis = redisTemplate.opsForValue().get(
                model.getTelephone());
        if (activecodeRedis == null || !activecodeRedis.equals(activecodeRedis)) {
            // 激活码无效
            ServletActionContext.getResponse().getWriter()
                    .println("激活码无效,请登录系统,重新绑定邮箱!");
        } else {
            // 激活码有效
            // 防止重复绑定
            // 调用CRM webService 查询客户信息,判断是否已经绑定
            Customer customer = WebClient
                    .create(Constants.CRM_MANAGEMENT_URL + "/services"
                            + "/customerService/customer/telephone/"
                            + model.getTelephone())
                    .accept(MediaType.APPLICATION_JSON).get(Customer.class);
            if (customer.getType() == null || customer.getType() != 1) {
                // 没有绑定,进行绑定
                WebClient.create(
                        Constants.CRM_MANAGEMENT_URL + "/services"
                                + "/customerService/customer/updatetype/"
                                + model.getTelephone()).get();
                ServletActionContext.getResponse().getWriter()
                        .println("邮箱绑定成功!");
            } else {
                // 已经绑定过
                ServletActionContext.getResponse().getWriter()
                        .println("邮箱已经绑定过,无需重复绑定!");
            }

            // 删除redis的激活码
            redisTemplate.delete(model.getTelephone());
        }
        return NONE;
    }

用户登录功能

    @Action(value = "customer_login", results = {
            @Result(name = "login", location = "login.html", type = "redirect"),
            @Result(name = "success", location = "index.html#/myhome", type = "redirect") })
    public String login() {
        Customer customer = WebClient
                .create(Constants.CRM_MANAGEMENT_URL
                        + "/services/customerService/customer/login?telephone="
                        + model.getTelephone() + "&password="
                        + model.getPassword())
                .accept(MediaType.APPLICATION_JSON).get(Customer.class);
        if (customer == null) {
            // 登录失败
            return LOGIN;
        } else {
            // 登录成功
            ServletActionContext.getRequest().getSession()
                    .setAttribute("customer", customer);
            return SUCCESS;
        }
    }

}

17.后端系统 宣传活动的添加
一.页面form表单,添加Action

        <script type="text/javascript">
            $(function(){
                // 先将body隐藏,再显示,不会出现页面刷新效果
                $("body").css({visibility:"visible"});

                // 收派标准信息表格
                $('#grid').datagrid( {
                    iconCls : 'icon-forward',
                    fit : true,
                    border : false,
                    rownumbers : true,
                    striped : true,
                    pageList: [30,50,100],
                    pagination : true,
                    toolbar : toolbar,
                    url : "../../standard_pageQuery.action",
                    idField : 'id',
                    columns : columns
                });


二.点击保存按钮,提交表单

            // 对收派标准 save按钮,添加click事件
                $("#save").click(function(){
                    // 判断是否form中所有表单对象 都是通过校验 
                    if($("#standardForm").form('validate')){
                        // 通过校验 
                        $("#standardForm").submit();
                    }else{
                        $.messager.alert("警告","表单中还存在需要一些非法内容","warning");                     
                    }
                });
            }); 

            //工具栏
            var toolbar = [ {
                id : 'button-add',
                text : '增加',
                iconCls : 'icon-add',
                handler : function(){
                    $("#standardWindow").window('open');
                }
            }, {
                id : 'button-edit',
                text : '修改',
                iconCls : 'icon-edit',
                handler : function(){
                    // 获取当前datagrid选中数据 
                    var rows = $("#grid").datagrid('getSelections');
                    if(rows.length != 1){
                        // 没选 或 多选 
                        $.messager.alert("提示信息","修改数据时,只能选中一行","warning");
                    }else{
                        // 只选中一行 
                        var row = rows[0]; 
                        // 进行表单回显操作 
                        $("#standardForm").form('load',row);
                        // 显示窗口
                        $("#standardWindow").window('open');
                    }
                }
            } ];

三.编写PromotionAction,提供save方法

@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class PromotionAction extends BaseAction<Promotion> {

    private File titleImgFile;
    private String titleImgFileFileName;

    public void setTitleImgFileFileName(String titleImgFileFileName) {
        this.titleImgFileFileName = titleImgFileFileName;
    }

    public void setTitleImgFile(File titleImgFile) {
        this.titleImgFile = titleImgFile;
    }

    @Autowired
    private PromotionService promotionService;

    @Action(value = "promotion_save", results = { @Result(name = "success", type = "redirect", location = "./pages/take_delivery/promotion.html") })
    public String save() throws IOException {
        // 宣传图 上传、在数据表保存 宣传图路径
        String savePath = ServletActionContext.getServletContext().getRealPath(
                "/upload/");
        String saveUrl = ServletActionContext.getRequest().getContextPath()
                + "/upload/";

        // 生成随机图片名
        UUID uuid = UUID.randomUUID();
        String ext = titleImgFileFileName.substring(titleImgFileFileName
                .lastIndexOf("."));
        String randomFileName = uuid + ext;

        // 保存图片 (绝对路径)
        File destFile = new File(savePath + "/" + randomFileName);
        System.out.println(destFile.getAbsolutePath());
        FileUtils.copyFile(titleImgFile, destFile);

        // 将保存路径 相对工程web访问路径,保存model中
        model.setTitleImg(ServletActionContext.getRequest().getContextPath()
                + "/upload/" + randomFileName);

        // 调用业务层,完成活动任务数据保存
        promotionService.save(model);

        return SUCCESS;
    }

18.后端系统 宣传活动的分页展示
一.在datagrid中添加url

<div class="easyui-window" title="对收派标准进行添加或者修改" id="standardWindow" collapsible="false" minimizable="false" maximizable="false" modal="true" closed="true" style="width:600px;top:50px;left:200px">
            <div region="north" style="height:31px;overflow:hidden;" split="false" border="false">
                <div class="datagrid-toolbar">
                    <a id="save" icon="icon-save" href="#" class="easyui-linkbutton" plain="true">保存</a>
                </div>
            </div>

            <div region="center" style="overflow:auto;padding:5px;" border="false">

                <form id="standardForm" 
                    action="../../standard_save.action" method="post">
                    <table class="table-edit" width="80%" align="center">
                        <tr class="title">
                            <td colspan="2">收派标准信息
                                <!--提供隐藏域 装载id -->
                                <input type="hidden" name="id" />
                            </td>
                        </tr>
                        <tr>
                            <td>收派标准名称</td>
                            <td>
                                <input type="text" name="name" 
                                    class="easyui-validatebox" data-options="required:true" />
                            </td>
                        </tr>
                        <tr>
                            <td>最小重量</td>
                            <td>
                                <input type="text" name="minWeight" 
                                        class="easyui-numberbox" required="true" />
                            </td>
                        </tr>
                        <tr>
                            <td>最大重量</td>
                            <td>
                                <input type="text" name="maxWeight" class="easyui-numberbox" required="true" />
                            </td>
                        </tr>
                        <tr>
                            <td>最小长度</td>
                            <td>
                                <input type="text" name="minLength" class="easyui-numberbox" required="true" />
                            </td>
                        </tr>
                        <tr>
                            <td>最大长度</td>
                            <td>
                                <input type="text" name="maxLength" class="easyui-numberbox" required="true" />
                            </td>
                        </tr>
                    </table>
                </form>
            </div>
        </div>

二.在PromotionAction中添加pageQuery方法

@Action(value = "promotion_pageQuery", results = { @Result(name = "success", type = "json") })
    public String pageQuery() {
        // 构造分页查询参数
        Pageable pageable = new PageRequest(page - 1, rows);
        // 调用业务层 完成查询
        Page<Promotion> pageData = promotionService.findPageData(pageable);
        // 压入值栈
        pushPageDataToValueStack(pageData);

        return SUCCESS;
    }

19.前端系统 宣传活动分页
一.使用AngularJs动态加载数据,显示动态数据表格

扫描二维码关注公众号,回复: 2773271 查看本文章
<div class="col-sm-6 col-md-3" ng-repeat="item in pageItems">
                            <div class="thumbnail">
                                <img ng-src="{{item.titleImg}}" alt="活动一">
                                <div class="caption">
                                    <p><a href="#/promotion_detail/{{item.id}}">{{item.title}}</a></p>
                                    <p class="text-right status">
                                        <span ng-show="item.status == '1'">进行中</span>
                                        <span ng-show="item.status == '2'">已结束</span>
                                    </p>
                                    <p class="text-right grey">{{item.startDate}}{{item.endDate}}</p>
                                    <p class="text-right grey">{{item.activeScope}}</p>
                                </div>
                            </div>
                        </div>
                    </div>
                </tbody>
            </table>

<script src="js/self/page.js"></script>

二.基于Angular实现分页工具条功能

            <!--  分页按钮 -->
            <div>
                <ul class="pagination pull-right">
                    <li>
                        <a href ng-click="prev()">上一页</a>
                    </li>
                    <li ng-repeat="page in pageList" ng-class="{active: isActivePage(page)}">
                        <a ng-click="selectPage(page)">{{ page }}</a>
                    </li>
                    <li>
                        <a href ng-click="next()">下一页</a>
                    </li>
                </ul>
            </div>
        </div>
    </section>
</div>

三.修改promotion.html页面分页列表page.js中请求路径

bosfore_app.controller("ctrlRead", ['$scope', '$http', function($scope, $http) {
    $scope.currentPage = 1;
    $scope.pageSize = 4;
    $scope.totalCount = 0;
    $scope.totalPages = 0;

    $scope.prev = function() {
        if($scope.currentPage > 1) {
            $scope.selectPage($scope.currentPage - 1);
        }
    }

    $scope.next = function() {
        if($scope.currentPage < $scope.totalPages) {
            $scope.selectPage($scope.currentPage + 1);
        }
    }

    $scope.selectPage = function(page) {
        // 如果页码超出范围
        if($scope.totalPages != 0) {
            if(page < 1 || page > $scope.totalPages) return;
        }

        $http({
            method: 'GET',
            url: 'promotion_pageQuery.action',
            params: {
                "page": page, // 当前页码 
                "rows": $scope.pageSize // 每页记录数
            }
        }).success(function(data, status, headers, config) {
            // 显示表格数据 
            $scope.pageItems = data.pageData;
            // 计算总页数
            $scope.totalCount = data.totalCount;
            $scope.totalPages = Math.ceil($scope.totalCount / $scope.pageSize);

            // 当前显示页,设为当前页
            $scope.currentPage = page;

            // 固定显示10页 (前5后4)
            var begin;
            var end;

            begin = page - 5;
            if(begin < 0) {
                begin = 1;
            }

            end = begin + 9;
            if(end > $scope.totalPages) {
                end = $scope.totalPages;
            }

            begin = end - 9;
            if(begin < 1) {
                begin = 1;
            }

            $scope.pageList = new Array();
            for(var i = begin; i <= end; i++) {
                $scope.pageList.push(i);
            }

        }).error(function(data, status, headers, config) {
            // 当响应以错误状态返回时调用
            alert("出错,请联系管理员 ");
        });
    }

    $scope.isActivePage = function(page) {
        return page == $scope.currentPage;
    }

    // 发起请求 显示第一页数据 
    $scope.selectPage($scope.currentPage);

}]);

四.在bos_fore项目新建PromotionAction提供pageQuery方法

    @Action(value = "promotion_pageQuery", results = { @Result(name = "success", type = "json") })
    public String pageQuery() {
        System.out.println("11110000000000000000000000000000000000000000000");
        // 基于WebService 获取 bos_management的 活动列表 数据信息
        PageBean<Promotion> pageBean = WebClient
                .create(Constants.BOS_MANAGEMENT_URL+"/services/promotionService/pageQuery?page="
                        + page + "&rows=" + rows)
                .accept(MediaType.APPLICATION_JSON).get(PageBean.class);

        ActionContext.getContext().getValueStack().push(pageBean);

        return SUCCESS;
    }

五.在bos_management项目提供webService服务,根据page和rows返回分页数据

public interface PromotionService {

    // 保存宣传任务
    void save(Promotion promotion);

    // 分页查询
    Page<Promotion> findPageData(Pageable pageable);

    // 根据page和rows 返回分页数据
    @Path("/pageQuery")
    @GET
    @Produces({ "application/xml", "application/json" })
    PageBean<Promotion> findPageData(@QueryParam("page") int page,
            @QueryParam("rows") int rows);

    // 根据id 查询
    @Path("/promotion/{id}")
    @GET
    @Produces({ "application/xml", "application/json" })
    Promotion findById(@PathParam("id") Integer id);

    // 设置活动过期
    void updateStatus(Date date);
}

六.在bos_domain中创建PageBean对象

// 自定义 分页数据封装对象 
@XmlRootElement(name = "pageBean")
@XmlSeeAlso({ Promotion.class })
public class PageBean<T> {

    private long totalCount; // 总记录数
    private List<T> pageData; // 当前页数据

    public long getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(long totalCount) {
        this.totalCount = totalCount;
    }

    public List<T> getPageData() {
        return pageData;
    }

    public void setPageData(List<T> pageData) {
        this.pageData = pageData;
    }

}

七.在Promotion加入@XmlRootElement(name=”Promotion”)

@Entity
@Table(name = "T_PROMOTION")
@XmlRootElement(name = "promotion")
public class Promotion implements Serializable {

八.在PromotionService提供WebService服务接口方法

九.在bos_management配置WebService配置文件(web.xml applicationContext-webService.xml)

<!-- spring配置文件位置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <!-- spring核心监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>CXFService</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFService</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>

    <!--shiro的Filter  -->

    <filter>
        <!--去spring配置文件中寻找同名的本  -->
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- struts2 过滤器 -->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.action</url-pattern>
    </filter-mapping>


    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
</web-app>

applicationContext-webService.xml

<jaxrs:server id="promotionService" address="/promotionService">
        <jaxrs:serviceBeans>
            <bean class="cn.itcast.bos.service.take_delivery.impl.PromotionServiceImpl" />
        </jaxrs:serviceBeans>
        <jaxrs:inInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
        </jaxrs:inInterceptors>
        <jaxrs:outInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
        </jaxrs:outInterceptors>
    </jaxrs:server>

20.前端系统 宣传活动详情页面静态化
一.安装插件(将freemarker_eclipseplugin复制在eclipse的dropins文件夹,再重启)
二.编辑freemarker的模版文件(放在WEB-INF)

<html>
    <title>
        ${title}
    </title>
    <body>
        ${msg}
    </body>
</html>

三.修改promotion.html的href(点击不同链接,拼接上不同id)”#/promotion_detail/{{item.id}}”

四.修改index.html(添加angularjS路由)
五.在PromotionAction提供showDetail方法
//先判断id对应html是否存在,如果存在,直接返回
String htmlRealPath=ServletActionContext.getServletContext().getRealPath(“/freemarker”);
File htmlFile =new File(htmlRealPath+”/”+model.getID()+”.html”);

@Action(value = "promotion_showDetail")
    public String showDetail() throws Exception {
        // 先判断 id 对应html 是否存在,如果存在 直接返回
        String htmlRealPath = ServletActionContext.getServletContext()
                .getRealPath("/freemarker");
        File htmlFile = new File(htmlRealPath + "/" + model.getId() + ".html");

        // 如果html文件不存在 ,查询数据库,结合freemarker模板生成 页面
        if (!htmlFile.exists()) {
            Configuration configuration = new Configuration(
                    Configuration.VERSION_2_3_22);
            configuration.setDirectoryForTemplateLoading(new File(
                    ServletActionContext.getServletContext().getRealPath(
                            "/WEB-INF/freemarker_templates")));
            // 获取模板对象
            Template template = configuration
                    .getTemplate("promotion_detail.ftl");

            // 动态数据
            Promotion promotion = WebClient
                    .create(Constants.BOS_MANAGEMENT_URL
                            + "/services/promotionService/promotion/"
                            + model.getId()).accept(MediaType.APPLICATION_JSON)
                    .get(Promotion.class);
            Map<String, Object> parameterMap = new HashMap<String, Object>();
            parameterMap.put("promotion", promotion);

            // 合并输出
            template.process(parameterMap, new OutputStreamWriter(
                    new FileOutputStream(htmlFile), "utf-8"));

        }

        // 存在 ,直接将文件返回
        ServletActionContext.getResponse().setContentType(
                "text/html;charset=utf-8");
        FileUtils.copyFile(htmlFile, ServletActionContext.getResponse()
                .getOutputStream());

        return NONE;
    }

六.在bos_management修改PromotionService加入服务接口

21.后端系统 宣传活动自动过期
思路:修改Promotion数据表的status字段,在bos_management中使用,在bos_management集成quartz
一.在common_parent引入定时框架
二.在bos_management编写继承Job和继承JobFactory的类

public class PromotionJob implements Job {
    @Autowired
    private PromotionService promotionService;

    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        System.out.println("活动过期处理程序执行....");
        // 每分钟执行一次,当前时间 大于 promotion数据表 endDate, 活动已经过期,设置status='2'
        promotionService.updateStatus(new Date());
    }
@Service("jobFactory")
public class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle)
            throws Exception {
        Object jobInstance = super.createJobInstance(bundle);
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }

三.配置applicationContext-quartz.xml

<bean id="promotionJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="cn.itcast.bos.quartz.PromotionJob" />
    </bean>

    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="promotionJob" />
        <property name="startDelay" value="0" />
        <property name="repeatInterval" value="6000000" />
    </bean>

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="jobFactory" ref="jobFactory" />
        <property name="triggers">
            <list>
                <ref bean="simpleTrigger"/>
            </list>
        </property>
    </bean>

22.前端系统 用户登录
一.修改login.html中表单路径

    <section class="mainlogin"  ng-app="validationApp" ng-controller="mainController">
            <div class="container">
                <div class="col-md-4 col-md-offset-7 loginbox">
                    <h4>用户登录</h4>
                    <form class="form-horizontal" id="customerForm" 
                        action="customer_login.action" method="post">

                        <div class="form-group">
                            <label  class="col-sm-3 control-label">登录方式</label>
                            <div class="col-sm-7">
                                <label for="r1" class="radio-inline"><input type="radio" value="0" name="article" onclick="show()" checked/>密码登录</label>
                                <label for="r2" class="radio-inline"><input type="radio" value="1" name="article" onclick="show()"  />手机号登录</label>

                            </div>
                        </div>


                        <div id="format1">              
                        <div class="form-group" ng-class="{ 'has-error' : userForm.name.$invalid && !userForm.name.$pristine }">
                            <label for="inputaccount" class="col-sm-3 control-label">账号</label>
                            <div class="col-sm-8">
                            <input  type="text" name="telephone" class="form-control" 
                                ng-model="user.name" required placeholder="请输入账号">
                                <p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">用户名不可为空.</p>
                            </div>
                        </div>
                        <div class="form-group" ng-class="{ 'has-error' : userForm.username.$invalid && !userForm.username.$pristine }">
                            <label for="inputpassword" class="col-sm-3 control-label">密码</label>
                            <div class="col-sm-8">
                                <input type="password" 
                                       class="form-control" 
                                       name="password" 
                                       placeholder="请输入您的密码"
                                       ng-model="user.password" 
                           ng-minlength="3" 
                           ng-maxlength="8"
                           required

                                       >
                    <p ng-show="userForm.password.$error.minlength" class="help-block">用户名太短了!</p>
                    <p ng-show="userForm.password.$error.maxlength" class="help-block">用户名太长了!</p>

                            </div>
                        </div>
                        <div class="form-group" style="margin-bottom: 0;">
                            <label for="inputvalidate" class="col-sm-3 control-label">验证码</label>
                            <div class="col-sm-5">
                                <input type="password" class="form-control" id="inputaccount" placeholder="请输入验证码">
                            </div>
                            <div class="col-sm-3">
                                <img src="images/icon/yanz.png"> 
                            </div>
                        </div>
                        <div class="form-group">

                            <div class="col-sm-offset-4 col-sm-4">
                                <div class="checkbox">
                                    <input type="checkbox"><span class="size12"> 记住用户名</span>

                                </div>
                            </div>

                        </div>
                        </div>  



                        <div id="format2" style="display:none;">
                            <div class="form-group">
                            <label for="inputPassword3" class="col-sm-3 control-label">手机号</label>
                            <div class="col-sm-8">
                                <input type="text" class="form-control" id="inputaccount" placeholder="请输入手机号/邮箱/用户名">
                            </div>
                        </div>

                        <div class="form-group">
                            <label for="inputvalidate" class="col-sm-3 control-label">验证码</label>
                            <div class="col-sm-4">
                                <input type="password" class="form-control" id="inputaccount" placeholder="验证码">
                            </div>
                            <div class="col-sm-3">
                                <button class="btn btn-default">获取验证码</button>
                            </div>
                        </div>

                        </div>




                        <div class="col-md-offset-3 col-md-8">
                            <a href="javascript:$('#customerForm').submit();" class="btn btn-danger" >登录</a>

                        </div>
                        <p class="text-right clearfix">还不是我们的会员?<b><a href="signup.html">立即注册</a></b></p>

                    </form>

                </div>
            </div>

        </section>

二.编写bos_fore系统CustomerAction提供login方法

@Action(value = "customer_login", results = {
            @Result(name = "login", location = "login.html", type = "redirect"),
            @Result(name = "success", location = "index.html#/myhome", type = "redirect") })
    public String login() {
        Customer customer = WebClient
                .create(Constants.CRM_MANAGEMENT_URL
                        + "/services/customerService/customer/login?telephone="
                        + model.getTelephone() + "&password="
                        + model.getPassword())
                .accept(MediaType.APPLICATION_JSON).get(Customer.class);
        if (customer == null) {
            // 登录失败
            return LOGIN;
        } else {
            // 登录成功
            ServletActionContext.getRequest().getSession()
                    .setAttribute("customer", customer);
            return SUCCESS;
        }
    }

23.前端系统 用户下单
一.前端页面添加city pciker和百度地图API实现相应功能

<!--
    描述:用户下单页面
-->
<html>

    <head>
        <meta content="application/xhtml+xml;charset=UTF-8" http-equiv="Content-Type">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
        <title>速运快递</title>

        <link rel="stylesheet" type="text/css" href="css/styleindex.css">
        <link rel="stylesheet" type="text/css" href="css/public.css">
        <link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap.css">
        <link rel="stylesheet" type="text/css" href="css/nav.css">

        <script src="js/jquery.min.js" type="text/javascript"></script>
        <script src="js/bootstrap.min.js" type="text/javascript"></script>
        <script src="js/self/effect.js" type="text/javascript"></script>
        <script type="text/javascript" src="js/angular.min.js"></script>
        <script type="text/javascript" src="js/angular-route.min.js"></script>

        <!--[if IE]>
    <script src="js/html5.js"  type="text/javascript"></script>
    <script src="js/respond.min.js"  type="text/javascript"></script>
    <![endif]-->
    </head>
<link href="css/city-picker.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" type="text/css" href="css/styleorder.css">
<script type="text/javascript">
    // 百度地图API功能
    function G(id) {
        return document.getElementById(id);
    }

    var ac = new BMap.Autocomplete(    //建立一个自动完成的对象
        {"input" : "sendAddress"
    });

    ac.addEventListener("onhighlight", function(e) {  //鼠标放在下拉列表上的事件
    var str = "";
        var _value = e.fromitem.value;
        var value = "";
        if (e.fromitem.index > -1) {
            value = _value.province +  _value.city +  _value.district +  _value.street +  _value.business;
        }    
        str = "FromItem<br />index = " + e.fromitem.index + "<br />value = " + value;

        value = "";
        if (e.toitem.index > -1) {
            _value = e.toitem.value;
            value = _value.province +  _value.city +  _value.district +  _value.street +  _value.business;
        }    
        str += "<br />ToItem<br />index = " + e.toitem.index + "<br />value = " + value;
        G("searchResultPanel").innerHTML = str;
    });

    var myValue;
    ac.addEventListener("onconfirm", function(e) {    //鼠标点击下拉列表后的事件
    var _value = e.item.value;
        myValue = _value.province +  _value.city +  _value.district +  _value.street +  _value.business;
        G("searchResultPanel").innerHTML ="onconfirm<br />index = " + e.item.index + "<br />myValue = " + myValue;

        // 选中详细地址后,调用百度地图获取 云地理编码 
        var address = encodeURIComponent(myValue);
        $.getJSON(
            "http://api.map.baidu.com/cloudgc/v1?ak=zbLsuDDL4CS2U0M4KezOZZbGUY9iWtVf&address="+address+"&callback=?",
            function(data){
            if(data.status == 0 && data.result.length>0 ){
                $('#sendAreaInfo').citypicker('reset');
                $('#sendAreaInfo').citypicker('destroy');

                $('#sendAreaInfo').citypicker({
                    province: data.result[0].address_components.province ,
                    city: data.result[0].address_components.city ,
                    district: data.result[0].address_components.district ,
                });
            }
        });
    });

</script>
<div class="order" id="ng-app" ng-app="app">
    <!-- orderall -->
    <section>
        <div class="tab-title ">
            <ul class="nav nav-tabs container" role="tablist">
                <li role="presentation" class="active">
                    <a href="#home" aria-controls="home" role="tab" data-toggle="tab">单件下单</a>
                </li>
                <li role="presentation">
                    <a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">批量下单</a>
                </li>

            </ul>

            </ul>
        </div>
        <div class="tab-content container">
            <div class="jiangli">
                <div class="col-md-4 xiaojf">
                    <h5>消费积分奖励</h5>
                    <p class="grey">消费1元积1分,累计积分兑好礼</p>
                </div>
                <div class="col-md-4 tenjf">
                    <h5>消费积分奖励</h5>
                    <p class="grey">消费1元积1分,累计积分兑好礼</p>
                </div>
                <div class="col-md-4 qianjf">
                    <h5>消费积分奖励</h5>
                    <p class="grey">消费1元积1分,累计积分兑好礼</p>
                </div>
            </div>
            <div role="tabpanel" class="tab-pane fade in active" id="home">

                <ul class="nav nav-tabs">
                    <li role="presentation" class="active">
                        <a href="#one" aria-controls="home" role="tab" data-toggle="tab">国内</a>
                    </li>
                    <li role="presentation">
                        <a href="#two" aria-controls="profile" role="tab" data-toggle="tab">港澳台</a>
                    </li>
                </ul>
                <div class="tab-content">
                    <div role="tabpanel" class="tab-pane fade in active" id="one">
                        <form id="orderForm" class="form-horizontal" action="order_add.action" method="post">
                        <!--寄件人信息 -->
                        <div class="model">
                            <div class="areatitle">
                                <h4><span class="zhezhao">寄件人信息</span></h4>
                            </div>
                            <div class="col-md-5">
                                    <div class="orderinfo">
                                        <div class="form-group " id="sender-group">
                                            <label class="col-sm-3 control-label"><b>*</b>姓名:</label>

                                            <div class="col-sm-8 input-group" style="padding:0 15px;">
                                                <input name="sendName" type="text" class="form-control" placeholder="请输入发件人姓名" required="required">
                                                <div class="input-group-addon">
                                                    <button type="button" data-toggle="modal" data-target="#myModal" id="dao"><img src="images/icon/suyun/contact.png"></button>
                                                </div>

                                                <!-- Modal -->
                                                <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                                                    <div class="modal-dialog">
                                                        <div class="modal-content">
                                                            <div class="modal-header">
                                                                <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">关闭</span></button>
                                                                <h4 class="modal-title" id="myModalLabel"><strong>从地址薄导入</strong></h4>
                                                            </div>
                                                            <div class="modal-body">
                                                                <!-- Nav tabs -->
                                                                <ul class="nav nav-tabs" role="tablist">
                                                                    <li role="presentation" class="active">
                                                                        <a href="#person" role="tab" data-toggle="tab">个人地址</a>
                                                                    </li>
                                                                    <li role="presentation">
                                                                        <a href="#all" role="tab" data-toggle="tab">常用地址</a>
                                                                    </li>

                                                                </ul>

                                                                <!-- Tab panes -->
                                                                <div class="tab-content">
                                                                        <div class="form-group col-md-5">
                                                                            <input type="password" class="form-control" id="inputPassword" placeholder="输入姓名/手机号/地址查询">
                                                                        </div>
                                                                        <button type="submit" class="btn btn-danger new">搜索</button>
                                                                    <table class="table table-striped">
                                                                        <thead>
                                                                            <tr>
                                                                                <th>姓名</th>
                                                                                <th>联系方式</th>
                                                                                <th>公司名</th>
                                                                                <th>详细地址</th>
                                                                            </tr>
                                                                        </thead>
                                                                        <tbody>

                                                                        </tbody>

                                                                    </table>

                                                                    <div role="tabpanel" class="tab-pane active clearfix" id="person">个人地址</div>
                                                                    <div role="tabpanel" class="tab-pane" id="all">常用地址</div>

                                                                </div>

                                                            </div>
                                                            <div class="modal-footer">
                                                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                                                <button type="button" class="btn btn-primary">确定</button>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                                <!-- Modal end -->

                                            </div>
                                        </div>

                                        <div class="form-group" id="mobile-group">
                                            <label class="col-sm-3 control-label"><b>*</b>联系方式:</label>

                                            <div class="col-sm-8">
                                                <input name="sendMobile" type="text" class="form-control" placeholder="请输入联系电话" required="required">

                                            </div>
                                        </div>
                                        <div class="form-group" id="sendcompany-group">
                                            <label class="col-sm-3 control-label">寄件公司:</label>

                                            <div class="col-sm-8">
                                                <input name="sendCompany" type="text" class="form-control" placeholder="请输入寄件公司">
                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label class="col-sm-3 control-label">省市区:</label>

                                            <div class="col-sm-8 province">
                                                <div class="docs-methods">
                                                        <div id="distpicker">
                                                            <div class="form-group">
                                                                <div class="innercity">
                                                                    <input id="sendAreaInfo" name="sendAreaInfo" class="form-control" readonly type="text" data-toggle="city-picker">
                                                                </div>
                                                                <button class="btn btn-warning" id="reset" type="button" style="float: right;padding: 8px;">重置</button>
                                                            </div>

                                                        </div>
                                                </div>

                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label for="sendAddress" class="col-sm-3 control-label"><b>*</b>详细地址:</label>

                                            <div class="col-sm-8">
                                                <input id="sendAddress" name="sendAddress" type="text" class="form-control" placeholder="街道详细(精确到门牌号) " required="required">
                                                <div id="searchResultPanel" style="border:1px solid #C0C0C0;width:150px;height:auto; display:none;"></div>
                                                <p class="error">
                                                    请填写正确的信息
                                                </p>
                                            </div>
                                        </div>

                                    </div>
                            </div>
                            <div class="col-md-7"></div>
                        </div>
                        <!--收件人信息 -->
                        <div class="model clearfix">
                            <div class="areatitle">
                                <h4><span class="zhezhao">收件人信息</span></h4>

                            </div>
                            <div class="col-md-5">
                                    <div class="orderinfo">
                                        <div class="form-group" id="sender-group">
                                            <label class="col-sm-3 control-label"><b>*</b>姓名:</label>

                                            <div class="col-sm-8 input-group" style="padding:0 15px;">
                                                <input name="recName" type="text" class="form-control" style="z-index: 0;" placeholder="请输入收件人姓名" required="required">
                                                <div class="input-group-addon">
                                                    <button type="button" data-toggle="modal" data-target="#myModal" id="dao"><img src="images/icon/suyun/contact.png"></button>
                                                </div>

                                                <!-- Modal -->
                                                <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                                                    <div class="modal-dialog">
                                                        <div class="modal-content">
                                                            <div class="modal-header">
                                                                <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">关闭</span></button>
                                                                <h4 class="modal-title" id="myModalLabel"><strong>从地址薄导入</strong></h4>
                                                            </div>
                                                            <div class="modal-body">
                                                                <!-- Nav tabs -->
                                                                <ul class="nav nav-tabs" role="tablist">
                                                                    <li role="presentation" class="active">
                                                                        <a href="#person" role="tab" data-toggle="tab">个人地址</a>
                                                                    </li>
                                                                    <li role="presentation">
                                                                        <a href="#all" role="tab" data-toggle="tab">常用地址</a>
                                                                    </li>

                                                                </ul>

                                                                <!-- Tab panes -->
                                                                <div class="tab-content">
                                                                        <div class="form-group col-md-5">
                                                                            <input type="text" class="form-control" id="inputPassword" placeholder="输入姓名/手机号/地址查询">
                                                                        </div>
                                                                        <button type="submit" class="btn btn-danger new">搜索</button>
                                                                    <table class="table table-striped">
                                                                        <thead>
                                                                            <tr>
                                                                                <th>姓名</th>
                                                                                <th>联系方式</th>
                                                                                <th>公司名</th>
                                                                                <th>详细地址</th>
                                                                            </tr>
                                                                        </thead>
                                                                        <tbody>

                                                                        </tbody>

                                                                    </table>

                                                                    <div role="tabpanel" class="tab-pane active clearfix" id="person">个人地址</div>
                                                                    <div role="tabpanel" class="tab-pane" id="all">常用地址</div>

                                                                </div>

                                                            </div>
                                                            <div class="modal-footer">
                                                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                                                <button type="button" class="btn btn-primary">确定</button>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                                <!-- Modal end -->
                                            </div>
                                        </div>
                                        <div class="form-group" id="mobile-group">
                                            <label class="col-sm-3 control-label"><b>*</b>联系方式:</label>

                                            <div class="col-sm-8">
                                                <input name="recMobile" type="text" class="form-control" placeholder="请输入收件人电话" required="required">

                                            </div>
                                        </div>
                                        <div class="form-group" id="sendcompany-group">
                                            <label class="col-sm-3 control-label">收件公司:</label>

                                            <div class="col-sm-8">
                                                <input name="recCompany" type="text" class="form-control" placeholder="请输入收件公司">
                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label class="col-sm-3 control-label">省市区:</label>

                                            <div class="col-sm-8 province">
                                                <div class="docs-methods">
                                                        <div id="distpicker">
                                                            <div class="form-group">
                                                                <div class="innercity">
                                                                    <input name="recAreaInfo" id="recAreaInfo" class="form-control" readonly type="text" data-toggle="city-picker">
                                                                </div>
                                                                <button class="btn btn-warning" id="reset2" type="button" style="float: right;padding: 8px;">重置</button>
                                                            </div>
                                                        </div>
                                                </div>
                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label for="inputPassword" class="col-sm-3 control-label"><b>*</b>详细地址:</label>

                                            <div class="col-sm-8">
                                                <input name="recAddress" type="text" class="form-control" placeholder="街道详细(精确到门牌号) " required="required">
                                            </div>
                                        </div>
                                    </div>
                            </div>
                            <div class="col-md-7"></div>

                        </div>

                        <div class="partline"></div>

                        <!--快件信息 -->
                        <div class="model clearfix">
                            <div class="areatitle">
                                <h4><span class="zhezhao">快件信息</span></h4>

                            </div>
                            <div class="col-md-5">
                                    <div class="orderinfo">
                                        <div class="form-group" id="sender-group">
                                            <label class="col-sm-3 control-label"><b>*</b>拖寄物:</label>

                                            <div class="col-sm-8">
                                                <select class="form-control" name="goodsType">
                                                    <option>文件</option>
                                                    <option>衣服</option>
                                                    <option>食品</option>
                                                    <option>电子产品</option>
                                                </select>

                                            </div>
                                        </div>
                                        <div class="form-group" id="mobile-group">
                                            <label class="col-sm-3 control-label">重量:</label>

                                            <div class="col-sm-8">
                                                <input name="weight" type="text" class="form-control" placeholder="请输入重量" required="required">

                                            </div>
                                        </div>
                                        <div class="form-group" id="sendcompany-group">
                                            <label class="col-sm-3 control-label">备注:</label>

                                            <div class="col-sm-8">
                                                <input name="remark" type="text" class="form-control" placeholder="备注不超过20个字">
                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label class="col-sm-3 control-label">快递产品:</label>

                                            <div class="col-sm-8">
                                                <select class="form-control" name="sendProNum">
                                                    <option>速运当日</option>
                                                    <option>速运次日</option>
                                                    <option>速运隔日</option>

                                                </select>

                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label for="inputPassword" class="col-sm-3 control-label">付款方式:</label>
                                            <div class="col-sm-8">
                                                <select class="form-control" name="payTypeNum">
                                                    <option>寄付日结</option>
                                                    <option>寄付月结</option>
                                                    <option>到付</option>
                                                </select>
                                            </div>
                                        </div>
                                        </div>
                                </div>
                                <div class="col-md-7"></div>
                            </div>

                            <div class="partline"></div>

                            <!--预约-->
                            <div class="model clearfix">
                                <div class="col-md-5">
                                        <div class="orderinfo">
                                            <div class="form-group" id="mobile-group">
                                                <label class="col-sm-3 control-label">给小哥捎话:</label>

                                                <div class="col-sm-8">
                                                    <input name="sendMobileMsg" type="text" class="form-control" placeholder="有什么要提醒快递员的吗?" required="required">

                                                </div>
                                            </div>
                                        </div>
                                    </form>
                                </div>
                                <div class="col-md-7"></div>
                            </div>
                        </form>
                            </div>

                            <div role="tabpanel" class="tab-pane fade" id="two">港澳台待定</div>

                        </div>

                    </div>
                    <div role="tabpanel" class="tab-pane fade" id="profile">

                        <div class="model">
                            <div class="areatitle">
                                <h4><span class="zhezhao">寄件人信息</span></h4>

                            </div>
                            <div class="col-md-5">
                                <form class="form-horizontal" name="OrderForm" ng-submit="submitForm()">
                                    <div class="orderinfo">
                                        <div class="form-group" id="sender-group">
                                            <label class="col-sm-3 control-label"><b>*</b>姓名:</label>

                                            <div class="col-sm-8">
                                                <input name="sendName" type="text" class="form-control" placeholder="请输入发件人姓名" ng-model="orderdata.sendName" required="required">

                                            </div>
                                        </div>
                                        <div class="form-group" id="mobile-group">
                                            <label class="col-sm-3 control-label"><b>*</b>联系方式:</label>

                                            <div class="col-sm-8">
                                                <input name="sendMobile" type="text" class="form-control" placeholder="请输入联系电话" ng-model="orderdata.sendMobile" required="required">

                                            </div>
                                        </div>
                                        <div class="form-group" id="sendcompany-group">
                                            <label class="col-sm-3 control-label">寄件公司:</label>

                                            <div class="col-sm-8">
                                                <input name="sendCompany" type="text" class="form-control" placeholder="请输入寄件公司" ng-model="orderdata.sendCompany">
                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label class="col-sm-3 control-label">省市区:</label>

                                            <div class="col-sm-8">
                                                <input name="sendArea" type="text" class="form-control" placeholder="请填写正确的省市区" ng-model="orderdata.sendArea">

                                            </div>
                                        </div>
                                        <div class="form-group">
                                            <label for="inputPassword" class="col-sm-3 control-label"><b>*</b>详细地址:</label>

                                            <div class="col-sm-8">
                                                <input name="sendAddress" type="text" class="form-control" placeholder="街道详细(精确到门牌号) " ng-model="orderdata.sendAddress" required="required">

                                            </div>
                                        </div>

                                    </div>

                                </form>
                            </div>
                            <div class="col-md-7"></div>
                        </div>

                        <div class="partline clearfix"></div>

                        <div class="daoru">
                            <p>
                                <a href="#" class="btn btn-default" data-toggle="modal" data-target="#ExcelModal">导入Excel</a>
                                <a href="#" class="btn btn-default">从地址薄导入</a>
                                <input type="button" onclick="Click()" value="下载Excel" class="btn btn-danger">
                            </p>

                        </div>
                        <!--导入excel-->

                        <div class="modal fade" id="ExcelModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                            <div class="modal-dialog">
                                <div class="modal-content">
                                    <div class="modal-header">
                                        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
                                        <h4 class="modal-title" id="myModalLabel"><strong>导入Excel模板</strong></h4>
                                    </div>
                                    <div class="modal-body" ng-controller="AppController" nv-file-drop="" uploader="uploader" filters="queueLimit, customFilter">
                                        <center><img src="images/icon/xlsLogo.png" class="img-responsive"></center>
                                        <div class="row excel">

                                            <div class="col-md-6">
                                                <input type="file" nv-file-select="" uploader="uploader" />
                                            </div>

                                            <div class="col-md-6">

                                                <div>
                                                    <span style="float:left">上传进度:</span>
                                                    <div class="progress" style="">
                                                        <div class="progress-bar" role="progressbar" ng-style="{ 'width': uploader.progress + '%' }"></div>
                                                    </div>
                                                </div>
                                            </div>
                                            <button type="button" class="btn btn-success btn-s" ng-click="uploader.uploadAll()" ng-disabled="!uploader.getNotUploadedItems().length">
                            <span class="glyphicon glyphicon-upload"></span> 上传
                        </button>

                                            <button type="button" class="btn btn-danger btn-s" ng-click="uploader.clearQueue()" ng-disabled="!uploader.queue.length">
                            <span class="glyphicon glyphicon-trash"></span> 删除
                        </button>

                                        </div>

                                    </div>
                                    <div class="modal-footer">
                                        <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                                        <button type="button" class="btn btn-primary">确定</button>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div class="searchset">
                            <form class="form-inline">
                                <div class="form-group">

                                    <div class="input-group">

                                        <input type="text" class="form-control" id="exampleInputAmount" placeholder="请输入姓名/电话/地址">
                                        <div class="input-group-addon btn btn-danger">搜索</div>
                                    </div>
                                </div>
                                <input type="checkbox" /><span class="typetext">只显示错误信息</span>
                            </form>
                            <div class="setbtn">
                                <a href="#" class="btn btn-danger">批量设置</a>
                                <a href="#" class="btn btn-default">删除</a>
                            </div>

                        </div>

                        <!--piliang-->
                        <table class="table table-striped clearfix">

                            <tr>
                                <td><input type="checkbox" /> 收件人姓名</td>
                                <td>收件人联系电话 </td>
                                <td>收件人公司 </td>
                                <td></td>
                                <td></td>
                                <td></td>
                                <td>详细地址</td>
                            </tr>
                        </table>

                    </div>

                    <div class="col-md-11 submit">
                        <div class="xieyi"><input type="checkbox" checked="checked" />我已阅读并同意《快件运单契约条款》 </div>
                        <div class="tijiao">
                            <div class="tjtext">
                                <p class="red">1、如果您在周六、日等法定节假日期间寄件,将会做相应加时。(以上“预计时效”已做相应加时)</p>
                                <p class="red">2、如果您收方地址属于偏远地区,在“预计时效”基础上需增加0.5天.</p>
                            </div>

                        </div>

                    </div>
                    <div class="col-md-1 ordertj">
                        <a href="javascript:$('#orderForm').submit();" class="btn btn-danger" >提交</a>
                    </div>
                </div>
    </section>
</div>
    <script src="js/self/city-picker.data.js"></script>
    <script src="js/self/city-picker.js"></script>
    <script src="js/angular-file-upload.min.js"></script>
    <script src="js/console-sham.min.js"></script>
    <script src="js/self/controllers.js"></script>
    <script src="js/self/main.js"></script>
    <script src="js/self/effect.js"></script>
    <script>
        function Click() {
            $('a[rel*=downloadr]').downloadr()
        }
    </script>

二.自动分单逻辑:分单的方式(根据用户填写的区域和地址信息,查询快递员的过程)
(一)根据区域和地址一起查询快递员。
1.先根据区域的信息,在数据库中查询区域对象,从区域对象中获取管理的分区对象列表
2.循环分区对象列表,获取分区关键字,和用户输入的地址比较,是否包含关键字,找到当前用户输入的地址属于哪个具体的分区。
3.根据查到的分区对象,获得其定区对象
4.定区和快递员是多对多关系,但是一个排班时间中,只有一个快递员
5.选中当前班次的快递员,让他去。
(二)根据定区关联的客户查询快递员
1.根据用户输入的地址信息,通过webservice在CRM系统中,查询定区的ID
2.根据定区ID,查询定区对象
3.定区和快递员是多对多关系,但是一个排班时间中,只有一个快递员
4.选中当前班次的快递员,让他去。
(三)第二种根据关联客户的信息查询快递员更加准确,所以应该先判断是否关联了定区,没有关联定区的时候,才判断分区信息。如果上述两种情况,都没有匹配快递员,进入人工分单。

三.前端表单提交,基于WebService将数据传递后台系统
1.在bos_fore系统,添加OrderAction封装订单数据

@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class OrderAction extends BaseAction<Order> {

    private String sendAreaInfo; // 发件人省市区 信息
    private String recAreaInfo; // 收件人 省市区信息

    public void setSendAreaInfo(String sendAreaInfo) {
        this.sendAreaInfo = sendAreaInfo;
    }

    public void setRecAreaInfo(String recAreaInfo) {
        this.recAreaInfo = recAreaInfo;
    }

    @Action(value = "order_add", results = { @Result(name = "success", type = "redirect", location = "index.html") })
    public String add() {
        System.out.println(sendAreaInfo+"地址在+++++++++++++++++++++++++++++++");
        // 手动封装 Area关联
        Area sendArea = new Area();
        String[] sendAreaData = sendAreaInfo.split("/");
        sendArea.setProvince(sendAreaData[0]);
        sendArea.setCity(sendAreaData[1]);
        sendArea.setDistrict(sendAreaData[2]);

        Area recArea = new Area();
        System.out.println(recAreaInfo);
        String[] recAreaData = recAreaInfo.split("/");

        recArea.setProvince(recAreaData[0]);
        recArea.setCity(recAreaData[1]);
        recArea.setDistrict(recAreaData[2]);

        model.setSendArea(sendArea);
        model.setRecArea(recArea);

        // 关联当前登录客户
        Customer customer = (Customer) ServletActionContext.getRequest()
                .getSession().getAttribute("customer");
        model.setCustomer_id(customer.getId());

        // 调用WebService 将数据传递 bos_management系统
        WebClient
                .create(Constants.BOS_MANAGEMENT_URL
                        + "/services/orderService/order")
                .type(MediaType.APPLICATION_JSON).post(model);

        return SUCCESS;
    }

2.修改bos_domain 添加@XmlRootElement

@Entity
@Table(name = "T_ORDER")
@XmlRootElement(name = "order")
public class Order {

3.在bos_management中添加OrderService发布WebService

public interface OrderService {

    @Path("/order")
    @POST
    @Consumes({ "application/xml", "application/json" })
    public void saveOrder(Order order);

    public Order findByOrderNum(String orderNum);
}

4.配置applicationContext-webService.xml

<jaxrs:server id="orderService" address="/orderService">
        <jaxrs:serviceBeans>
            <bean class="cn.itcast.bos.service.take_delivery.impl.OrderServiceImpl" />
        </jaxrs:serviceBeans>
        <jaxrs:inInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
        </jaxrs:inInterceptors>
        <jaxrs:outInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
        </jaxrs:outInterceptors>
    </jaxrs:server>

5.完善bos_fore的OrderAction代码,调用WebService

四.自动分单—基于CRM地址完全匹配自动分单
1.在crm_management中CustomerService添加根据地址获取定区编码方法

public interface CustomerService {

    // 查询所有未关联客户列表
    @Path("/noassociationcustomers")
    @GET
    @Produces({ "application/xml", "application/json" })
    public List<Customer> findNoAssociationCustomers();

    // 已经关联到指定定区的客户列表
    @Path("/associationfixedareacustomers/{fixedareaid}")
    @GET
    @Produces({ "application/xml", "application/json" })
    public List<Customer> findHasAssociationFixedAreaCustomers(
            @PathParam("fixedareaid") String fixedAreaId);

    // 将客户关联到定区上 , 将所有客户id 拼成字符串 1,2,3
    @Path("/associationcustomerstofixedarea")
    @PUT
    public void associationCustomersToFixedArea(
            @QueryParam("customerIdStr") String customerIdStr,
            @QueryParam("fixedAreaId") String fixedAreaId);

    @Path("/customer")
    @POST
    @Consumes({ "application/xml", "application/json" })
    public void regist(Customer customer);

    @Path("/customer/telephone/{telephone}")
    @GET
    @Consumes({ "application/xml", "application/json" })
    public Customer findByTelephone(@PathParam("telephone") String telephone);

    @Path("/customer/updatetype/{telephone}")
    @GET
    public void updateType(@PathParam("telephone") String telephone);

    @Path("customer/login")
    @GET
    @Consumes({ "application/xml", "application/json" })
    public Customer login(@QueryParam("telephone") String telephone,
            @QueryParam("password") String password);

    @Path("/customer/findFixedAreaIdByAddress")
    @GET
    @Consumes({ "application/xml", "application/json" })
    public String findFixedAreaIdByAddress(@QueryParam("address") String address);

}

2.修改bos_management的OrderService

第三阶段
24.✔★后端系统 运单快速录入
一.在表单上添加列属性editor
二.调用datagrid的方法,对指定数据行开启编辑功能
三.在表格插入一行新的数据,进行编辑

        <script type="text/javascript">
            var editIndex ;

            function doAdd(){
                if(editIndex != undefined){
                    $("#grid").datagrid('endEdit',editIndex);
                }
                if(editIndex==undefined){
                    //alert("快速添加电子单...");
                    $("#grid").datagrid('insertRow',{
                        index : 0,
                        row : {}
                    });
                    $("#grid").datagrid('beginEdit',0);
                    editIndex = 0;
                }
            }

            function doSave(){
                $("#grid").datagrid('endEdit',editIndex );
            }

            function doCancel(){
                if(editIndex!=undefined){
                    $("#grid").datagrid('cancelEdit',editIndex);
                    if($('#grid').datagrid('getRows')[editIndex].id == undefined){
                        $("#grid").datagrid('deleteRow',editIndex);
                    }
                    editIndex = undefined;
                }
            }

            //工具栏
            var toolbar = [ {
                id : 'button-add',  
                text : '新增一行',
                iconCls : 'icon-edit',
                handler : doAdd
            }, {
                id : 'button-cancel',
                text : '取消编辑',
                iconCls : 'icon-cancel',
                handler : doCancel
            }, {
                id : 'button-save',
                text : '保存',
                iconCls : 'icon-save',
                handler : doSave
            }];
            // 定义列
            var columns = [ [ {
                field : 'wayBillNum',
                title : '运单号',
                width : 120,
                align : 'center',
                editor :{
                    type : 'validatebox',
                    options : {
                        required: true
                    }
                }
            }, {
                field : 'arriveCity',
                title : '到达地',
                width : 120,
                align : 'center',
                editor :{
                    type : 'validatebox',
                    options : {
                        required: true
                    }
                }
            },{
                field : 'goodsType',
                title : '货物类型',
                width : 120,
                align : 'center',
                editor :{
                    type : 'validatebox',
                    options : {
                        required: true
                    }
                }
            }, {
                field : 'num',
                title : '件数',
                width : 120,
                align : 'center',
                editor :{
                    type : 'numberbox',
                    options : {
                        required: true
                    }
                }
            }, {
                field : 'weight',
                title : '重量',
                width : 120,
                align : 'center',
                editor :{
                    type : 'validatebox',
                    options : {
                        required: true
                    }
                }
            }, {
                field : 'floadreqr',
                title : '配载要求',
                width : 220,
                align : 'center',
                editor :{
                    type : 'validatebox',
                    options : {
                        required: true
                    }
                }
            }] ];

            $(function(){
                // 先将body隐藏,再显示,不会出现页面刷新效果
                $("body").css({visibility:"visible"});

                // 运单数据表格
                $('#grid').datagrid( {
                    iconCls : 'icon-forward',
                    fit : true,
                    border : true,
                    rownumbers : true,
                    striped : true,
                    pageList: [30,50,100],
                    pagination : true,
                    toolbar : toolbar,
                    url :  "../../waybill_pageQuery.action",
                    idField : 'id',
                    columns : columns,
                    onDblClickRow : doDblClickRow,
                    onAfterEdit : function(rowIndex, rowData, changes){
                        // 将新添加运单数据 保存到 数据表中 
                        $.post("../../waybill_save.action",rowData, function(data){
                            // data数据格式 {success:true, msg:'xxxx'} 
                            if(data.success){
                                // 保存成功 
                                $.messager.show({
                                    title:'提示窗口',   
                                    msg: data.msg,
                                });

                                // 刷新列表 
                                $("#grid").datagrid('reload');
                            }
                        });
                        console.info(rowData);
                        editIndex = undefined;
                    }
                });
            });

            function doDblClickRow(rowIndex, rowData){
                alert("双击表格数据...");
                console.info(rowIndex);
                $('#grid').datagrid('beginEdit',rowIndex);
                editIndex = rowIndex;
            }
        </script>

四.服务器代码上创建WayBillAction提供save方法

// 运单管理 
@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class WayBillAction extends BaseAction<WayBill> {

    private static final Logger LOGGER = Logger.getLogger(WayBillAction.class);

    @Autowired
    private WayBillService wayBillService;

    @Action(value = "waybill_save", results = { @Result(name = "success", type = "json") })
    public String save() {
        Map<String, Object> result = new HashMap<String, Object>();
        try {
            // 去除 没有id的order对象
            if (model.getOrder() != null
                    && (model.getOrder().getId() == null || model.getOrder()
                            .getId() == 0)) {
                model.setOrder(null);
            }

            wayBillService.save(model);
            // 保存成功
            result.put("success", true);
            result.put("msg", "保存运单成功!");
            LOGGER.info("保存运单成功,运单号:" + model.getWayBillNum());
        } catch (Exception e) {
            e.printStackTrace();
            // 保存失败
            result.put("success", false);
            result.put("msg", "保存运单失败!");
            LOGGER.error("保存运单失败,运单号:" + model.getWayBillNum(), e);
        }
        ActionContext.getContext().getValueStack().push(result);
        return SUCCESS;
    }

25.✔★后端系统 运单录入
一.订单数据回显表单
1.对订单号输入框,添加onblur离焦事件

        <script type="text/javascript">
            $(function() {
                $("body").css({
                    visibility: "visible"
                });



                // 对save按钮条件 点击事件
                $('#save').click(function() {
                    // 对form 进行校验
                    $.post("../../waybill_save.action",$("#waybillForm").serialize(),function(data){
                            // data数据格式 {success:true, msg:'xxxx'} 
                            if(data.success){
                                // 保存成功 
                                $.messager.show({
                                    title:'提示窗口',   
                                    msg: data.msg,
                                });

                                // 重置表单
                                $("#waybillForm").get(0).reset();
                                $("input[name='order.id']").val('');
                            }
                    });
                });

                // 对订单号输入项 添加 blur事件
                $("#orderNum").blur(function(){
                    // 发起Ajax请求,查询订单数据 
                    $.post("../../order_findByOrderNum.action",{orderNum: $(this).val()}, 
                    function(data){
                        // 封装结果数据  { success:true , orderData: {} }
                        if(data.success){
                            // 装载数据 
                            $("#waybillForm").form('load',data.orderData) ;
                            // 处理无法自动load 元素
                            $("input[name='order.id']").val(data.orderData.id);
                            $("input[name='order.orderNum']").val(data.orderData.orderNum);
                            $("input[name='order.courier.company']").val(data.orderData.courier.company);
                            $("input[name='order.courier.name']").val(data.orderData.courier.name);
                        }else{
                            // 订单号 不存在,重置表单 
                            $("#waybillForm").get(0).reset();
                        }
                    });
                });

                // 对运单快速录入数据进行回显
                $("#wayBillNum").blur(function(){
                    // 发起Ajax请求 
                    $.post("../../waybill_findByWayBillNum.action",{wayBillNum: $(this).val()},
                    function(data){
                        // 封装结果数据  { success:true , wayBillData: {} }
                        if(data.success){
                            // 装载数据 
                            $("#waybillForm").form('load',data.wayBillData) ;
                        }
                    });
                });
            });
        </script>

2.在OrderAction添加findByOrderNum方法

@Autowired
    private OrderService orderService;

    @Action(value="order_findByOrderNum",results={@Result(name="success",type="json")})
    public String findByOrderNum(){
        //调用业务层,查询order信息
        Order order=orderService.findByOrderNum(model.getOrderNum());
        Map<String,Object> result=new HashMap<>();
        if(order==null){
            //订单号不存在
            result.put("success", false);

        }else{
            //订单号存在
             result.put("success", true);
             result.put("orderData", order);
        }
        ActionContext.getContext().getValueStack().push(result);
        return SUCCESS;
    }

二.采用运单快速录入的数据回显表单
思路:对运单号输入框,添加onblur事件,根据运单号查询,查询到信息回显表单
1.对运单数据项添加blur事件
2.服务器代码wayBillAction提供findByWayBillNum方法

@Action(value = "waybill_findByWayBillNum", results = { @Result(name = "success", type = "json") })
    public String findByWayBillNum() {
        // 调用业务层 查询
        WayBill wayBill = wayBillService
                .findByWayBillNum(model.getWayBillNum());
        Map<String, Object> result = new HashMap<String, Object>();
        if (wayBill == null) {
            // 运单不存在
            result.put("success", false);
        } else {
            // 运单存在
            result.put("success", true);
            result.put("wayBillData", wayBill);
        }
        ActionContext.getContext().getValueStack().push(result);

        return SUCCESS;
    }

三.运单录入数据保存
1.点击页面保存按钮,提交表单访问WayBillAction的save方法

private static final Logger LOGGER = Logger.getLogger(WayBillAction.class);

    @Autowired
    private WayBillService wayBillService;

    @Action(value = "waybill_save", results = { @Result(name = "success", type = "json") })
    public String save() {
        Map<String, Object> result = new HashMap<String, Object>();
        try {
            // 去除 没有id的order对象
            if (model.getOrder() != null
                    && (model.getOrder().getId() == null || model.getOrder()
                            .getId() == 0)) {
                model.setOrder(null);
            }

            wayBillService.save(model);
            // 保存成功
            result.put("success", true);
            result.put("msg", "保存运单成功!");
            LOGGER.info("保存运单成功,运单号:" + model.getWayBillNum());
        } catch (Exception e) {
            e.printStackTrace();
            // 保存失败
            result.put("success", false);
            result.put("msg", "保存运单失败!");
            LOGGER.error("保存运单失败,运单号:" + model.getWayBillNum(), e);
        }
        ActionContext.getContext().getValueStack().push(result);
        return SUCCESS;
    }

26.✔★运单管理
一.运单列表显示—–(条件查询)全文检索
1.在bos_management引入elasticsearch和spring data elasticsearch 的支持

<!-- elasticsearch  -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>

2.在实体类WayBill对象,添加elasticsearch索引和映射信息操作bos_domain项目

@Entity
@Table(name = "T_WAY_BILL")
@Document(indexName = "bos", type = "waybill")
public class WayBill implements Serializable {

    @Id
    @GeneratedValue
    @Column(name = "C_ID")
    @org.springframework.data.annotation.Id
    @Field(index = FieldIndex.not_analyzed, store = true, type = FieldType.Integer)
    private Integer id;
    @Column(name = "C_WAY_BILL_NUM", unique = true)
    @Field(index = FieldIndex.not_analyzed, store = true, type = FieldType.String)
    private String wayBillNum; // 运单编号
    @OneToOne
    @JoinColumn(name = "C_ORDER_ID")
    private Order order; // 订单信息

    @Column(name = "C_SEND_NAME")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String sendName; // 寄件人姓名
    @Column(name = "C_SEND_MOBILE")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String sendMobile;// 寄件人电话
    @Column(name = "C_SEND_COMPANY")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String sendCompany;// 寄件人公司
    @OneToOne
    @JoinColumn(name = "C_SEND_AREA_ID")
    private Area sendArea; // 寄件人省市区信息
    @Column(name = "C_SEND_ADDRESS")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String sendAddress;// 寄件人详细地址信息

    @Column(name = "C_REC_NAME")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String recName;// 收件人姓名
    @Column(name = "C_REC_MOBILE")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String recMobile;// 收件人电话
    @Column(name = "C_REC_COMPANY")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String recCompany;// 收件人公司
    @OneToOne
    @JoinColumn(name = "C_REC_AREA_ID")
    private Area recArea; // 收件人省市区信息
    @Column(name = "C_REC_ADDRESS")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String recAddress;// 收件人详细地址信息

    @Column(name = "C_SEND_PRO_NUM")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String sendProNum; // 快递产品类型编号:速运当日、速运次日、速运隔日
    @Column(name = "C_GOODS_TYPE")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String goodsType;// 托寄物类型:文件、衣服 、食品、电子商品
    @Column(name = "C_PAY_TYPE_NUM")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String payTypeNum;// 支付类型编号:寄付日结、寄付月结、到付
    @Column(name = "C_WEIGHT")
    @Field(index = FieldIndex.no, store = true, type = FieldType.String)
    private Double weight;// 托寄物重量
    @Column(name = "C_REMARK")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String remark; // 备注
    @Column(name = "C_NUM")
    @Field(index = FieldIndex.no, store = true, type = FieldType.String)
    private Integer num; // 原件数

    @Column(name = "C_ARRIVE_CITY")
    @Field(index = FieldIndex.analyzed, analyzer = "ik", searchAnalyzer = "ik", store = true, type = FieldType.String)
    private String arriveCity; // 到达地

    @Column(name = "C_FEEITEMNUM")
    @Field(index = FieldIndex.no, store = true, type = FieldType.String)
    private Integer feeitemnum; // 实际件数
    @Column(name = "C_ACTLWEIT")
    @Field(index = FieldIndex.no, store = true, type = FieldType.String)
    private Double actlweit; // 实际重量
    @Column(name = "C_VOL")
    @Field(index = FieldIndex.no, store = true, type = FieldType.String)
    private String vol; // 体积 输入格式为1*1*1*1,表示长*宽*高*数量
    @Column(name = "C_FLOADREQR")
    @Field(index = FieldIndex.no, store = true, type = FieldType.String)
    private String floadreqr; // 配载要求 (比如录入1=无,2=禁航,4=禁航空铁路)

    @Column(name = "C_WAY_BILL_TYPE")
    private String wayBillType; // 运单类型(类型包括:正常单据、异单、调单)
    /*
     * 运单状态: 1 待发货、 2 派送中、3 已签收、4 异常
     */
    @Column(name = "C_SIGN_STATUS")
    @Field(index = FieldIndex.not_analyzed, store = true, type = FieldType.String)
    private Integer signStatus; // 签收状态

    /*
     * 1、新增修改单据状态为“否” 2、作废时需将状态置为“是” 3、取消作废时需要将状态置为“否”
     */
    @Column(name = "C_DELTAG")
    private String delTag; // 作废标志

    public String getArriveCity() {
        return arriveCity;
    }

    public void setArriveCity(String arriveCity) {
        this.arriveCity = arriveCity;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getWayBillNum() {
        return wayBillNum;
    }

    public void setWayBillNum(String wayBillNum) {
        this.wayBillNum = wayBillNum;
    }

    @JsonIgnore
    public Order getOrder() {
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
    }

    public String getSendName() {
        return sendName;
    }

    public void setSendName(String sendName) {
        this.sendName = sendName;
    }

    public String getSendMobile() {
        return sendMobile;
    }

    public void setSendMobile(String sendMobile) {
        this.sendMobile = sendMobile;
    }

    public String getSendCompany() {
        return sendCompany;
    }

    public void setSendCompany(String sendCompany) {
        this.sendCompany = sendCompany;
    }

    @JsonIgnore
    public Area getSendArea() {
        return sendArea;
    }

    public void setSendArea(Area sendArea) {
        this.sendArea = sendArea;
    }

    public String getSendAddress() {
        return sendAddress;
    }

    public void setSendAddress(String sendAddress) {
        this.sendAddress = sendAddress;
    }

    public String getRecName() {
        return recName;
    }

    public void setRecName(String recName) {
        this.recName = recName;
    }

    public String getRecMobile() {
        return recMobile;
    }

    public void setRecMobile(String recMobile) {
        this.recMobile = recMobile;
    }

    public String getRecCompany() {
        return recCompany;
    }

    public void setRecCompany(String recCompany) {
        this.recCompany = recCompany;
    }

    @JsonIgnore
    public Area getRecArea() {
        return recArea;
    }

    public void setRecArea(Area recArea) {
        this.recArea = recArea;
    }

    public String getRecAddress() {
        return recAddress;
    }

    public void setRecAddress(String recAddress) {
        this.recAddress = recAddress;
    }

    public String getSendProNum() {
        return sendProNum;
    }

    public void setSendProNum(String sendProNum) {
        this.sendProNum = sendProNum;
    }

    public String getGoodsType() {
        return goodsType;
    }

    public void setGoodsType(String goodsType) {
        this.goodsType = goodsType;
    }

    public String getPayTypeNum() {
        return payTypeNum;
    }

    public void setPayTypeNum(String payTypeNum) {
        this.payTypeNum = payTypeNum;
    }

    public Double getWeight() {
        return weight;
    }

    public void setWeight(Double weight) {
        this.weight = weight;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public Integer getFeeitemnum() {
        return feeitemnum;
    }

    public void setFeeitemnum(Integer feeitemnum) {
        this.feeitemnum = feeitemnum;
    }

    public Double getActlweit() {
        return actlweit;
    }

    public void setActlweit(Double actlweit) {
        this.actlweit = actlweit;
    }

    public String getVol() {
        return vol;
    }

    public void setVol(String vol) {
        this.vol = vol;
    }

    public String getFloadreqr() {
        return floadreqr;
    }

    public void setFloadreqr(String floadreqr) {
        this.floadreqr = floadreqr;
    }

    public String getWayBillType() {
        return wayBillType;
    }

    public void setWayBillType(String wayBillType) {
        this.wayBillType = wayBillType;
    }

    public Integer getSignStatus() {
        return signStatus;
    }

    public void setSignStatus(Integer signStatus) {
        this.signStatus = signStatus;
    }

    public String getDelTag() {
        return delTag;
    }

    public void setDelTag(String delTag) {
        this.delTag = delTag;
    }

}

3.配置elasticsearch applicationContext-elasticsearch.xml

<!-- 搜索DAO 扫描 -->
    <elasticsearch:repositories base-package="cn.itcast.bos.index" />

    <!-- 配置Client -->
    <elasticsearch:transport-client id="client" cluster-nodes="127.0.0.1:9300"/>

    <!-- 配置搜索模板  -->
    <bean id="elasticsearchTemplate" 
        class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
        <constructor-arg name="client" ref="client" />
    </bean>

4.操作索引库,创建dao继承ElasticsearchRepository

public interface WayBillIndexRepository extends
        ElasticsearchRepository<WayBill, Integer> {

    public List<WayBill> findBySendAddress(String sendAddress);
}

5.修改Service代码添加保存索引的方法

@Service
@Transactional
public class WayBillServiceImpl implements WayBillService {

    @Autowired
    private WayBillRepository wayBillRepository;

    @Autowired
    private WayBillIndexRepository wayBillIndexRepository;

    @Override
    public void save(WayBill wayBill) {
        // 判断运单号是否存在
        WayBill persistWayBill = wayBillRepository.findByWayBillNum(wayBill
                .getWayBillNum());
        if (persistWayBill == null || persistWayBill.getId() == null) {
            // 运单不存在
            wayBill.setSignStatus(1); // 待发货
            wayBillRepository.save(wayBill);
            // 保存索引
            wayBillIndexRepository.save(wayBill);
        } else {
            // 运单存在
            try {
                // 判断运单状态是否 为待发货
                if (persistWayBill.getSignStatus() == 1) {
                    Integer id = persistWayBill.getId();
                    BeanUtils.copyProperties(persistWayBill, wayBill);
                    persistWayBill.setId(id);
                    persistWayBill.setSignStatus(1);// 待发货
                    // 保存索引
                    wayBillIndexRepository.save(persistWayBill);
                } else {
                    // 运单状态 已经运输中,不能修改
                    throw new RuntimeException("运单已经发出,无法修改保存!!");
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }

    }

二.搜索运单,基于索引库查询
1.将查询form的数据,转换json格式,绑定数据表格上
2.修改WayBillAction的pageQuery方法

@Action(value = "waybill_pageQuery", results = { @Result(name = "success", type = "json") })
    public String pageQuery() {
        // 无条件查询
        Pageable pageable = new PageRequest(page - 1, rows, new Sort(
                new Sort.Order(Sort.Direction.DESC, "id")));
        // 调用业务层进行查询
        Page<WayBill> pageData = wayBillService.findPageData(model, pageable);
        // 压入值栈返回
        pushPageDataToValueStack(pageData);

        return SUCCESS;
    }

3.编写业务层findPageData方法

@Override
    public Page<WayBill> findPageData(WayBill wayBill, Pageable pageable) {
        // 判断WayBill 中条件是否存在
        if (StringUtils.isBlank(wayBill.getWayBillNum())
                && StringUtils.isBlank(wayBill.getSendAddress())
                && StringUtils.isBlank(wayBill.getRecAddress())
                && StringUtils.isBlank(wayBill.getSendProNum())
                && (wayBill.getSignStatus() == null || wayBill.getSignStatus() == 0)) {
            // 无条件查询 、查询数据库
            return wayBillRepository.findAll(pageable);
        } else {
            // 查询条件
            // must 条件必须成立 and
            // must not 条件必须不成立 not
            // should 条件可以成立 or
            BoolQueryBuilder query = new BoolQueryBuilder(); // 布尔查询 ,多条件组合查询
            // 向组合查询对象添加条件
            if (StringUtils.isNoneBlank(wayBill.getWayBillNum())) {
                // 运单号查询
                QueryBuilder tempQuery = new TermQueryBuilder("wayBillNum",
                        wayBill.getWayBillNum());
                query.must(tempQuery);
            }
            if (StringUtils.isNoneBlank(wayBill.getSendAddress())) {
                // 发货地 模糊查询
                // 情况一: 输入"北" 是查询词条一部分, 使用模糊匹配词条查询
                QueryBuilder wildcardQuery = new WildcardQueryBuilder(
                        "sendAddress", "*" + wayBill.getSendAddress() + "*");

                // 情况二: 输入"北京市海淀区" 是多个词条组合,进行分词后 每个词条匹配查询
                QueryBuilder queryStringQueryBuilder = new QueryStringQueryBuilder(
                        wayBill.getSendAddress()).field("sendAddress")
                        .defaultOperator(Operator.AND);

                // 两种情况取or关系
                BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
                boolQueryBuilder.should(wildcardQuery);
                boolQueryBuilder.should(queryStringQueryBuilder);

                query.must(boolQueryBuilder);
            }
            if (StringUtils.isNoneBlank(wayBill.getRecAddress())) {
                // 收货地 模糊查询
                QueryBuilder wildcardQuery = new WildcardQueryBuilder(
                        "recAddress", "*" + wayBill.getRecAddress() + "*");
                query.must(wildcardQuery);
            }
            if (StringUtils.isNoneBlank(wayBill.getSendProNum())) {
                // 速运类型 等值查询
                QueryBuilder termQuery = new TermQueryBuilder("sendProNum",
                        wayBill.getSendProNum());
                query.must(termQuery);
            }
            if (StringUtils.isNoneBlank(wayBill.getSendProNum())) {
                // 速运类型 等值查询
                QueryBuilder termQuery = new TermQueryBuilder("sendProNum",
                        wayBill.getSendProNum());
                query.must(termQuery);
            }
            if (wayBill.getSignStatus() != null && wayBill.getSignStatus() != 0) {
                // 签收状态查询
                QueryBuilder termQuery = new TermQueryBuilder("signStatus",
                        wayBill.getSignStatus());
                query.must(termQuery);
            }

            SearchQuery searchQuery = new NativeSearchQuery(query);
            searchQuery.setPageable(pageable); // 分页效果
            // 有条件查询 、查询索引库
            return wayBillIndexRepository.search(searchQuery);
        }

    }

27.权限控制 用户登录
一.配置shiro的Filter实现URL级别权限控制
1.web.xml(shiroFilter) applicationContext-shiro.xml(核心shiroFilter的bean、安全管理器)
web.xml

<!--shiro的Filter  -->

    <filter>
        <!--去spring配置文件中寻找同名的本  -->
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
<!--shiro核心Filter  -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <!--安全管理器  -->
    <property name="securityManager" ref="securityManager"></property>
    <!--未认证,跳转到哪个页面  -->
    <property name="loginUrl" value="/login.html"/>
    <!--登录页面-->
    <property name="successUrl" value="/index.html"/>
    <!--认证后,没有权限跳转页面 -->
    <property name="unauthorizedUrl" value="/unauthorized.html"></property>
    <!--shiro URL控制过滤器规则      -->
    <property name="filterChainDefinitions">
    <value>
        /login.html*=anon
        /user_login.action*=anon
        /validatecode.jsp*=anon
        /css/**=anon
        /js/**=anon
        /images/**=anon
        /services/**=anon
        /upload/**=anon
        /attached/**=anon
        /pages/base/courier.html*=perms[courier:list]
        /pages/base/area.html*=roles[base]
        /**=authc
    </value>
    </property>

    </bean>
    <!--安全管理器  -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="bosRealm"></property>
        <property name="cacheManager" ref="shiroCacheManager"/>
    </bean>

    <!-- 配置Realm -->
    <bean id="bosRealm" class="cn.itcast.bos.realm.BosRealm">
            <!--缓存区的名字  就是ehcache.xml 自定义cache的name  -->
        <property name="authorizationCacheName" value="bos"/>
    </bean>
    <!--后处理器  -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>

    <!--开启shiro注解模式  -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true"></property>
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"></property>
    </bean>

二.用户登录(认证)功能实现
1.login.html的form表单设置action


        <section class="mainlogin">
            <div class="container">
                <div class="col-md-4 col-md-offset-7 logincontent">
                    <h4>员工登录</h4>
                    <form class="form-horizontal" id="loginform" name="loginform" method="post" action="user_login.action">
                        <div class="form-group" id="idInputLine">
                            <label for="inputPassword3" class="col-sm-3 control-label">账号</label>
                            <div class="col-sm-8">
                                <input id="loginform:idInput" type="text" name="username" class="form-control" placeholder="请输入手机号/邮箱/用户名">
                            </div>
                        </div>
                        <div class="form-group" id="pwdInputLine">
                            <label id="loginform:pwdInput"  class="col-sm-3 control-label">密码</label>
                            <div class="col-sm-8">
                                <input for="pwdInput" type="password" name="password" class="form-control" id="inputaccount" placeholder="请输入您的密码">
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="inputvalidate" class="col-sm-3 control-label">验证码</label>
                            <div class="col-sm-4">
                                <input type="password" class="form-control" id="inputaccount" placeholder="请输入验证码">
                            </div>
                            <div class="col-sm-4">
                                <img id="loginform:vCode" src="validatecode.jsp"  onclick="javascript:document.getElementById('loginform:vCode'). src='validatecode.jsp?'+Math.random();" />
                            </div>
                        </div>
                        <div class="form-group">

                            <div class="col-sm-offset-3 col-sm-4">
                                <input type="checkbox"><span class="size12"> 记住用户名</span>
                            </div>
                            <div class="col-sm-4">
                                <a href="#"><span class="size12 forget">忘记密码</span></a>
                            </div>
                        </div>
                        <div class="col-md-offset-3 col-md-8">
                            <a href="javascript:$('#loginform').submit();" id="loginform:j_id19" name="loginform:j_id19"
                                 class="btn btn-danger" target="_blank">立即登录</a>

                        </div>
                    </form>
                </div>
            </div>
        </section>

2.点击登录提交表单

@Action(value="user_login",results={@Result(name="login",type="redirect",location="login.html") ,
                              @Result(name="success",type="redirect",location="index.html")})
    public String login(){
        //用户名和密码都保存在model
        //基于shiro实现登录
        Subject subject=SecurityUtils.getSubject();

        //用户名和密码信息
        AuthenticationToken token=new UsernamePasswordToken(
                model.getUsername(), model.getPassword()
                );

        try {
            subject.login(token);
            //登录成功
            return SUCCESS;
        } catch (AuthenticationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            //登录失败
            return LOGIN;
        }
    }

4.编写BosRealm实现Realm接口,并实现Realm的认证方法

@Service("bosRealm")
public class BosRealm extends AuthorizingRealm{

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    @Override
    //授权... 
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
        System.out.println("shiro 授权管理。。。。");
        SimpleAuthorizationInfo authorizationInfo =new SimpleAuthorizationInfo();
        //根据当前登录用户查询对应角色和权限
        Subject subject=SecurityUtils.getSubject();
        User user=(User) subject.getPrincipal();
        //调用业务层,查询角色
        List<Role> roles=roleService.findByUser(user);
        for (Role role : roles) {
            authorizationInfo.addRole(role.getKeyword());
        }
        //调用业务层 ,查询权限
        List<Permission> permissions=permissionService.findByUser(user);
        for (Permission permission : permissions) {
            authorizationInfo.addStringPermission(permission.getKeyword());
        }


        return authorizationInfo;
    }

    @Override
    //认证。。。
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        System.out.println("shiro认证管理。。。。");

        //转换token
        UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken) token;
        //根据用户名查询用户信息
        User user=userService.findByUsername(usernamePasswordToken.getUsername());
        if(user==null){
            /**用户名不存在
             * 参数一:期望登陆后,保存在Subject中信息
             * 参数二:如果返回为null 说明用户不存在,报用户名
             * 参数三:realm名称
             */
            return null;
        }else{
            /**用户名存在
             * 当返回用户名密码时,securityManager安全管理器,自动比较返回密码和用户输入密码是否一致
             * 如果密码一致 登录成功,如果密码不一致 报密码错误异常
             */
            System.out.println(user.getPassword()+"+++++++++++++++++++++++");
            System.out.println(usernamePasswordToken.getPassword()+"+++++++++++++++++++++++");
            return new SimpleAuthenticationInfo(user, user.getPassword(),getName());
        }
    }

}

28.权限控制 用户授权
一.修改applicationContext-shiro.xml配置shiroFilter权限过滤程序
/pages/base/courier.html*=perms[courier:list]
/pages/base/area.html*=roles[base]
二.实现Realm的授权方法
29.用户注销
一.修改页面退出系统的跳转路径 location.href=’./user_logout.action’
二.在UserAction提供logout方法Subject subject=SecurityUtils.getSubject();
Subject.logout();

    @Action(value="user_logout",results={@Result(name="success",type="redirect",location="login.html")})
    public String logout(){
        //基于shiro完成退出
        Subject subject=SecurityUtils.getSubject();
        subject.logout();
        return SUCCESS;
    }
    @Autowired
    private UserService userService;
    @Action(value="user_list",results={@Result(name="success",type="json")})
    public String list(){
        //调用业务层,返回用户列表
        List<User> users=userService.findAll();
        ActionContext.getContext().getValueStack().push(users);
        return SUCCESS;
    }

    //属性驱动
    private String [] roleIds;


    public void setRoleIds(String[] roleIds) {
        this.roleIds = roleIds;
    }
    @Action(value="user_save",results={@Result(name="success",type="redirect",location="pages/system/userlist.html")})
    public String save(){
        userService.saveUser(model,roleIds);
        return SUCCESS;
    }

30.授权数据管理:菜单管理
一.菜单列表显示
1.修改menu.html的datagrid的url属性

<script type="text/javascript">
            $(function(){
                $("#grid").datagrid({
                    toolbar : [
                        {
                            id : 'add',
                            text : '添加菜单',
                            iconCls : 'icon-add',
                            handler : function(){
                                location.href='menu_add.html';
                            }
                        }           
                    ],
                    url : '../../menu_list.action',
                    columns : [[
                      {
                          field : 'id',
                          title : '编号',
                          width : 200
                      },
                      {
                          field : 'name',
                          title : '名称',
                          width : 200
                      },  
                      {
                          field : 'description',
                          title : '描述',
                          width : 200
                      },  
                      {
                          field : 'priority',
                          title : '优先级',
                          width : 200
                      },  
                      {
                          field : 'page',
                          title : '路径',
                          width : 200
                      }
                    ]]
                });
            });
        </script>

2.编写MenuAction提供list查询所有菜单方法

二.菜单数据添加
1.检查页面表单元素是否与实体类匹配
2.在MenuAction添加save保存方法

@Action(value="menu_save",results={@Result(name="success",type="redirect",location="pages/system/menu.html")})
    public String save(){
        //调用业务层,保存菜单数据
        menuService.save(model);
        return SUCCESS;
    }

31.授权数据管理:权限管理
一.权限数据列表显示
1.修改permission.html的datagrid
2.编写PermissionAction添加list查询方法

@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class PermissionAction extends BaseAction<Permission> {

    @Autowired
    private PermissionService permissionService;
    @Action(value="permission_list",results={@Result(name="success",type="json")})
    public String list(){
        List<Permission> permissions=permissionService.findAll();
        ActionContext.getContext().getValueStack().push(permissions);
        return SUCCESS;
    }

二.权限数据添加
1.更改permiss_add.html的form表单的action

<script type="text/javascript">
            $(function(){
                // 点击保存
                $('#save').click(function(){
                    if($("#permissionForm").form('validate')){
                        $("#permissionForm").submit();
                    }
                });
            });
        </script>
    </head>

    <body class="easyui-layout">
        <div data-options="region:'north'">
            <div class="datagrid-toolbar">
                <a id="save" icon="icon-save" href="#" class="easyui-linkbutton" plain="true">保存</a>
            </div>
        </div>
        <div data-options="region:'center'">
            <form id="permissionForm" method="post" action="../../permission_save.action">
                <table class="table-edit" width="80%" align="center">
                    <tr class="title">
                        <td colspan="2">权限信息</td>
                    </tr>
                    <tr>
                        <td>名称</td>
                        <td>
                            <input type="text" name="name" class="easyui-validatebox" data-options="required:true" />
                        </td>
                    </tr>
                    <tr>
                        <td>关键字</td>
                        <td>
                            <input type="text" name="keyword" class="easyui-validatebox" data-options="required:true" /> 
                        </td>
                    </tr>
                    <tr>
                        <td>描述</td>
                        <td>
                            <textarea name="description" rows="4" cols="60"></textarea>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    </body>

2.服务器代码编写PermissionAction提供save方法

@Action(value="permission_save",results={@Result(name="success",type="redirect",location="pages/system/permission.html")})

    public String save(){
        permissionService.save(model);
        return SUCCESS;
    }

32.授权数据管理:角色管理
一.角色列表显示
1.修改role.html的datagrid的url属性

<script type="text/javascript">
            $(function(){
                // 数据表格属性
                $("#grid").datagrid({
                    toolbar : [
                        {
                            id : 'add',
                            text : '添加角色',
                            iconCls : 'icon-add',
                            handler : function(){
                                location.href='role_add.html';
                            }
                        }           
                    ],
                    url : '../../role_list.action',
                    columns : [[
                        {
                            field : 'id',
                            title : '编号',
                            width : 200
                        },
                        {
                            field : 'name',
                            title : '名称',
                            width : 200
                        }, 
                        {
                            field : 'keyword',
                            title : '关键字',
                            width : 200
                        }, 
                        {
                            field : 'description',
                            title : '描述',
                            width : 200
                        } 
                    ]]
                });
            });
        </script>

2.编写RoleAction添加list

@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class RoleAction extends BaseAction<Role> {

    @Autowired
    private RoleService roleService;

    @Action(value="role_list",results={@Result(name="success",type="json")})

    public String list(){
        //调用业务层,查询所有角色
        List<Role> roles=roleService.findAll();
        ActionContext.getContext().getValueStack().push(roles);
        return SUCCESS;
    }

二.角色数据添加
1.(permission数据)权限checkbox列表显示
2.(menu数据)显示菜单树形选择列表
①修改ztree获取数据的url属性
②修改menu实体类,添加返回pId方法,在json中生成pId属性

@Entity
@Table(name = "T_MENU")
public class Menu implements Serializable {
    @Id
    @GeneratedValue
    @Column(name = "C_ID")
    private int id;
    @Column(name = "C_NAME")
    private String name; // 菜单名称
    @Column(name = "C_PAGE")
    private String page; // 访问路径
    @Column(name = "C_PRIORITY")
    private Integer priority; // 优先级
    @Column(name = "C_DESCRIPTION")
    private String description; // 描述

    @ManyToMany(mappedBy = "menus")
    private Set<Role> roles = new HashSet<Role>(0);

    @OneToMany(mappedBy = "parentMenu")
    private Set<Menu> childrenMenus = new HashSet<Menu>();

    @ManyToOne
    @JoinColumn(name = "C_PID")
    private Menu parentMenu;

    @Transient
    // 在数据表不去生成数据列
    public Integer getpId() {
        if (parentMenu == null) {
            return 0;
        } else {
            return parentMenu.getId();
        }
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPage() {
        return page;
    }

    public void setPage(String page) {
        this.page = page;
    }

    public Integer getPriority() {
        return priority;
    }

    public void setPriority(Integer priority) {
        this.priority = priority;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @JSON(serialize = false)
    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    @JSON(serialize = false)
    public Set<Menu> getChildrenMenus() {
        return childrenMenus;
    }

    public void setChildrenMenus(Set<Menu> childrenMenus) {
        this.childrenMenus = childrenMenus;
    }

    public Menu getParentMenu() {
        return parentMenu;
    }

    public void setParentMenu(Menu parentMenu) {
        this.parentMenu = parentMenu;
    }

}

3..提交表单,为form指定action(并在页面添加隐藏字段存放勾选菜单的id,”,”分隔)
4.RoleAction提供save方法,完成角色添加、关联权限、关联菜单

    //属性驱动
    private String [] permissionIds;
    private String menuIds;


    public void setPermissionIds(String[] permissionIds) {
        this.permissionIds = permissionIds;
    }


    public void setMemuIds(String memuIds) {
        this.menuIds = memuIds;
    }


    @Action(value="role_save",results={@Result(name="success",type="redirect",location="pages/system/role.html")})
    public String save(){
        //调用业务层,保存角色
        roleService.saveRole(model,permissionIds,menuIds);
        return SUCCESS;
    }
}

33.授权数据管理:用户管理
一.用户数据列表显示
1.在userlist.html修改datagrid的url

$(function(){
                // 初始化 datagrid
                // 创建grid
                $('#grid').datagrid( {
                    iconCls : 'icon-forward',
                    fit : true,
                    border : false,
                    rownumbers : true,
                    striped : true,
                    toolbar : toolbar,
                    url : "../../user_list.action",
                    idField : 'id', 
                    frozenColumns : frozenColumns,
                    columns : columns,
                    onClickRow : onClickRow,
                    onDblClickRow : doDblClickRow
                });

                $("body").css({visibility:"visible"});

            });
            // 双击
            function doDblClickRow(rowIndex, rowData) {
                var items = $('#grid').datagrid('selectRow',rowIndex);
                doView();
            }
            // 单击
            function onClickRow(rowIndex){

            }

            function doAdd() {
                location.href="../../pages/system/userinfo.html";
            }

            function doView() {
                var item = $('#grid').datagrid('getSelected');
                console.info(item);
                //window.location.href = "edit.html";
            }

            function doDelete() {
                var ids = [];
                var items = $('#grid').datagrid('getSelections');
                for(var i=0; i<items.length; i++){
                    ids.push(items[i].id);      
                }

                console.info(ids.join(","));

                $('#grid').datagrid('reload');
                $('#grid').datagrid('uncheckAll');
            }
        </script>

2.在UserAction提供list查询方法

@Autowired
    private UserService userService;
    @Action(value="user_list",results={@Result(name="success",type="json")})
    public String list(){
        //调用业务层,返回用户列表
        List<User> users=userService.findAll();
        ActionContext.getContext().getValueStack().push(users);
        return SUCCESS;
    }

二.用户添加功能
1.角色checkbox列表显示 在显示角色列表元素添加id=”roleTD”
2.编写form属性,提交表单
3.在UserAction中提供save方法
34.动态菜单功能
一。修改index.html 加载基本菜单url路径
二.在MenuAction中提供showMenu方法

@Action(value="menu_showmenu",results={@Result(name="success",type="json")})
    public String showMenu(){
        //调用业务层,查询当前用户具有菜单列表
        Subject subject =SecurityUtils.getSubject();
        User user=(User) subject.getPrincipal();

        List<Menu> menus=menuService.findByUser(user);

        ActionContext.getContext().getValueStack().push(menus);
        return SUCCESS;
    }

35.✔★Ehcache 对普通业务数据进行缓存
一.在common_parent导入ehcache maven的坐标

<!-- 缓存 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.11</version>
        </dependency>

二.使用ehcache,导入ehcache.xml

    <Cache name="bos"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </Cache>
    <Cache name="standard"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </Cache>

三.配置spring整合ehcache 将ehcacheManager交给spring管理
四.配置spring缓存管理器,封装ehcache自带CacheManager
五.在applicationContext-cache.xml引入cach名称空间
六.激活spring缓存注解

 <!-- 缓存配置  -->
    <bean id="ehCacheManager" 
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml" />
    </bean>

    <!-- shiro封装cacheManager -->
    <bean id="shiroCacheManager" 
        class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManager" ref="ehCacheManager" />
    </bean>

    <!-- spring 封装ehcache缓存管理器  -->
    <bean id="springCacheManager" 
        class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehCacheManager" />
    </bean>

    <!-- 激活spring 缓存注解 -->
    <cache:annotation-driven cache-manager="springCacheManager"/>

七.在被spring管理bean对象方法上使用@Cacheable @CacheEvict
standardServiceImpl

public class StandardServiceImpl implements StandardService {

    // 注入DAO
    @Autowired
    private StandardRepository standardRepository;

    @Override
    @CacheEvict(value="standard",allEntries=true)
    public void save(Standard standard) {
        standardRepository.save(standard);
    }


    @Override
    @Cacheable("standard")
    public List<Standard> findAll() {
        return standardRepository.findAll();
    }

八.对有参数的方法对其结果数据进行缓存

@Override
    @Cacheable(value="standard",key="#pageable.pageNumber+'_'+#pageable.pageSize")
    public Page<Standard> findPageData(Pageable pageable) {
        return standardRepository.findAll(pageable);
    }

第四阶段
36.中转业务模块:运单管理–开启中转配送
一.在waybill_manager.html添加按钮“中转配送”
二.通过js对按钮添加点击事件(将选中的运单的id,提交给服务器)

        <script type="text/javascript">
        $.fn.serializeJson=function(){  
            var serializeObj={};  
            var array=this.serializeArray();  
            var str=this.serialize();  
            $(array).each(function(){  
                if(serializeObj[this.name]){  
                    if($.isArray(serializeObj[this.name])){  
                        serializeObj[this.name].push(this.value);  
                    }else{  
                        serializeObj[this.name]=[serializeObj[this.name],this.value];
                    }  
                }else{  
                    serializeObj[this.name]=this.value;   
                }  
            });  
            return serializeObj;  
        }; 
            function doSearch() {
                // 将form内容,转换为json 
                var queryParams = $("#searchForm").serializeJson();

                // 绑定数据表格 重新加载数据 
                $('#tt').datagrid('load', queryParams);
            }

            $(function(){
                // 开启运输管理 按钮
                $("#transitBtn").click(function(){
                    // 获取数据表格所有选中行 
                    var rows = $("#tt").datagrid('getSelections');
                    if(rows.length == 0){
                        // 没有选中
                        $.messager.alert('警告','必须选中运单信息','warning');
                    }else{
                        // 选中运单 ,获取选中运单id 
                        var array = new Array();
                        for(var i=0; i<rows.length; i++){
                            array.push(rows[i].id);
                        }
                        var wayBillIds = array.join(",");
                        $.post("../../transit_create.action",{wayBillIds: wayBillIds}, function(data){
                            // 返回值 {success:true, msg: ...}
                            if(data.success == true){
                                $.messager.show({
                                    title:'操作成功',
                                    msg: data.msg
                                });
                            }else{
                                $.messager.show({
                                    title:'操作失败',
                                    msg: data.msg
                                });
                            }
                        });
                    }
                });

三.服务器代码编写TransitInfoAction,提供create方法

@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class TransitInfoAction extends BaseAction<TransitInfo>{

    @Autowired
    private TransitInfoService transitInfoService;

    //属性驱动
    private String  wayBillIds;


    public void setWayBillIds(String wayBillIds) {
        this.wayBillIds = wayBillIds;
    }


    @Action(value="transit_create",results={@Result(name="success",type="json")})
    public String create(){
        //调用业务层,保存transitInfo信息
        Map<String,Object> result=new  HashMap<>();
        try {
            transitInfoService.createTransits(wayBillIds);
            //成功
            result.put("success", true);
            result.put("msg", "开启中转配送成功");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            //失败
            result.put("success", false);
            result.put("msg", "开启中转配送失败,异常:"+e.getMessage());
        }
        ActionContext.getContext().getValueStack().push(result);
        return SUCCESS;


    }

}

37.中转业务模块:运输配送管理:运输地图显示
一.在显示地图的位置添加div(class=”easyui-window”)

<div class="easyui-window" title="运输路径查看" id="transitPathWindow" modal="true" closed="true" collapsible="false" minimizable="false" maximizable="false" style="top:20px;left:100px;width: 800px; height: 400px">
            <div id="allmap"></div>
        </div>

        <div class="easyui-window" title="实时配送路径" id="deliveryInTimePathWindow" modal="true" closed="true" collapsible="false" minimizable="false" maximizable="false" style="top:20px;left:100px;width: 800px; height:400px">
        </div>

二.编写显示地图的方法:(注意:引入百度地图src=”地址&ak”)

<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=4IU3oIAMpZhfWZsMu7xzqBBAf6vMHcoa"></script>
// 显示地图
                        // 百度地图API功能
                        var map = new BMap.Map("allmap");
                        map.centerAndZoom('北京', 11);
                        map.enableScrollWheelZoom(true);
                        //三种驾车策略:最少时间,最短距离,避开高速
                        var routePolicy = [BMAP_DRIVING_POLICY_LEAST_TIME, BMAP_DRIVING_POLICY_LEAST_DISTANCE, BMAP_DRIVING_POLICY_AVOID_HIGHWAYS];
                        var start = row.wayBill.sendAddress;
                        var end = row.wayBill.recAddress;
                        // 获取driveringRoute 
                        var driving = new BMap.DrivingRoute(map, {renderOptions:{map: map, autoViewport: true},policy: routePolicy[0]});

                        // 给id为2路径 添加途经点  北京-上海 
                        if(row.id == "2"){
                            // 显示路径  带途经点
                            driving.search(start,end, {waypoints:['南京夫子庙']});
                        }else{
                            // 显示路径 
                            driving.search(start,end);
                        }

                        // 弹出窗口
                        $("#transitPathWindow").window('open');
                    }
                }, {
                    id: 'button-path',
                    text: '实时配送路径',
                    iconCls: 'icon-search',
                    handler: function() {
                        $("#deliveryInTimePathWindow").window('open');
                    }
                }];

38.中转业务模块:运输配送信息列表查询
一.修改transitinfo.html的datagrid的url地址
二.服务器代码编写TransitInfoAction提供pageQuery方法

@Action(value = "transit_pageQuery", results = { @Result(name = "success", type = "json") })
    public String pageQuery() {
        // 分页查询
        Pageable pageable = new PageRequest(page - 1, rows);
        // 调用业务层 查询分页数据
        Page<TransitInfo> pageData = transitInfoService.findPageData(pageable);

        // 压入值栈返回
        pushPageDataToValueStack(pageData);
        return SUCCESS;
    }

业务层同步索引库

@Service
@Transactional
public class TransitInfoServiceImpl implements TransitInfoService {

    @Autowired
    private WayBillRepository wayBillRepository;

    @Autowired
    private TransitInfoRepository transitInfoRepository;

    @Autowired
    private WayBillIndexRepository wayBillIndexRepository;

    @Override
    public void createTransits(String wayBillIds) {
        if (StringUtils.isNotBlank(wayBillIds)) {
            // 处理运单
            for (String wayBillId : wayBillIds.split(",")) {
                WayBill wayBill = wayBillRepository.findOne(Integer
                        .parseInt(wayBillId));
                // 判断运单状态是否为待发货
                if (wayBill.getSignStatus() == 1) {
                    // 待发货
                    // 生成TransitInfo信息
                    TransitInfo transitInfo = new TransitInfo();
                    transitInfo.setWayBill(wayBill);
                    transitInfo.setStatus("出入库中转");
                    transitInfoRepository.save(transitInfo);

                    // 更改运单状态
                    wayBill.setSignStatus(2); // 派送中

                    // 同步索引库
                    wayBillIndexRepository.save(wayBill);
                }
            }
        }
    }

    @Override
    public Page<TransitInfo> findPageData(Pageable pageable) {
        return transitInfoRepository.findAll(pageable);
    }

}

39.出入库操作:在弹出窗口中显示 运单配送信息
一.前端代码编写回显方法row.wayBill.属性


                        // 判断运输状态 ,是否为开始配送 
                        if(row.status == "开始配送"){
                            // 在表单隐藏域保存 当前操作 中转信息 
                            $("#signId").val(row.id);

                            // 回显运输配送信息 
                            $("#signTransitInfoView").append("运单号:" + row.wayBill.wayBillNum+"<br/>");
                            $("#signTransitInfoView").append("货物类型:" + row.wayBill.goodsType+"<br/>");
                            $("#signTransitInfoView").append("发货地:" + row.wayBill.sendAddress+"<br/>");
                            $("#signTransitInfoView").append("收货地:" + row.wayBill.recAddress+"<br/>");
                            $("#signTransitInfoView").append("物流信息:" + row.transferInfo+"<br/>");


                            // 弹出窗口 
                            $("#signWindow").window('open');
                        }else{
                            $.messager.alert('警告','签收录入操作只能针对开始配送状态运单','warning');
                            return ;
                        }

40.出入库操作:提交表单,在服务器保存关联出入库信息
一.为出入库表单,指定Action
二.给保存按钮添加点击事件,提交表单到服务器
三.服务器代码编写InOutStorageAction提供save方法

// 出入库操作 
@Namespace("/")
@ParentPackage("json-default")
@Controller
@Scope("prototype")
public class InOutStorageInfoAction extends BaseAction<InOutStorageInfo> {

    @Autowired
    private InOutStorageInfoService inOutStorageInfoService;

    private String transitInfoId ; 

    public void setTransitInfoId(String transitInfoId) {
        this.transitInfoId = transitInfoId;
    }


    @Action(value = "inoutstore_save", results = { 
            @Result(name = "success", type = "redirect", location = "pages/transit/transitinfo.html") })
    public String save() {
        inOutStorageInfoService.save(transitInfoId, model);
        return SUCCESS;
    }

}

41.开始配送功能
思路:选中一条运单信息,点击开始配送按钮,弹出窗口(只针对到达网点运单)
一.为form指定action 给保存按钮添加事件
二.服务器代码编写DeliveryInfoAction的save

@Namespace("/")
@ParentPackage("json-default")
@Controller
@Scope("prototype")
public class DeliveryInfoAction extends BaseAction<DeliveryInfo> {

    @Autowired
    private DeliveryInfoService deliveryInfoService;

    private String transitInfoId;

    public void setTransitInfoId(String transitInfoId) {
        this.transitInfoId = transitInfoId;
    }

    @Action(value = "delivery_save", results = { @Result(name = "success", type = "redirect", location = "pages/transit/transitinfo.html") })
    public String save() {
        deliveryInfoService.save(transitInfoId, model);
        return SUCCESS;
    }

}

42.✔★签收录入功能
一.对状态为”开始配送“的运单,弹出签收录入窗口
二.为页面form添加action,绑定提交事件
三.服务器代码编写SignlnfoAction提供save方法

@Namespace("/")
@ParentPackage("json-default")
@Controller
@Scope("prototype")
public class SignInfoAction extends BaseAction<SignInfo> {

    @Autowired
    private SignInfoService signInfoService;

    private String transitInfoId;

    public void setTransitInfoId(String transitInfoId) {
        this.transitInfoId = transitInfoId;
    }

    @Action(value = "sign_save", results = { @Result(name = "success", type = "redirect", location = "pages/transit/transitinfo.html") })
    public String save() {
        signInfoService.save(transitInfoId, model);
        return SUCCESS;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_17044239/article/details/81572389