<!DOCTYPE html>
<html dir="ltr" class="js desktop" lang="zh"><head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 <meta charset="utf-8">
 <title></title>
 <script>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})(document.documentElement)</script>
 <meta name="generator" content="DokuWiki">
 <meta name="robots" content="noindex,nofollow">
 <meta name="keywords" content="product,develop">
 <link rel="stylesheet" type="text/css" href="files/css.css">
 <script type="text/javascript"> </script>
 <script type="text/javascript" charset="utf-8" src="files/js.js"></script>
 <meta name="viewport" content="width=device-width,initial-scale=1">
</head><body>
<!--[if lte IE 7 ]><div id="IE7"><![endif]--><!--[if IE 8 ]><div id="IE8"><![endif]-->
<div id="dokuwiki__site"><div id="dokuwiki__top" class="site dokuwiki mode_show tpl_dokuwiki "> <div class="wrapper group">
 <!-- ********** CONTENT ********** -->
 <div id="dokuwiki__content"><div class="pad group"> <div class="pageId"><span>product:develop</span></div>
 <div class="page group">
 <!-- wikipage start -->
 <!-- TOC START -->
 <div id="dw__toc">
 <div style=""> <ul class="toc" aria-expanded="true" style="">
 <li class="level1"><div class="li"><a href="#KstoreBBC商城系统开发手册">KstoreV2 BBC多用户商城系统开发手册</a></div>
 <ul class="toc">
 <li class="level2"><div class="li"><a href="#简介">简介</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#需要的基础知识">需要的基础知识</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#模块及目录结构">模块及目录结构</a></div></li>
 <li class="level2"><div class="li"><a href="#开发基础">开发基础</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#Model开发">Model开发</a></div></li>
 <li class="level3"><div class="li"><a href="#Controller开发">Controller开发</a></div></li>
 <li class="level3"><div class="li"><a href="#service开发">Service开发</a></div></li>
 <li class="level3"><div class="li"><a href="#dao开发">mapper开发</a></div></li>
 <li class="level3"><div class="li"><a href="#模板开发">模板开发</a></div></li>
 <li class="level3"><div class="li"><a href="#Marketing模块前端开发">Marketing模块前端开发</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#异常处理">异常处理</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#异常处理规范">异常处理规范</a></div></li>
 <li class="level3"><div class="li"><a href="#建立自定义异常类">建立自定义异常类</a></div></li> </ul>
 </li>
 <li class="level2"><div class="li"><a href="#安全">安全</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#安全规范">安全规范</a></div></li>
 <li class="level3"><div class="li"><a href="#防范sql注入">防SQL注入</a></div></li>
 <li class="level3"><div class="li"><a href="#防范xss">防范XSS</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#菜单_权限">菜单&权限</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#权限体系">权限体系</a></div></li>
 <li class="level3"><div class="li"><a href="#菜单权限相关表">菜单权限相关表</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#消息队列">消息队列</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#使用方法">使用方法</a></div></li>
 <li class="level3"><div class="li"><a href="#范例">范例</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#缓存">缓存</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#使用方法1">使用方法</a></div></li>
 <li class="level3"><div class="li"><a href="#范例1">范例</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#验证">验证</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#后端验证">后端验证</a></div></li>
 <li class="level3"><div class="li"><a href="#前端验证">前端验证</a></div></li>
 <li class="level3"><div class="li"><a href="#admin模块前端验证">Admin模块前端验证</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#操作日志">操作日志</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#系统日志模块">系统日志模块</a></div></li>
 <li class="level3"><div class="li"><a href="#运行后台日志模块">运行后台日志模块</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#注意事项">注意事项</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#UrlRewriteFilter">UrlRewriteFilter</a></div></li> <li class="level3"><div class="li"><a href="#CSRFToken">CSRFToken</a></div></li>
 <li class="level3"><div class="li"><a href="#WeiXinUtil">常用工具类</a></div></li> </ul>
 </li>
 <li class="level2"><div class="li"><a href="#完整开发范例">完整开发范例</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#确定需求">确定需求</a></div></li>
 <li class="level3"><div class="li"><a href="#创建表结构">创建表结构</a></div></li>
 <li class="level3"><div class="li"><a href="#添加功能菜单">添加功能菜单</a></div></li>
 <li class="level3"><div class="li"><a href="#配置权限">菜单模块</a></div></li>
 <li class="level3"><div class="li"><a href="#Model开发1">Model开发</a></div></li>
 <li class="level3"><div class="li"><a href="#dao开发1">Example开发</a></div></li>
 <li class="level3"><div class="li"><a href="#push模块">push模块</a></div></li>
 <li class="level3"><div class="li"><a href="#seller模块3">Service模块</a></div></li>
 </ul>
 </li>
 <li class="level2"><div class="li"><a href="#编码规范">编码规范</a></div>
 <ul class="toc">
 <li class="level3"><div class="li"><a href="#约定">约定</a></div></li>
 <li class="level3"><div class="li"><a href="#url规范">URL规范</a></div></li>
 <li class="level3"><div class="li"><a href="#模板规范">模板规范</a></div></li>
 <li class="level3"><div class="li"><a href="#命名">命名</a></div></li>
 <li class="level3"><div class="li"><a href="#注释">注释</a></div></li>
 <li class="level3"><div class="li"><a href="#数据库">数据库</a></div></li>
 <li class="level3"><div class="li"><a href="#日志">日志</a></div></li>
 </ul></li>
 </ul></li>
 </ul>
 </div>
 </div>
 <!-- TOC END --> <h1 class="sectionedit1" id="KstoreBBC商城系统开发手册">KStore_V2bbc多用户商城系统开发手册</h1>
 <div class="level1"> </div>
 <h2 class="sectionedit2" id="简介">简介</h2>
 <div class="level2"> <p>
 本手册用于开发人员熟悉<span class="sectionedit1">KStore_V2bbc,</span>多用户商城系统的开发工作,对开发中涉及的问题进行了全面的介绍,最后用一个完整范例介绍了功能整体开发流程。本手册面对有实际开发经验的开发人员,开发人员应该具备所需的基础知识。</p> </div>
 <h3 class="sectionedit3" id="需要的基础知识">需要的基础知识</h3>
 <div class="level3">
 <ul>
 <li class="level1">
 <div class="li"> 熟悉Java8开发语言</div>
 </li>
 <li class="level1"><div class="li"> 熟练使用Spring4、Spring4 MVC、Mybatis3等开发框架</div>
 </li>
 <li class="level1"><div class="li"> 熟练使用redis3.2.8、elasticsearch1.7.1、activeMQ5.14等应用框架</div>
 </li>
 <li class="level1"><div class="li"> 熟练使用HTML、CSS、JavaScript、jQuery1.9、Bootstrap3.0、Vue.js2.0等前端框架</div>
 </li>
 <li class="level1"><div class="li"> 熟练使用IDEA,Eclipse等开发工具</div>
 </li>
 <li class="level1"><div class="li"> 熟练使用Maven3.2构建编译操作</div>
 </li>
 <li class="level1"><div class="li"> 熟练使用Tomcat8等应用服务器的环境配置</div>
 </li>
 <li class="level1"><div class="li"> 熟练使用mysql5.6</div>
 </li>
 <li class="level1"><div class="li"> 熟练常用linux命令</div>
 </li>
 </ul> </div>
 <h2 class="sectionedit4" id="模块及目录结构">模块及目录结构</h2>
 <div class="level2">
