<!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">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>php
<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 ">css
<div class="wrapper group">html
<!-- ********** CONTENT ********** -->
<div id="dokuwiki__content"><div class="pad group">前端
<div class="pageId"><span>product:develop</span></div>vue
<div class="page group">
<!-- wikipage start -->
<!-- TOC START -->
<div id="dw__toc">
<div style="">java
<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>mysql
</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>jquery
<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>linux
</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"> 熟练使用Spring四、Spring4 MVC、Mybatis3等开发框架</div>
</li>
<li class="level1"><div class="li"> 熟练使用redis3.2.八、elasticsearch1.7.一、activeMQ5.14等应用框架</div>
</li>
<li class="level1"><div class="li"> 熟练使用HTML、CSS、JavaScript、jQuery1.九、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:;" onclick="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>