<!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>