<pre class="file">
├── KstoreBBC商城系统
 ├─
 ├─dbmigrate(数据库变更记录)
 │ ├─2017-02-13推广达人
 │ ├─2017-05-10拼团营销
 │ ├─2017-07-18微信模板消息
 │ ├─2017-08-02物流模板
 │ ├─2017-08-22增税发票
 │ ├─2017-08-30通用优惠券
 │ ├─2017-10-09订单列表性能优化
 │ ├─2017-10-16小能客服
 │ ├─2017-10-18 app 推送
 │ ├─2017-9-26 商品状态
 │
 ├─kstore_app_push(app应用内消息为例)
 │ ├─src
 │ ├─main
 │ ├─java
 │ │ └─com
 │ │ └─qianmi
 │ │ └─push
 │ │ ├─bean(实体类,数据库表对象)
 │ │ ├─constant(实体类,数据库表对象)
 │ │ ├─mapper(mabatis接口)
 │ │ ├─mq(activeMq处理器)
 │ │ ├─service(业务服务类及实现)
 │ │
 │ └─resources(mybatis配置文件)
 │
 ├─kstore_bitanswer(比特安索授权)
 ├─kstore_boss_third_common(boss与第三方共用)
 ├─kstore_code_tool(代码生成工具)
 ├─kstore_common(系统公共)
 ├─kstore_core(核心类包maven配置)
 ├─kstore_custom(用户模块)
 ├─kstore_gift(商品模块)
 ├─kstore_goods(商品模块)
 ├─kstore_goods_platform(商品与elasticsearch)
 ├─kstore_information(PC端站点资讯模块)
 ├─kstore_io_cfg(activeMQ配置)
 ├─kstore_io_consumer(activeMQ消费端)
 ├─kstore_io_producer(activeMQ生产端)
 ├─kstore_jd_oss(oss管理端)
 ├─kstore_marketing(营销模块)
 ├─kstore_message(微信模板消息模块配置)
 ├─kstore_mobile_site(H5端站点)
 ├─kstore_newboss_site(BOSS平台管理端站点为例)
 │ ├─java
 │ │ └─com
 │ │ └─ningpai
 │ │ ├─app(APP站点配置相关)
 │ │ ├─appOpen(APP下载)
 │ │ ├─appPush(app推送消息)
 │ │ ├─backOrder(退单)
 │ │ ├─common(公共)
 │ │ ├─controlpanel(控制面板)
 │ │ ├─elasticsearch(搜索引擎)
 │ │ ├─goods(商品)
 │ │ ├─invoice(发票)
 │ │ ├─jdpage(京东商品抓取)
 │ │ ├─order(订单)
 │ │ ├─qmMarketing(营销)
 │ │ ├─qmPromotioner(推广达人)
 │ │ ├─scheduling(计划)
 │ │ ├─task(定时任务)
 │ │ ├─third(第三方店铺)
 │ │ ├─tool(工具)
 │ │ └─weixin(微信)
 │ ├─resources(资源配置文件)
 │ │ │ acp_sdk(production).properties
 │ │ │ acp_sdk.properties
 │ │ │ main2012.dic
 │ │ │ quantifier.dic
 │ │ │ wechat.properties
 │ │ │
 │ │ ├─com
 │ │ │ └─ningpai
 │ │ │ └─web
 │ │ │ ├─config(spring,es,redis等应用配置文件)
 │ │ │ │ aliPayOrderQuery.properties
 │ │ │ │ amq.properties
 │ │ │ │ ApplicationContext.xml
 │ │ │ │ controltask.properties
 │ │ │ │ copyright.properties
 │ │ │ │ dispatcher-servlet.xml
 │ │ │ │ es-hosts.properties
 │ │ │ │ feedbackEmail.properties
 │ │ │ │ jdbc.properties
 │ │ │ │ log4j.properties
 │ │ │ │ redis.properties
 │ │ │ │ spring-jcaptcha.xml
 │ │ │ │ upload.properties
 │ │ │ │ upyun.properties
 │ │ │ │
 │ │ │ └─mybatis
 │ │ │ SqlMapConfig.xml
 │ │ │
 │ │ └─META-INF
 │ │ app.properties
 │ │
 │ └─webapp
 │ │ ap.kuaidi100.doc
 │ │ ap.kuaidi100公司代码.doc
 │ │
 │ ├─css(样式表)
 │ ├─docs (文档)
 │ ├─fonts (字体)
 │ ├─iconfont (icon)
 │ ├─images(图片)
 │ ├─js (js脚本)
 │ ├─jsp (jsp文件)
 │ └─WEB-INF (web描述符)
 │ │ web.xml
 │
 ├─kstore_newthird_site(商家端站点)
 ├─kstore_order(订单模块)
 ├─kstore_order_query_si(集成查询接口,用于对接第三方支付的同步查询接口)
 ├─kstore_promotioner(推广达人,分销系统)
 ├─kstore_searchplatform(elasticsearch接口)
 ├─kstore_site_common(PC端与H5站点共用)
 ├─kstore_system(系统模块)
 ├─kstore_tax_invoice(系统模块)
 ├─kstore_templet(营销模块)
 ├─kstore_wx(微信支付与登录相关)
 └─kstore_wx_tmp_msg(微信模板消息处理器模块)
</pre> </div>
 <h2 class="sectionedit5" id="开发基础">开发基础</h2>
 <div class="level2"> </div>
 <h3 class="sectionedit6" id="Model开发">Model开发</h3>
 <div class="level3"> <p>
 Model主要用来描述实体模型,通过mybatis实现和数据库表的映射关系
 </p> </div>
 <h4 id="注解说明">注解说明</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> 后台验证相关注解,具体参考<span class="curid"><a href="http://192.168.1.230/wiki/doku.php?id=product:develop#%E5%90%8E%E5%8F%B0%E9%AA%8C%E8%AF%81" class="wikilink1" title="product:develop">后台验证</a></span></div>
 </li>
 </ul> </div>
 <h4 id="Model范例">Model范例</h4>
 <div class="level4">
<pre class="code java">
/**
 * app 推送信息配置
 */
@Data //lombok注解
public class AppPushCfg implements Serializable { private static final long serialVersionUID = 1L;
 /**
 * id
 */
 private Integer pushId; /**
 * 推送名称
 */
 private String pushName; /**
 * 平台类型(1:android, 2:ios)
 */
 private Short osType; /**
 * 应用唯一标识
 */
 private String appKey; /**
 * 服务器秘钥
 */
 private String appMasterSecret; /**
 * app Umeng Message Secre
 */
 private String umengMessageSecret; /**
 * 是否启用(1启用,0不启用)
 */
 private Short isEnable; /**
 * 修改人
 */
 private Long userId; /**
 * 变动时间
 */
 private Date updateTime;}
</pre> </div>
 <h3 class="sectionedit7" id="Controller开发">Controller开发</h3>
 <div class="level3"> <p>
 Controller为功能入口View层行为,Controller继承
 BaseController,Controller类用于返回模板或JSON
 数据的请求。比如开发一个用户功能的CustomerController继承BaseController用于处理模板页面请求与Restful请求。
 </p> </div>
 <h4 id="注解说明1">注解说明</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> @Controller 定义为Controller</div>
 </li>
 <li class="level1"><div class="li"> @Autowired 自动装配</div>
 </li>
 <li class="level1"><div class="li"> @RequestMapping 定义请求映射的url</div>
 </li>
 <li class="level1"><div class="li"> @ResponseBody 返回JSON格式数据</div>
 </li>
 </ul> </div>
 <h4 id="Controller范例">Controller范例</h4>
 <div class="level4"><pre class="code java">
 <xmp>
/**
 * app push
 *
 * @author OF2772 luocz
 * @date 2017/10/17
 */
@Controller
@RequestMapping("/appPush")
public class AppPushController extends BaseController { @Autowired
 private AppPushService appPushService; /**
 * push 配置页面
 *
 * @return
 */
 @RequestMapping("/pusCfgIndex")
 public String pusCfgIndex(ModelMap map) {
 map.put("isAppPushCfg", appPushService.isAppPushCfg(AppPushConstant.UMENG_PUSH_ID));
 return "/jsp/apppush/push_cfg";
 } /**
 * 根据id查询对应的配置
 *
 * @return
 */
 @RequestMapping("/queryAppPushCfg")
 @ResponseBody
 public ResultMsg<AppPushCfgVo> queryAppPushCfg(int pushId) { ResultMsg<AppPushCfgVo> resultMsg = new ResultMsg<>();
 resultMsg.setCode(ResultMsg.SUCCESS);
 resultMsg.setContext(appPushService.queryAppPushCfgById(pushId));
 return resultMsg;
 }
} </xmp>
</pre> </div>
 <h3 class="sectionedit8" id="service开发">Service开发</h3>
 <div class="level3"> <p>
 Service用于实现业务逻辑。
 </p> </div>
 <h4 id="注解说明2">注解说明</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> @Service 定义为Service</div>
 </li>
 <li class="level1"><div class="li"> @Transactional 定义事物回滚</div>
 </li>
 <li class="level1"><div class="li"> @Autowired 自动注入依赖的类</div>
 </li>
 </ul> </div>
 <h4 id="service范例">Service范例</h4>
 <div class="level4">
<pre class="code java">
<xmp>
/**
 * 发票配置开关
 */
@Service
@Transactional(rollbackFor = {Exception.class})
public class TaxInvoiceCfgService { @Autowired
 TaxInvoiceCfgMapper taxInvoiceCfgMapper; /**
 * 创建
 * @param cfg
 * @return
 */
 public int create(TaxInvoiceCfg cfg) {
 return taxInvoiceCfgMapper.create(cfg);
 } /**
 * 获取
 *
 * @return
 */
 public TaxInvoiceCfg getConfig() {
 return taxInvoiceCfgMapper.getConfig();
 } /**
 * 更新
 *
 * @param cfg
 * @return
 */
 public int updateConfig(TaxInvoiceCfg cfg) {
 return taxInvoiceCfgMapper.updateConfig(cfg);
 }
}</xmp>
</pre> </div>
 <h3 class="sectionedit9" id="dao开发">Mapper开发</h3>
 <div class="level3"> <p>
 Mapper用于实现具体的SQL语句,需要与mabatis配置文件一一对应。
 </p> </div>
 <h4 id="注解说明3">注解说明</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> @Repository 定义为Mapper</div>
 </li>
 <li class="level1"><div class="li"> @Transactional 定义事物回滚</div>
 </li>
 <li class="level1"><div class="li"> @Autowired 自动注入依赖的类</div>
 </li>
 </ul> </div>
 <h4 id="service范例1">Service范例</h4>
 <div class="level4">
<pre class="code java">
/**
 * 发票配置开关
 */
@Service
@Transactional(rollbackFor = {Exception.class})
public class TaxInvoiceCfgService { @Autowired
 TaxInvoiceCfgMapper taxInvoiceCfgMapper; /**
 * 创建
 * @param cfg
 * @return
 */
 public int create(TaxInvoiceCfg cfg) {
 return taxInvoiceCfgMapper.create(cfg);
 } /**
 * 获取
 *
 * @return
 */
 public TaxInvoiceCfg getConfig() {
 return taxInvoiceCfgMapper.getConfig();
 } /**
 * 更新
 *
 * @param cfg
 * @return
 */
 public int updateConfig(TaxInvoiceCfg cfg) {
 return taxInvoiceCfgMapper.updateConfig(cfg);
 }
}</pre>
 </div>
 <h3 class="sectionedit10" id="模板开发">模板开发</h3>
 <div class="level3"> </div>
 <h4 id="freemarker">freemarker</h4>
 <div class="level4"> <p>
 FreeMarker是一个基于Java的通用模板引擎,拥有丰富的功能和内置函数,可以参考<a href="https://freemarker.apache.org/docs/index.html" class="urlextern" title="https://freemarker.apache.org/docs/index.html" rel="nofollow">FreeMarker官方文档</a>。
 </p> </div>
 <h4 id="layout">Layout</h4>
 <div class="level4"> <p>
 每个模块都会根据页面布局定义一个或多个layout用于实现页面的通用部分,每个具体的页面只需要引入相应的layout即可实现页面的通用部分。
 </p>
 <pre class="code html4strict">
<xmp>
<head>
 <#assign basePath=request.contextPath>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="renderer" content="webkit">
 <title>创建营销</title>
 <#include "../common/common-head.ftl">
 <@vueRequired/>
 <link href="${basePath}/css/qm-marketing/marketing.css" rel="stylesheet">
 <style type="text/css">
 label.error { display:block; }
 </style>
</head>
<body>
<#--引入头部-->
<#include "../index/indextop.ftl"><div class="wp" id="create-marketing" v-cloak>
 <!-- 引用头 -->
 <div class="ui-sidebar">
 <div class="sidebar-nav">
 <#import "../index/indexleft.ftl" as leftmenu>
 <@leftmenu.leftmenu '${basePath}' />
 </div>
 </div>
</xmp>
</pre> </div>
 <h3 class="sectionedit11" id="Marketing模块前端开发">Marketing模块前端开发</h3>
 <div class="level3"> <p>
 Marketing模块前端基于Bootstrap扩展开发,VUE前端技术。
 </p> </div>
 <h4 id="列表">列表</h4>
 <div class="level4"> <p>
 marketing模块的列表实现比较方便,只需要在接口中返回json,前台用JS就可以自由定义列表的显示。
 </p>
 <pre class="code java">
 <xmp>
/**
 * 营销分页列表
 *
 * @param form 条件分页
 * @return
 */
 @RequestMapping("/pageFreeDelivery")
 @SystemControllerLog(description = "包邮分页查询")
 @ResponseBody
 public ResultMsg<Page<MarketingFreeDelivery>> pageFreeDelivery(MarketingFreeDeliveryForm form) {
 try {
 form.setDelFlag(Constants.DEL_FLAG_NO);
 form.setIsThird(Constants.YES);
 form.setThirdId(super.getThirdId());
 form.setOrderBy("B.begin_time desc , B.marketing_id desc");
 return success(marketingFreeDeliveryService.pageMarketingFullReduction(form));
 } catch (Exception e) {
 LOGGER.error("包邮分页查询异常", e);
 }
 return error();
 }
 </xmp>
</pre>
 <pre class="code html4strict">
<xmp>
<head>
 <#assign basePath=request.contextPath>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="renderer" content="webkit">
 <title>包邮营销列表</title>
 <#include "../../common/common-head.ftl">
 <@dateRequired/>
 <@validationRequired/>
 <@zTreeRequired/>
 <@vueRequired/>
 <link href="${basePath}/css/qm-marketing/marketing.css" rel="stylesheet">
 <style type="text/css">
 label.error { display:block; }
 </style>
</head>
<body>
<#--引入头部-->
<#include "../../index/indextop.ftl">
<div class="wp" id="webapp" v-cloak>
 <!-- 引用头 -->
 <div class="ui-sidebar">
 <div class="sidebar-nav">
 <#import "../../index/indexleft.ftl" as leftmenu>
 <@leftmenu.leftmenu '${basePath}' />
 </div>
 </div>
</xmp>
</pre> <p>
 <img src="files/icon_exclaim.gif" class="icon" alt=":!:">vue结合后端数据接口展示列表
 </p>
 <pre class="code javascript">
 <xmp>
 <table class="table table-bordered orders-table">
 <thead>
 <tr>
 <th>活动名称</th>
 <th>活动类型</th>
 <th>状态</th>
 <th>开始时间</th>
 <th>结束时间</th>
 <th>操作</th>
 </tr>
 </thead>
 <tbody v-if="page.context.rows > 0">
 <tr v-for="item in page.context.list">
 <td>{{ item.others.marketingName }}</td>
 <td>{{ getFreeDeliveryTypes(item.freeDeliveryType) }}</td>
 <td>{{ getStatus(item.others.activityStatus) }}</td>
 <td>{{ item.others.beginTime | moment}}</td>
 <td>{{ item.others.endTime | moment }}</td>
 </tr>
 </tbody>
 <table>
 </xmp>
</pre> </div>
 <h4 id="ajax表单提交">AJAX表单提交</h4>
 <h5 id="使用范例">使用范例</h5>
 <div class="level5">
<pre class="code html4strict">
/**
 * ajax修改订单价格
 */
function modifyOrderPriceAjax() {
 var formParam = $('#upmoneyform').serialize();
 var root = $('#root').val();
 $.ajax({
 url: root + '/modifyOrderPriceAjax.htm',
 type: 'post',
 data: formParam,
 dataType: 'json'
 }).done(function (data) {
 if (data && data == 1) {
 // 普通订单容器
 var $listOrderContainer = $('#listOrderContainer');
 if ($listOrderContainer.css('display') == 'block') {
 submitOrder();
 } else {
 var groupNo = $('#listGroupOrderContainer #groupNo').val();
 var businessId = $('#listGroupOrderContainer #businessId').val();
 ajaxListGroupOrder(groupNo, businessId);
 $("#changeOrderMoneypwd").modal('hide');
 }
 } else {
 console.log("ajax修改订单价格异常:===>" + data);
 }
 }).fail(function (ex) {
 console.log("ajax修改订单价格失败:===>" + ex.status + ",失败内容===>" + ex.responseText);
 });
}
</pre><p>
 通过 var formParam = $('#upmoneyform').serialize();序列化表单参数
</p><h4>vue的AJAX请求</h4>
<pre class="code html4strict">
 doDelTax: function () {
 var self = this;
 var item = self.toDelItem;
 if(!item.id){return;} if (!$('#verifyForm').valid()) {
 return;
 }
 var defer = $.ajax({
 url: $("#basePath").val() +'orderTaxInvoice/orderTaxInvoiceDelete.htm?CSRFToken=' + self.CSRFToken,
 data: {
 'id': item.id,
 'cancelReason':$("#cancelReason").val()
 }
 });
 //操作成功处理
 defer.done(function (data) {
 if(data.code==1){
 self.toDelItem = {};
 showTipAlert('订单开票作废成功');
 self.initEvent();//表单元素清空
 self.searchPage();//刷新列表
 }
 });
 //操作失败处理
 defer.fail(function(data){
 console.log(data.responseText);
 showTipAlert('订单开票作废失败');
 });
 $("#delTaxModal").modal("hide");
 },
</pre>
<h5 id="回调">回调</h5>
<div class="level5">
 <p>
 当提交defer成功或者失败之后会有回调事件,当请求提交成功后会触发<code>defer.done</code>事件,当请求提交失败会触发 <code>defer.fail</code> 事件
 </p>
</div> 
 <h2 class="sectionedit12" id="异常处理">异常处理</h2>
 <div class="level2"> </div>
 <h3 class="sectionedit13" id="异常处理规范">异常处理规范</h3>
 <div class="level3">
 <ul>
 <li class="level1"><div class="li"> 目前有ServiceException,无特殊需要不建议增加其它自定义异常</div> </li>
 <li class="level1"><div class="li"> Service原则上只抛出异常不做try catch处理,调用第三方接口时可以try catch后根据具体情况来做相应的处理</div>
 </li>
 <li class="level1"><div class="li"> Controller做try catch,返回友好提示信息</div>
 </li>
 <li class="level1"><div class="li"> 拦截器中统一处理所有未捕获的异常,并返回相应的错误信息</div>
 </li>
 </ul> </div>
 <h3 class="sectionedit14" id="建立自定义异常类">建立自定义异常类</h3>
 <div class="level3"> <p>
 com.ningpai.exception包下建立自定义异常类继承com.ningpai.exception.ServiceException类
 </p>
 <pre class="code java">
 <xmp>
/**
 * Created by huhaichao
 */
public class ServiceException extends RuntimeException {
 public ServiceException() {
 super();
 } public ServiceException(String message) {
 super(message);
 } public ServiceException(String message, Throwable cause) {
 super(message, cause);
 } public ServiceException(Throwable cause) {
 super(cause);
 } protected ServiceException(String message, Throwable cause,
 boolean enableSuppression,
 boolean writableStackTrace) {
 super(message, cause, enableSuppression, writableStackTrace);
 }
}
 </xmp>
</pre> </div>
 <h2 class="sectionedit17" id="安全">安全</h2>
 <div class="level2"> </div>
 <h3 class="sectionedit18" id="安全规范">安全规范</h3>
 <div class="level3">
 <ul>
 <li class="level1"><div class="li"> 所有新增、修改、删除操作统一使用POST方法提交,禁止使用GET方法进行写操作</div>
 </li>
 <li class="level1"><div class="li"> 所有SQL语句禁止直接拼接字符串统一使用参数化提交以防范SQL注入攻击</div>
 </li>
 <li class="level1"><div class="li"> 所有用户输入数据显示时一定要转义或者进行安全过滤以防范XSS攻击</div>
 </li>
 <li class="level1"><div class="li"> 所有敏感数据和写操作都要严格检查操作权限,保证操作人有权限查看或操作该数据</div>
 </li>
 <li class="level1"><div class="li"> 所有隐私数据比如密码等都要在Model添加@JsonIgnore注解避免通过接口发生泄漏</div>
 </li>
 </ul> </div>
 <h3 class="sectionedit19" id="防范sql注入">防范SQL注入</h3>
 <div class="level3"> <p>
 使用数据库安全中间件druid与mybatis预编译方式,防止sql注入
 </p>
 <pre class="code java">
@Data //lombok注解
public class City implements Serializable {
 /*
 * 主键ID
 */
 private Long cityId;
 /*
 * 城市名称
 */
 private String cityName;
}
</pre> <pre class="code java">
<xmp>
 <update id="updateByPrimaryKeySelective" parameterType="com.ningpai.system.bean.City">
 update np_sys_city
 <set>
 <if test="cityName != null">
 city_name = #{cityName,jdbcType=VARCHAR},
 </if>
 </set>
 where city_id = #{cityId,jdbcType=BIGINT}
 </update>
</xmp>
</pre> </div>
 <h3 class="sectionedit20" id="防范xss">防范XSS</h3>
 <div class="level3"> </div>
 <h4 id="默认转义">默认转义</h4>
 <div class="level4"> <p>
 默认情况下所有模板输出变量都会进行转义
 </p>
 <pre class="code java"><span class="co1">//XSSRequestWrapper</span>
<xmp>
/**
* 重写父类方法
* */
@Override
public String[] getParameterValues(String parameter) {
 String[] values = super.getParameterValues(parameter);
 if (values == null) {
 return values;
 }
 int count = values.length;
 String[] encodedValues = new String[count];
 for (int i = 0; i < count; i++) {
 encodedValues[i] = stripXSS(values[i], parameter);
 }
 return encodedValues;
}
 </xmp>
</pre> </div>
 </div>
 <h4 id="js中的赋值操作">js中的赋值操作</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> 纯文本赋值统一使用jQuery的text方法赋值,除非必要并且内容可控不要使用html()赋值</div>
 </li>
 <li class="level1"><div class="li"> 使用前端模板时也要注意使用转义的方式渲染</div>
 </li>
 </ul> </div>
 <h2 class="sectionedit21" id="菜单_权限">菜单&权限</h2>
 <div class="level2"> </div>
 <h3 class="sectionedit22" id="权限体系">权限体系</h3>
 <div class="level3">
 <ul>
 <li class="level1">
 <div class="li"> 1.添加操作命令,如:xxxx.htm</div>
 </li>
 <li class="level1">
 <div class="li"> 2.添加角色</div>
 </li>
 <li class="level1">
 <div class="li"> 3.添加用户,选择相应的角色</div>
 </li>
 <li class="level1">
 <div class="li"> 4.角色授权</div>
 </li>
 </ul> </div>
 <h3 class="sectionedit23" id="菜单权限相关表">菜单权限相关表</h3>
 <div class="level3">
 <ul>
 <li class="level1">
 <div class="li"> 1.np_manager,BOSS管理员表</div>
 </li>
 <li class="level1">
 <div class="li"> 2.np_authority,角色表</div>
 </li>
 <li class="level1">
 <div class="li"> 3.np_manager_authority,管理员角色映射表</div>
 </li>
 <li class="level1">
 <div class="li"> 4.np_page权限资源表,用户拥有的权限全部存放在该表中</div>
 </li>
 <li class="level1">
 <div class="li"> 5.np_authority_page,角色权限资源映射</div>
 </li>
 </ul>
 </div> <h2 class="sectionedit25" id="消息队列">消息队列</h2>
 <div class="level2"> <p>
 消息队列主要用于系统解耦、异步操作
 </p> </div>
 <h3 class="sectionedit26" id="使用方法">使用方法</h3>
 <div class="level3">
 <ol>
 <li class="level1"><div class="li"> 在amq.properties中定义消息地址</div>
 </li>
 <li class="level1"><div class="li"> 使用AMQProducer的send()方法发送消息</div>
 </li>
 <li class="level1"><div class="li"> 继承Handler接口,重写handle()方法,实现业务处理</div>
 </li>
 </ol> </div>
 <h3 class="sectionedit27" id="范例">范例</h3>
 <div class="level3"> </div>
 <h4 id="发送消息">发送消息</h4>
 <div class="level4">
<pre class="code java"><span class="co1">//发送消息</span>
AMQProducer aMQProducer = (AMQProducer) ApplicationContextHelper.getBean("AMQProducer");
//发送消息重新计算推广佣金
Map<String, Object> header = new HashMap<>();
header.put("bizType", "COMMISION_RECALC");
header.put("backOrderId", backOrder.getBackOrderId());
aMQProducer.sendMessage("", header);
</pre>
 </div> <h4 id="消息处理">消息处理</h4>
 <div class="level4">
<pre class="code java"><span class="co1">//消息处理</span>
@Override
public void handle(Message t) throws JMSException {
 //业务逻辑处理
}
</pre>
 </div> <h2 class="sectionedit28" id="缓存">缓存</h2>
 <h3 class="sectionedit29">Elasticsearch</h3>
 <div class="level2">
 <p>
 主要用来缓存商品数据以及对应的商品营销数据,通过IndexService基础接口类完成相关操作。
 </p>
 </div> <h3 class="sectionedit29" id="使用方法1">使用方法</h3>
 <div class="level3">
 <ol>
 <li class="level1"><div class="li"> 首先定义一个索引(index)名称和对应的类型(type)名称</div>
 </li>
 <li class="level1"><div class="li"> 根据index+type读取数据首先尝试从ES缓存读取,如果ES缓存命中直接返回数据,如果ES缓存未命中再从数据库查询数据缓存后返回</div>
 </li>
 <li class="level1"><div class="li"> 数据发生改变时删除ES缓存,下次读取时返回新数据并再次缓存到ES</div>
 </li>
 </ol> </div>
 <h3 class="sectionedit30" id="范例1">范例</h3>
 <div class="level3"> </div>
 <h4 id="数据读取">数据读取</h4>
 <div class="level4">
<pre class="code java">
<xmp>
/**
* 单次新增ES索引
* @param esMarketing
*/
public void save(EsMarketing esMarketing){
indexService.batchAddDoc(indexName,typeName , esMarketing);
}
</xmp>
</pre>
 </div> <h3 class="sectionedit29">Redis</h3>
 <div class="level2">
 <p>
 用于缓存频繁使用但是很少变化的数据提高系统响应速度,默认缓存使用Redis实现,通过RedisAdapter类完成相关操作。
 </p> </div>
 <h3 class="sectionedit29" id="使用方法1">使用方法</h3>
 <div class="level3">
 <ol>
 <li class="level1"><div class="li"> 首先在CacheKey常量中定义一个缓存名称</div>
 </li>
 <li class="level1"><div class="li"> 根据缓存KEY读取数据首先尝试从Redis缓存读取,如果Redis缓存命中直接返回数据,如果Redis缓存未命中再从数据库查询数据缓存后返回</div>
 </li>
 <li class="level1"><div class="li"> 数据发生改变时删除Redis缓存,下次读取时返回新数据并再次缓存到Redis</div>
 </li>
 </ol> </div>
 <h3 class="sectionedit30" id="范例1">范例</h3>
 <div class="level3"> </div>
 <h4 id="数据读取">数据读取</h4>
 <div class="level4">
<pre class="code java"><xmp>
List<Object> lists = (List<Object>) redisAdapter.get(CacheKeyConstant.TOPSHARE_KEY);if (!CollectionUtils.isEmpty(lists))
{
 return lists;
}//此处省略从数据库获取数据代码
redisAdapter.put(CacheKeyConstant.TOPSHARE_KEY, new ArrayList<>(lists));
return lists;
</xmp>
</pre> </div>
 <h4 id="删除缓存">删除缓存</h4>
 <div class="level4">
 <pre class="code java">redisAdapter.<span class="me1">delete</span><span class="br0">(</span>CacheKeyConstant.<span class="me1">TOPSHARE_KEY</span><span class="br0">)</span><span class="sy0">;</span></pre> </div>
 <h2 class="sectionedit31" id="验证">验证</h2>
 <div class="level2"> <p>
 验证采用后端验证和前端验证组合的方式,后端验证保证数据的完整性,前端验证提高用户的操作体验。
 </p> </div>
 <h3 class="sectionedit32" id="后端验证">后端验证</h3>
 <div class="level3"> <p>
 后端验证使用Spring的Validator验证方式
 </p> </div>
 <h4 id="范例2">范例</h4>
 <div class="level4">
<pre class="code java">
@Component
public class RushFormValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
 return MarketingRushForm.class.equals(clazz);
}@Override
public void validate(Object target, Errors errors) {
 ValidationUtils.rejectIfEmpty(errors, "marketing", null,"营销信息不能为空");
 ValidationUtils.rejectIfEmpty(errors, "marketing.marketingName", null,"请填写活动名称");
 ValidationUtils.rejectIfEmpty(errors, "marketing.beginTime", null,"请选择开始时间");
 ValidationUtils.rejectIfEmpty(errors, "marketing.endTime", null,"请选择结束时间");
 ValidationUtils.rejectIfEmpty(errors, "marketing.joinLevel", null,"至少选择一个参加会员");
 ValidationUtils.rejectIfEmpty(errors, "marketing.siteFlag", null,"至少选择一个活动站点");
 ValidationUtils.rejectIfEmpty(errors, "marketingRush", null, "抢购图片不能为空");
 ValidationUtils.rejectIfEmpty(errors, "marketingRush.rushDiscount", null, "抢购折扣不能为空");
 ValidationUtils.rejectIfEmpty(errors, "marketingRush.rushLimitCount", null, "ID限购数量不能为空");
 ValidationUtils.rejectIfEmpty(errors, "marketingRush.rushImg", null, "抢购图片不能为空");
}
}使用方法:
public class MarketingRushController {@Resource(name = "rushFormValidator")
private RushFormValidator rushFormValidator;@InitBinder
public void initBinder(DataBinder binder) {
 if (binder.getTarget() instanceof MarketingRushForm) {
 binder.setValidator(rushFormValidator);
 }
}
}
</pre> </div>
 <h3 class="sectionedit33" id="前端验证">前端验证</h3>
 <div class="level3"> <p>
 前台验证统一使用<a href="https://jqueryvalidation.org/" class="urlextern" title="https://jqueryvalidation.org/" rel="nofollow">jQuery Validation</a>插件实现,详细使用方法请参考<a href="https://jqueryvalidation.org/documentation/" class="urlextern" title="https://jqueryvalidation.org/documentation/" rel="nofollow">官方文档</a>
 </p> </div>
 <h4 id="范例3">范例</h4>
 <div class="level4">
<pre class="code javascript">
<xmp>
$('#saveForm').validate({
 debug: true,
 onfocusout: function (element) {
 if (element.name == 'startTime' || element.name == 'endTime') {
 return;
 }
 $(element).valid();
 },
 rules: {
 "marketing.marketingName": {
 specstr: true,
 required: true,
 maxlength: 40
 },
 "marketing.joinLevel": {
 required: true,
 minlength: 1
 },
 "marketing.siteFlag": {
 required: true,
 minlength: 1
 },
 "startTime": {
 required: true,
 date_lt: "#endTime"
 },
 "endTime": {
 required: true,
 date_gt: "#startTime"
 }
 },
 messages: {
 "marketing.marketingName": {
 specstr: '不允许含有特殊字符',
 required: '请填写活动名称',
 maxlength: '活动名称不超过个{0}字'
 },
 "marketing.joinLevel": "至少选择一个参加会员",
 "marketing.siteFlag": "至少选择一个活动站点",
 "startTime": {
 required: "请选择开始时间"
 },
 "endTime": {
 required: "请选择结束时间"
 }
 },
 errorPlacement: function (error, element) {
 error.appendTo(element.parents("div[class^='col-sm']:eq(0)"));
 }
 });
</xmp>
</pre> </div>
 <h2 class="sectionedit35" id="操作日志">操作日志</h2>
 <div class="level2"> <p>
 用于记录后台管理员和商家的操作日志
 </p> </div>
 <h3 class="sectionedit36" id="系统日志模块">系统日志模块(入数据库)</h3>
 <div class="level3"> <p>
 在方法中调用OperaLogUtil中的addOperaLog方法完成日志的记录
 </p>
 <pre class="code java"> // 操作日志
OperaLogUtil.addOperaLog(request, customerAllInfo.getCustomerUsername(), "新增退单物流信息", "新增退单物流信息-->需要执行退单操作的订单号【" + orderNo + "】,物流信息:名称【" + wlname + "】,物流单号【" + wlno
+ LOGGERINFO1 + customerAllInfo.getCustomerUsername());
</pre> </div>
 <h3 class="sectionedit37" id="运行后台日志模块">运行后台日志模块(控制台打印)</h3>
 <div class="level3"> <p>
 在方法上注解@Slf4j,调用log.info方法完成日志的记录
 </p>
 <pre class="code java">
log.info("OrderDrawBackHandler:backOrder:{} backResult {}", backOrder.getBackOrderId(), backResult);
</pre> </div>
 <h2 class="sectionedit38" id="注意事项">注意事项</h2>
 <div class="level2">
 </div>
 <h3 class="sectionedit39" id="UrlRewriteFilter">UrlRewriteFilter</h3>
 <div class="level3"> <p>
 Urlrewritefilter,通过java的Filter过滤器对URL进行重写,用户得到的全部都是经过处理后的URL地址,本质上通过伪地址进行页面跳转,隐藏真实地址,达到隐藏实现细节的目的。
 当你直接根据前台url找不到后台方法时,可以先试着去/WEB-INF/urlrewrite.xml查找一下。
 <pre class="code java">
<xmp><rule>
 <note>
 登录
 </note>
 <from>^/loginm(.html)?$</from>
 <to type="forward">/customerm/login.htm</to>
</rule></xmp>
</pre>
 </p> </div>
 
 <h3 class="sectionedit41" id="CSRFToken">CSRFToken</h3>
 <div class="level3"> <p>
 boss系统采用AOP验证token,如果有放开的控制器不需要验证token,可以在TokenAOPUtil中进行枚举。
 </p> <pre class="code java">
<xmp>
/* 如果是放开的控制器就不验证token */
for (String url : urls) {
 if (url.equals(path)) {
 bool = false;
 return;
 }
}
</xmp>
</pre>
 </p> </div>
 <h3 class="sectionedit42" id="WeiXinUtil">常用工具类</h3>
 <div class="level3"> </div>
 <p>
 系统封装了一些常用操作方法,便于重复开发利用。
 </p>
 <p>
 需要获取微信token时AccessTokenApi.getAccessTokenStr();统一获取避免频繁更新导致多应用之间token不一致。
 </p>
 <pre class="code html4strict">
<xmp>
//查询微信消息的模版ID
String token = AccessTokenApi.getAccessTokenStr();
if (StringUtil.isEmpty(token)) {
 return null;
}
if (wxMsgCfg.getTmpId() == null || "".equals(wxMsgCfg.getTmpId().toString())) {
 wxMsgCfg.setTmpId(owerAcessTokenUtil.getTempId(wxMsgCfg.getTmpNo()));
 wxMsgCfgMapper.update(wxMsgCfg);
}
</xmp>
</pre> <p>
 service层想获取request等信息时可以使用WebContextUtils.getRequest();
 </p>
 <pre class="code html4strict">
<xmp>
/**
* 获取request对象
* @return request对象
*/
public static HttpServletRequest getRequest() {
ServletRequestAttributes servletRequestAttributes = getServletRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
return request;
}
</xmp>
</pre> <div class="level3">
 <p>
 使用<strong>org.apache.commons.lang3</strong>命名空间下的StringUtil类进行空字符串判断
 </p>
 <ul>
 <li class="level1"><div class="li"> StringUtils.isBlank 判断““、null、空格</div>
 </li>
 <li class="level1"><div class="li"> StringUtils.isEmpty 判断”“、null</div>
 </li>
 </ul>
 <pre class="code java"><a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system"><span class="kw3">System</span></a>.<span class="me1">out</span>.<span class="me1">print</span><span class="br0">(</span>StringUtils.<span class="me1">isBlank</span><span class="br0">(</span><span class="st0">""</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">//true</span>
<a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system"><span class="kw3">System</span></a>.<span class="me1">out</span>.<span class="me1">print</span><span class="br0">(</span>StringUtils.<span class="me1">isBlank</span><span class="br0">(</span><span class="st0">" "</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">//true</span>
<a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system"><span class="kw3">System</span></a>.<span class="me1">out</span>.<span class="me1">print</span><span class="br0">(</span>StringUtils.<span class="me1">isBlank</span><span class="br0">(</span><span class="kw2">null</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">//true</span>
<a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system"><span class="kw3">System</span></a>.<span class="me1">out</span>.<span class="me1">print</span><span class="br0">(</span>StringUtils.<span class="me1">isEmpty</span><span class="br0">(</span><span class="st0">""</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">//true</span>
<a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system"><span class="kw3">System</span></a>.<span class="me1">out</span>.<span class="me1">print</span><span class="br0">(</span>StringUtils.<span class="me1">isEmpty</span><span class="br0">(</span><span class="st0">" "</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">//false</span>
<a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system"><span class="kw3">System</span></a>.<span class="me1">out</span>.<span class="me1">print</span><span class="br0">(</span>StringUtils.<span class="me1">isEmpty</span><span class="br0">(</span><span class="kw2">null</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">//true</span></pre> </div>
 <h2 class="sectionedit43" id="完整开发范例">完整开发范例</h2>
 <div class="level2"> </div>
 <h3 class="sectionedit44" id="确定需求">确定需求</h3>
 <div class="level3">
 <ul>
 <li class="level1"><div class="li"> Boss平台推送应用内消息配置</div>
 </li>
 <li class="level1"><div class="li"> Boss管理消息配置</div>
 </li>
 <li class="level1"><div class="li"> API查询消息配置</div>
 </li>
 </ul> </div>
 <h3 class="sectionedit45" id="创建表结构">创建表结构</h3>
 <div class="level3">
<pre class="code sql">
create table qm_app_push_cfg
(
 push_id int comment '主键id',
 push_name varchar(20) comment '推送名称',
 os_type smallint comment '平台类型(1:android, 2:ios)',
 app_key varchar(50) comment '应用唯一标识',
 app_master_secret varchar(50) comment '服务器秘钥',
 is_enable smallint comment '是否启用(1启用,0不启用)',
 user_id bigint comment '修改人'
);
</pre> </div>
 <h3 class="sectionedit46" id="添加功能菜单">添加功能菜单</h3>
 <div class="level3">
<pre class="code sql">
INSERT INTO `np_page` ( `designation`, `url`, `img_url`, `img_url_selected`, `parentId`, `grade`, `sort`, `type`, `characterization`, `create_time`, `mod_time`, `flag`, `bar_sort`, `bundle_name`)
SELECT
'添加修改APP push配置','appPush/addAppPushCfg.htm',NULL,NULL, a.id,'4','2','2',NULL,'2017-10-18 17:14:02','0000-00-00 00:00:00','0',NULL,NULL
FROM np_page a WHERE url = 'appPush/pusCfgIndex.htm' AND designation = '消息推送设置';
</pre> </div>
 <h3 class="sectionedit47" id="配置权限">配置权限</h3>
 <div class="level3"> </div>
 <h4 id="菜单模块2">菜单模块</h4>
 <div class="level4">
<pre class="code xml">
 //boss端不拦截
String[] urls = new String[] {
 "/appPush/pusCfgIndex.htm"
 ,"/handPush/index.htm"
 ,"/handPush/handIndex.htm"
 ,"/subjectPage/index.htm"
 ,"/appPush/pusCfgHelp.htm"
 };
</pre> </div>
 <h3 class="sectionedit48" id="Model开发1">Model开发</h3>
 <div class="level3">
<pre class="code java">
package com.qianmi.push.bean.entity;import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * app 推送信息配置
 */
@Data //lombok注解
public class AppPushCfg implements Serializable { private static final long serialVersionUID = 1L;
 /**
 * id
 */
 private Integer pushId; /**
 * 推送名称
 */
 private String pushName; /**
 * 平台类型(1:android, 2:ios)
 */
 private Short osType; /**
 * 应用唯一标识
 */
 private String appKey; /**
 * 服务器秘钥
 */
 private String appMasterSecret; /**
 * 是否启用(1启用,0不启用)
 */
 private Short isEnable; /**
 * 修改人
 */
 private Long userId;}
</pre> </div>
 <h3 class="sectionedit49" id="dao开发1">Example开发</h3>
 <div class="level3">
<pre class="code java">
package com.qianmi.push.bean.entity.example;import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class AppPushCfgExample {
 protected String orderByClause; protected boolean distinct;
 protected List<Criteria> oredCriteria;
 private Integer limit;
 private Integer offset;
 public AppPushCfgExample() {
 oredCriteria = new ArrayList<Criteria>();
 } public void setOrderByClause(String orderByClause) {
 this.orderByClause = orderByClause;
 } public String getOrderByClause() {
 return orderByClause;
 } public void setDistinct(boolean distinct) {
 this.distinct = distinct;
 } public boolean isDistinct() {
 return distinct;
 } public List<Criteria> getOredCriteria() {
 return oredCriteria;
 } public void or(Criteria criteria) {
 oredCriteria.add(criteria);
 } public Criteria or() {
 Criteria criteria = createCriteriaInternal();
 oredCriteria.add(criteria);
 return criteria;
 } public Criteria createCriteria() {
 Criteria criteria = createCriteriaInternal();
 if (oredCriteria.size() == 0) {
 oredCriteria.add(criteria);
 }
 return criteria;
 } protected Criteria createCriteriaInternal() {
 Criteria criteria = new Criteria();
 return criteria;
 } public void clear() {
 oredCriteria.clear();
 orderByClause = null;
 distinct = false;
 } public void setLimit(Integer limit) {
 this.limit = limit;
 } public Integer getLimit() {
 return limit;
 } public void setOffset(Integer offset) {
 this.offset = offset;
 } public Integer getOffset() {
 return offset;
 } /**
 */
 public static class Criteria extends GeneratedCriteria { protected Criteria() {
 super();
 }
 } public static class Criterion {
 private String condition; private Object value;
 private Object secondValue;
 private boolean noValue;
 private boolean singleValue;
 private boolean betweenValue;
 private boolean listValue;
 private String typeHandler;
 public String getCondition() {
 return condition;
 } public Object getValue() {
 return value;
 } public Object getSecondValue() {
 return secondValue;
 } public boolean isNoValue() {
 return noValue;
 } public boolean isSingleValue() {
 return singleValue;
 } public boolean isBetweenValue() {
 return betweenValue;
 } public boolean isListValue() {
 return listValue;
 } public String getTypeHandler() {
 return typeHandler;
 } protected Criterion(String condition) {
 super();
 this.condition = condition;
 this.typeHandler = null;
 this.noValue = true;
 } protected Criterion(String condition, Object value, String typeHandler) {
 super();
 this.condition = condition;
 this.value = value;
 this.typeHandler = typeHandler;
 if (value instanceof List<?>) {
 this.listValue = true;
 } else {
 this.singleValue = true;
 }
 } protected Criterion(String condition, Object value) {
 this(condition, value, null);
 } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
 super();
 this.condition = condition;
 this.value = value;
 this.secondValue = secondValue;
 this.typeHandler = typeHandler;
 this.betweenValue = true;
 } protected Criterion(String condition, Object value, Object secondValue) {
 this(condition, value, secondValue, null);
 }
 }
}
</pre> </div>
 <h3 class="sectionedit51" id="Controller和模板开发">Controller和模板开发</h3>
 <div class="level3"> </div>
 <h4 id="push模块">push模块</h4>
 <div class="level4">
<pre class="code java">
package com.ningpai.appPush.controller;import com.ningpai.base.controller.BaseController;
import com.ningpai.base.form.ResultMsg;
import com.qianmi.push.bean.vo.AppPushCfgVo;
import com.ningpai.constant.AppPushConstant;
import com.qianmi.push.service.AppPushService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;/**
 * app push
 * @date 2017/10/17
 */
@Controller
@RequestMapping("/appPush")
public class AppPushController extends BaseController { @Autowired
 private AppPushService appPushService; /**
 * push 配置页面
 *
 * @return
 */
 @RequestMapping("/pusCfgIndex")
 public String pusCfgIndex(ModelMap map) {
 map.put("isAppPushCfg", appPushService.isAppPushCfg(AppPushConstant.UMENG_PUSH_ID));
 return "/jsp/apppush/push_cfg";
 } /**
 * 帮助
 * @param map
 * @return
 */
 @RequestMapping("/pusCfgHelp")
 public String pusCfgHelp(ModelMap map) {
 return "/jsp/apppush/umengHelpDoc";
 } /**
 * 根据id查询对应的配置
 *
 * @return
 */
 @RequestMapping("/queryAppPushCfg")
 @ResponseBody
 public ResultMsg<AppPushCfgVo> queryAppPushCfg(int pushId) { ResultMsg<AppPushCfgVo> resultMsg = new ResultMsg<>();
 resultMsg.setCode(ResultMsg.SUCCESS);
 resultMsg.setContext(appPushService.queryAppPushCfgById(pushId));
 return resultMsg;
 } /**
 * 保存和修改数据
 *
 * @return
 */
 @RequestMapping("/addAppPushCfg")
 @ResponseBody
 public ResultMsg addAppPushCfg(AppPushCfgVo appPushCfgVo) { if (appPushCfgVo.getPushId() == null || StringUtils.isEmpty(appPushCfgVo.getAndAppKey()) || StringUtils.isEmpty(appPushCfgVo.getAndAppMasterSecret())
 || StringUtils.isEmpty(appPushCfgVo.getIosAppKey()) || StringUtils.isEmpty(appPushCfgVo.getIosAppMasterSecret())) {
 return error("必传参数不能为空!");
 } ResultMsg resultMsg = new ResultMsg();
 appPushCfgVo.setUserId(getLoginUserId());
 resultMsg.setCode(appPushService.addAppPushCfg(appPushCfgVo));
 return resultMsg;
 }}
</pre>
 <pre class="code html4strict">
<xmp>
<div class="container-fluid page_body">
 <div class="row">
 <div class="col-lg-20 col-md-19 col-sm-18 main">
 <div class="main_cont">
 <h2 class="main_title"></h2>
 <div class="common_form common_form_max p20">
 <div class="alert alert-warning alert-dismissible" role="alert" style="display:${isAppPushCfg==1 ? 'none':'block'}">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
 aria-hidden="true">×</span></button>
 <strong>温馨提示:</strong> <br/>
 消息推送功能适用于在App中<br/>
 推送功能使用友盟的推送服务,在使用推送功能时,请先配置参数<br/>
 如果您还没有友盟账号,请先到<a href="http://www.umeng.com/" target="_blank">友盟官网</a>注册
 </div>
 <div class="box_method p20">
 <div class="method_item" >
 <h4>友盟</h4>
 <div class="bar">
 <div class="links">
 <a href="javascript:;" οnclick="editPush('1001')">编辑</a>
 <a href="<%=basePath%>appPush/pusCfgHelp.htm" target="_blank">帮助</a>
 </div>
 <div class="status">已启用</div>
 </div>
 </div>
 </div>
 </div> </div>
 </div>
 </div>
</div>
</xmp></pre>
 <pre class="code javascript">
 <xmp>
function validateFrom(){
 $('#fromPush').validate({
 debug: true,
 ignore: 'button',
 onfocusout: function (element) {
 var self = $(element);
 if ($.trim(self.attr("save-valid")) == '') {
 self.valid();
 }
 },
 rules: {
 andAppKey: {
 required: true
 },
 andUmengMessageSecret: {
 required: true,
 },
 andAppMasterSecret: {
 required: true,
 },
 iosAppKey: {
 required: true
 },
 iosAppMasterSecret: {
 required: true
 } },
 messages: {
 required: '请填写AppKey',
 andAppKey: {
 required: '请填写AppKey',
 },
 andUmengMessageSecret: {
 required: '请填写Umeng Message Secret',
 },
 andAppMasterSecret: {
 required: '请填写App Master Secret',
 },
 iosAppKey: {
 required: '请填写AppKey',
 },
 iosAppMasterSecret: {
 required: '请填写App Master Secret',
 } },
 errorPlacement: function (error, element) {
 //console.log(error)
 //$(element).parents('.form-inline').append(error);
 error.appendTo(element.parents("div[class^='col-sm']:eq(0)")); },
 });
}function cacleSubmit() {
 $("#editPush").modal('hide');
}function savePushCfg(){
 if($("#fromPush").valid()){
 var data = $("#fromPush").serialize();
 var CSRFToken = $("#CSRFToken").val();
 $.ajax({
 type: 'POST',
 url: basePath+'appPush/addAppPushCfg.htm?CSRFToken='+CSRFToken,
 data:data,
 async: false,
 success: function (data) {
 if (data.code == 1) {
 showTipAlert('保存信息成功', function () {
 window.location.href = basePath+"appPush/pusCfgIndex.htm";
 });
 } else {
 showTipAlert('保存信息失败');
 } $('#chooseOnlineService').modal('hide');
 }
 }) }
}
 </xmp>
</pre> </div>
 <h4 id="seller模块3">Service模块</h4>
 <div class="level4">
<pre class="code java">
package com.qianmi.push.service.impl;@Service("appPushService")
@Slf4j
public class AppPushServiceImpl implements AppPushService { @Autowired
 private AppPushCfgMapper appPushCfgMapper; @Autowired
 private AppPushNotesMapper appPushNotesMapper; @Autowired
 private AppPushNotesCusMapper appPushNotesCusMapper; @Autowired
 private AppPushDeviceTokenMapper appPushDeviceTokenMapper; @Autowired
 private AppPushNotesTaskMapper appPushNotesTaskMapper; @Autowired
 private AppInsideMsgMapper appInsideMsgMapper;
 /**
 * 配置的线程池
 **/
 @Resource(name = "threadPool")
 private ThreadPoolTaskExecutor taskExecutor; private boolean isRun = false;
 private PushClientUtils pushClientUtils = null;
 public static final int QUERY_MAX = 10000;
 public static final int SUCCESS = 1;
 public static final int ERROR = 0;
 @PostConstruct
 public void init() {
 pushClientUtils = PushClientUtils.getInstance(); AppPushCfgVo appPushCfgVo = this.queryAppPushCfgById(AppPushConstant.UMENG_PUSH_ID);
 pushClientUtils.setAndroidAppKey(appPushCfgVo.getAndAppKey());
 pushClientUtils.setAndroidAppMasterSecret(appPushCfgVo.getAndAppMasterSecret()); pushClientUtils.setIosAppKey(appPushCfgVo.getIosAppKey());
 pushClientUtils.setIosAppMasterSecret(appPushCfgVo.getIosAppMasterSecret());
 } @Override
 public PushClientUtils getPushClientUtils() {
 return pushClientUtils;
 } @Override
 public AppPushCfgVo queryAppPushCfgById(int pushId) {
 AppPushCfgExample appPushCfgExample = new AppPushCfgExample();
 appPushCfgExample.createCriteria().andPushIdEqualTo(pushId); List<AppPushCfg> list = appPushCfgMapper.selectByExample(appPushCfgExample);
 AppPushCfgVo appPushCfgVo = new AppPushCfgVo();
 if (CollectionUtils.isNotEmpty(list)) {
 for (AppPushCfg appPushCfg : list) {
 if (appPushCfg.getOsType() == AppPushConstant.OS_TYPE_ANDROID) {
 //android
 appPushCfgVo.setAndAppKey(appPushCfg.getAppKey());
 appPushCfgVo.setAndAppMasterSecret(appPushCfg.getAppMasterSecret());
 appPushCfgVo.setAndUmengMessageSecret(appPushCfg.getUmengMessageSecret());
 } else if (appPushCfg.getOsType() == AppPushConstant.OS_TYPE_IOS) {
 //ios
 appPushCfgVo.setIosAppKey(appPushCfg.getAppKey());
 appPushCfgVo.setIosAppMasterSecret(appPushCfg.getAppMasterSecret());
 }
 }
 } return appPushCfgVo;
 } @Override
 @Transactional(rollbackFor = Exception.class)
 public int addAppPushCfg(AppPushCfgVo appPushCfgVo) { AppPushCfgExample appPushCfgExample = new AppPushCfgExample();
 appPushCfgExample.createCriteria().andPushIdEqualTo(appPushCfgVo.getPushId());
 appPushCfgMapper.deleteByExample(appPushCfgExample); //Android
 AppPushCfg appPushCfg = new AppPushCfg();
 appPushCfg.setAppKey(appPushCfgVo.getAndAppKey());
 appPushCfg.setUpdateTime(new Date());
 appPushCfg.setUserId(appPushCfg.getUserId());
 appPushCfgMapper.insert(appPushCfg); //ios
 appPushCfg = new AppPushCfg();
 appPushCfg.setAppKey(appPushCfgVo.getIosAppKey());
 appPushCfg.setIsEnable(NumberUtils.SHORT_ONE);
 appPushCfg.setUpdateTime(new Date());
 appPushCfg.setUserId(appPushCfg.getUserId());
 appPushCfgMapper.insert(appPushCfg); //重新设置key
 pushClientUtils.setAndroidAppKey(appPushCfgVo.getAndAppKey());
 pushClientUtils.setAndroidAppMasterSecret(appPushCfgVo.getAndAppMasterSecret()); pushClientUtils.setIosAppKey(appPushCfgVo.getIosAppKey());
 pushClientUtils.setIosAppMasterSecret(appPushCfgVo.getIosAppMasterSecret()); return SUCCESS;
 } @Override
 public int isAppPushCfg(int pushId) {
 AppPushCfgExample appPushCfgExample = new AppPushCfgExample();
 appPushCfgExample.createCriteria().andPushIdEqualTo(pushId); List<AppPushCfg> list = appPushCfgMapper.selectByExample(appPushCfgExample);
 //存在一个是空就算没有配置
 Optional<AppPushCfg> cfg = list.stream().filter(appPushCfg -> StringUtils.isBlank(appPushCfg.getAppKey()) || StringUtils.isBlank(appPushCfg.getAppMasterSecret())).findFirst();
 return cfg.isPresent() || CollectionUtils.isEmpty(list) ? ERROR : SUCCESS;
 }}
</pre>
 <pre class="code java">
package com.qianmi.push.mapper;import com.qianmi.push.bean.entity.AppPushCfg;
import com.qianmi.push.bean.entity.example.AppPushCfgExample;
import org.springframework.stereotype.Repository;import java.util.List;
@Repository
public interface AppPushCfgMapper { int deleteByExample(AppPushCfgExample example);
 int insert(AppPushCfg record);
 List<AppPushCfg> selectByExample(AppPushCfgExample example);
}
</pre> </div>
 <h2 class="sectionedit52" id="编码规范">编码规范</h2>
 <div class="level2"> </div>
 <h3 class="sectionedit53" id="约定">约定</h3>
 <div class="level3"> </div>
 <h4 id="编码">编码</h4>
 <div class="level4">
 <pre class="code">统一使用UTF-8编码</pre> </div>
 <h4 id="缩进">缩进</h4>
 <div class="level4">
 <pre class="code">代码缩进使用4个空格(space),而不是制表符(tab),务必统一调整编辑器设置。 </pre> </div>
 <h4 id="大括号位置">大括号位置</h4>
 <div class="level4">
<pre class="code java"><span class="kw1">class</span> DemoClass <span class="br0">{</span>
 <span class="co1">//code</span>
<span class="br0">}</span></pre> </div>
 <h3 class="sectionedit54" id="url规范">URL规范</h3>
 <div class="level3"> </div>
 <h4 id="顶级目录">顶级目录</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> /customer 会员中心</div>
 </li>
 <li class="level1"><div class="li"> /third 商家中心</div>
 </li>
 <li class="level1"><div class="li"> /core 后台管理</div>
 </li>
 </ul> </div>
 <h4 id="命名范例">命名范例</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> /goods</div>
 </li>
 <li class="level1"><div class="li"> /order/goodsInfo</div>
 </li>
 <li class="level1"><div class="li"> /third/goodsInfo</div>
 </li>
 <li class="level1"><div class="li"> /coupon/goodsInfo</div>
 </li>
 </ul> </div>
 <h4 id="操作命名">操作命名</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> /invoiceItem/list 列表</div>
 </li>
 <li class="level1"><div class="li"> /invoiceItem/item 详细</div>
 </li>
 <li class="level1"><div class="li"> /invoiceItem/add 添加</div>
 </li>
 <li class="level1"><div class="li"> /invoiceItem/update 编辑</div>
 </li>
 <li class="level1"><div class="li"> /invoiceItem/delete 删除</div>
 </li>
 </ul> </div>
 <h3 class="sectionedit55" id="模板规范">模板规范</h3>
 <div class="level3"> </div>
 <h4 id="顶级目录1">顶级目录</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> /core 核心</div>
 </li>
 <li class="level1"><div class="li"> /customer 会员中心</div>
 </li>
 <li class="level1"><div class="li"> /third 商家中心</div>
 </li> </ul>
 </div>
 <h4 id="命名规范">命名规范</h4>
 <div class="level4">
 <pre class="code">目录结构及命名与URL规范保持一致</pre> </div>
 <h3 class="sectionedit56" id="命名">命名</h3>
 <div class="level3"> </div>
 <h4 id="包名">包名</h4>
 <div class="level4"> <p>
 包名全部小写,不使用下划线,例:com.wanmi.foo
 </p> </div>
 <h4 id="类名">类名</h4>
 <div class="level4"> <p>
 类名都以<strong>UpperCamelCase</strong>驼峰风格编写,例:CustomerController
 </p> </div>
 <h4 id="方法名">方法名</h4>
 <div class="level4"> <p>
 方法名都以<strong>lowerCamelCase</strong>驼峰风格编写,例:saveUser(User u)
 </p>
 <ul>
 <li class="level1"><div class="li"> get,findById <em>获取单个对象,按某种规则获取</em></div>
 </li>
 <li class="level1"><div class="li"> list<em>获取列表</em></div>
 </li>
 <li class="level1"><div class="li"> save <em>保存</em></div>
 </li>
 <li class="level1"><div class="li"> update <em>更新</em></div>
 </li>
 <li class="level1"><div class="li"> delete <em>删除</em></div>
 </li>
 </ul> </div>
 <h4 id="常量名">常量名</h4>
 <div class="level4"> <p>
 常量名命名模式为<strong>CONSTANT_CASE</strong>,全部字母大写,用下划线分隔单词,例:PROMOTIONER_APPLY_SUCCESS
 </p> </div>
 <h4 id="变量_参数_属性">变量&参数&属性</h4>
 <div class="level4"> <p>
 以<strong>lowerCamelCase</strong>风格编写,例:customerName
 </p> </div>
 <h3 class="sectionedit57" id="注释">注释</h3>
 <div class="level3"> </div>
 <h4 id="实体注释">实体注释</h4>
 <div class="level4">
<pre class="code java"><span class="co3">
 <xmp>
/**
 * 增票资质form表单
 */
@Data //lombok注解
public class TaxInvoiceAptitudeForm extends Page<TaxInvoiceAptitudeVo> { /**
 * 增票资质类
 */
 private TaxInvoiceAptitude taxInvoiceAptitude; /**
 * 用户类
 */
 private Customer customer; /**
 * 排序规则
 */
 private String orderBy; /**
 * 选择的类型
 * 0.当前选中结果 1.当前筛选结果
 */
 private String selectRange;
}
 </xmp>
</pre> </div>
 <h4 id="类注释">类注释</h4>
 <div class="level4">
<pre class="code java"><span class="co3">/**
 * 类说明
*/</span>
<span class="kw1">public</span> <span class="kw1">class</span> DemoClass <span class="br0">{</span>
<span class="br0">}</span></pre> </div>
 <h4 id="方法注释">方法注释</h4>
 <div class="level4">
<pre class="code java"><span class="co3">/**
 * 方法说明
 * @param param 参数说明
 * @return 返回值说明
*/</span>
<span class="kw1">public</span> <a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string"><span class="kw3">String</span></a> demoMethod<span class="br0">(</span><a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string"><span class="kw3">String</span></a> param<span class="br0">)</span> <span class="br0">{</span>
 <span class="kw1">return</span> <span class="st0">"返回值"</span><span class="sy0">;</span>
<span class="br0">}</span></pre> </div>
 <h4 id="其它注释">其它注释</h4>
 <div class="level4">
<pre class="code java"><span class="co1">//变量赋值</span>
<a href="http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string"><span class="kw3">String</span></a> name <span class="sy0">=</span> <span class="st0">"string"</span><span class="sy0">;</span></pre> </div>
 <h3 class="sectionedit58" id="数据库">数据库</h3>
 <div class="level3"> </div>
 <h4 id="表名">表名</h4>
 <div class="level4"> <p>
 小写字母和下划线,最多30个字符(兼容Oracle),例:qm_marketing_scope
 </p> </div>
 <h4 id="字段名">字段名</h4>
 <div class="level4"> <p>
 小写字母和下划线,最多30个字符(兼容Oracle),例:scope_id
 </p> </div>
 <h3 class="sectionedit59" id="日志">日志</h3>
 <div class="level3"> </div>
 <h4 id="日志级别">日志级别</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> DEBUG</div>
 </li>
 <li class="level1"><div class="li"> INFO</div>
 </li>
 <li class="level1"><div class="li"> WARN</div>
 </li>
 <li class="level1"><div class="li"> ERROR</div>
 </li>
 <li class="level1"><div class="li"> FATAL</div>
 </li>
 </ul> </div>
 <h4 id="日志规范">日志规范</h4>
 <div class="level4">
 <ul>
 <li class="level1"><div class="li"> 一般调试信息 INFO</div>
 </li>
 <li class="level1"><div class="li"> 程序可处理的错误 WARN</div>
 </li>
 <li class="level1"><div class="li"> 程序不可处理的错误 ERROR</div>
 </li>
 </ul> </div>
 </div>
 <div class="docInfo">
 <bdi>copyright@</bdi>
 <bdi>wanmi</bdi>
 </div> </div></div><!-- /content -->
 <hr class="a11y">
</div><!-- /wrapper -->
</div></div><!-- /site -->
<div class="no"><img src="files/indexer.gif" alt="" height="1" width="2"></div>
<div id="screen__mode" class="no"></div> <!--[if ( lte IE 7 | IE 8 ) ]></div><![endif]--></body></html>