須要的其餘技術: |
CRM(客戶關係管理系統) |
Lucene(索引查詢):查看索引的第三方jar包:lukeall.jar |
先投資,作出產品,再去銷售(QQ、360) |
投標、分爲甲方(項目應用方)、乙方(項目開發方),甲方先給乙方項目前期款,交付項目以後,收取尾款 |
自動化辦公 |
客戶關係管理 |
針對物流資源管理(物流)、人力資源管理(人流)、財務資源管理(財流)、信息資源管理(信息流)集成一體化的企業管理軟件 |
開發任務:三個業務模塊 |
物流基礎數據(取派人員信息、區域信息、區域分配信息、線路信息)對應需求2.6章 |
對應需求2.7業務受理 2.8調度---接受客戶業務訴求,將業務數據錄入到系統中 |
---2.9之後 |
用戶和權限 |
Filter 和 Interceptor的區別: |
能夠攔截全部的web資源 |
只能攔截Action |
步驟 : 一、 編寫 攔截器類,實現 Interceptor 接口 public class LoginInterceptor extends AbstractInterceptor { 二、 實現intercept 方法,判斷當前用戶是否登錄 User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user"); if (user == null) { // 沒有登錄 return "login"; // 登錄頁面 } else { // 已經登錄 return invocation.invoke(); }
|
三、 註冊攔截器,配置結果頁面 <!-- 註冊攔截器 --> <interceptors> <interceptor name="login" class="cn.itcast.bos.web.interceptor.LoginInterceptor"></interceptor> <!-- 定義新的攔截器棧 --> <interceptor-stack name="loginStack"> <interceptor-ref name="defaultStack"></interceptor-ref> //默認的棧 <interceptor-ref name="login"></interceptor-ref> </interceptor-stack> </interceptors> 將攔截器 設置package默認攔截器 <!-- 設置默認攔截器棧 --> <default-interceptor-ref name="loginStack"></default-interceptor-ref> 配置全局結果集 <!-- 配置全局結果集 --> <global-results> <result name="login">/login.jsp</result> </global-results> 四、 使用攔截器傳遞錯誤信息 ActionSupport action = (ActionSupport) invocation.getAction(); action.addActionError("你還未登錄或者長時間未使用,請從新登錄!"); 問題: 使用iframe嵌套,主頁嵌套主頁 狀況 能夠,在未登錄,跳轉提示信息頁面,用戶點擊連接,跳到登錄 (整個瀏覽器跳轉) 或者 在登錄form 添加 target=」_top」
|
一個Action也能夠不須要配置class: |
strut-default.xml文件中 最底部有一個配置:<default-class-ref class = "com.opensymphony.xwork2.ActionSupport"> |
<!-- 須要進行權限控制的頁面訪問,沒有配置class 也能夠 --> <action name="page_*_*"> <result type="dispatcher"> /WEB-INF/pages/{1}/{2}.jsp </result> </action>
|
![]() |
查看倉庫中的內容 |
能夠將資源上傳服務器(沒有創建與SVN同步) |
講SVN倉庫文件檢出(與SVN同步)---開發人員檢出同步修改 |
將本地文件更新到最新版本 |
將本地修改提交到服務器。 |
導出文件,與SVN倉庫不一樣步(商品上線,拷貝代碼) |
頁面局部刷新 |
![]() |
EasyUI |
以 jQuery Easy UI 1.3.2 版(從1.2.3版本 開始收費) |
![]() |
JavaScript引用文件: |
<!-- 先引入 jquery的 js --> <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.8.3.js"> </script> <!-- 引入 easyui的js --> <script type="text/javascript" src="${pageContext.request.contextPath }/js/easyui/jquery.easyui.min.js"></script> <!-- 引入國際化 js --> <script type="text/javascript" src="${pageContext.request.contextPath }/js/easyui/locale/easyui-lang-zh_CN.js"></script> <!-- 引入 默認樣式 css --> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/js/easyui/themes/default/easyui.css"/> <!-- 引入 icon圖標 css --> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/js/easyui/themes/icon.css"/>
|
easyUi 的layout控件的使用 |
![]() |
easyUi的accordion摺疊面板的使用 |
<!-- 摺疊面板 --> <!-- fit屬性,使當前div大小佔滿父容器 --> <div class="easyui-accordion" data-options="fit:true"> <!-- 經過iconCls 設置圖標,找 icon.css中 類定義 --> <div data-options="title:'基本功能',iconCls:'icon-mini-add'">面板一</div> <!-- 這裏每一個div就是一個面板 --> <div data-options="title:'高級功能'">面板二</div> <div data-options="title:'管理員功能'">面板三</div> </div>
|
easyUi的tabls 選項卡面板 |
<div data-options="region:'center'"> <!-- 選項卡面板 --> <div class="easyui-tabs" data-options="fit:true"> <div data-options="title:'選項卡一'">內容一</div> <!-- 這裏每一個div 就是一個選項卡 --> <!-- closeable 可關閉 --> <div data-options="title:'選項卡二',closable:true">內容二</div> <div data-options="title:'選項卡三'">內容三</div> </div> </div>
|
easyUI的window控件 |
$('#win').window('open'); // open a window |
$('#win').window('close'); // close a window |
示例代碼: |
<!-- 經過 easyui的 class 將任意 div 變爲 窗口 --> <!-- 爲窗口 設置標題屬性 和 寬高 --> <div id="mywindow" class="easyui-window" data-options="title:'自定義窗口',maximizable:false,minimizable:false,modal:true,closed:true" style="width:200px;height: 150px">測試內容</div> <input type="button" value="打開窗口" onclick="$('#mywindow').window('open');"/>
|
樹菜單ztree |
![]() |
JavaScript引用: |
<!-- 引入ztree --> <script type="text/javascript" src="${pageContext.request.contextPath }/js/ztree/jquery.ztree.all-3.5.js"></script> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/js/ztree/zTreeStyle.css"/>
|
樹是在ul標籤中顯示的:<ul id="basicTree" class="ztree"></ul>javascript |
案例一: 標準數據tree 一、 在顯示樹位置寫 <ul> 標籤 <!-- 顯示樹 --> <ul id="basicTree" class="ztree"></ul> 二、 經過js 編寫setting對象 // 設置樹參數 var setting = {}; 三、 設置樹節點數據 // 數據 var zNodes = [ {"name":"菜單一"}, // 每一個{} 就是一個節點 {"name":"菜單二"} ]; 四、初始化樹 // 初始化樹 $.fn.zTree.init($("#basicTree"), setting, zNodes);
|
複雜的樹: |
![]() |
經過url 屬性,完成樹節點跳轉css 經過icon屬性,定製節點圖標html |
PowerDesigner數據庫建模工具 |
powerDesigner的4中模型文件 |
CDM |
PDM |
OOM |
BPM |
使用powerDesigner設計PDM(物理數據模型) |
![]() |
![]() |
![]() |
![]() |
![]() |
兩個表創建關係 |
![]() |
![]() |
生成SQL語句,導入數據表 |
![]() |
登陸 |
一般會將登錄功能,編寫單獨Action (與用戶管理其餘功能分離開 )好處: 便於進行權限控制 |
判斷驗證碼是否正確:比較用戶輸入的驗證碼和session中的驗證碼是否一致 |
系統退出 |
function logoutFun() { // 詢問 用戶是否確認退出 $.messager.confirm("確認窗口","你肯定退出系統嗎?", function(isConfirm){ if(isConfirm){ // 確認退出 location.href = "${pageContext.request.contextPath}/invalidate.jsp"; } }); } |
超連接提交form表單數據 |
<form id = "loginform" action = "xxx.action" method = "post" > <input type = "test"/> <!-- 超連接的形式提交表單數據 --> <a href = "javascript:$('#loginform').submit();"></a> </form> |
代碼架構 |
Action層 |
public abstract class BaseAction extends ActionSupport { |
Service層 |
public abstract class BaseService { } |
整合 |
將 DAO 注入 BaseService前端 將 Service 注入 BaseActionjava |
MD5 加密 |
package cn.itcast.bos.utils; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Utils { /** * 使用md5的算法進行加密 */ public static String md5(String plainText) { byte[] secretBytes = null; try { secretBytes = MessageDigest.getInstance("md5").digest( plainText.getBytes()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("沒有md5這個算法!"); } String md5code = new BigInteger(1, secretBytes).toString(16);// 16進制數字 // 若是生成數字未滿32位,須要前面補0 for (int i = 0; i < 32 - md5code.length(); i++) { md5code = "0" + md5code; } return md5code; } public static void main(String[] args) { System.out.println(md5("123")); } } |
JavaScript實現頁面跳轉 |
實現頁面跳轉(退出效果) |
彈出遮罩窗口 |
<!-- 經過 easyui的 class 將任意 div 變爲 窗口 --> <!-- 爲窗口 設置標題屬性 和 寬高 modal:true,closed:true--> <div id="mywindow" class="easyui-window" data-options="title:'自定義窗口',maximizable:false,minimizable:false,modal:true,closed:true" style="width:200px;height: 150px">傳智播客</div> <input type="button" value="打開窗口" onclick="$('#mywindow').window('open');"/> |
修改密碼,進行form校驗 |
點擊修改密碼 : $('#editPwdWindow').window('open'); 彈出窗口。 輸入密碼,點擊肯定 : 執行js函數 ,對密碼進行校驗node |
// 得到新密碼和確認密碼的輸入內容 var newPass = $("#txtNewPass").val(); // document.getElementById("txtNewPass").value; var rePass = $("#txtRePass").val(); // 進行校驗 // 新密碼是否爲空 if($.trim(newPass)==""){ // 也能夠寫爲 jQuery.trim // 新密碼輸入爲空 $.messager.alert('警告','新密碼不能爲空或者空白字符!','warning'); return ; } // 兩次密碼是否一致 if($.trim(newPass) != $.trim(rePass)){ $.messager.alert('警告','兩次密碼輸入不一致!','warning'); return ; } |
JSON對象有兩種: 對象{} 數組[{},{}] |
map.put("result","success"); map.put("msg","修改密碼成功"); |
![]() |
$.post( function(data){ if(data.result == "success"){ $.messager.alert("信息", data.msg, "info"); }else{ $.messager.alert("信息", data.msg, "info"); } // 窗口關閉 $("#editPwdWindow").window('close'); }); |
返回json 格式 : |
對象 {key:value, key:value} mysql 數組 [{},{}]jquery |
結果數據轉換爲json 推薦:flexjson 框架web |
![]() |
貨物收派標準(業務部分) |
基礎設置模塊 |
![]() |
數據字典(用來下拉列表項) 舉例:線路類型、保險類型、商品分類、取件配置要求類別。包括取派員類型、簽收類型、受理備註說明、配載信息、返貨緣由、消單緣由、取消簽收類型、返貨拒絕類型、大物流類型 |
![]() |
將承接的貨物,按照重量和體積兩個標準,雙維度定義貨物的標準,以便將不一樣標準的貨物分給不一樣的收取和派送能力的人員,由運營部門制定 |
班車設置、線路管理信息、對應需求文檔"路由管理" |
管理取貨和送貨人的人員信息。 |
收派人員,取派時有固定時間段。 |
制定那個取派員,在什麼時間內進行工做。將不一樣取派員。進行一天的排班設置。容許取派員進行請假、替班 |
此功能區域爲行政區域列表,爲價格、商務網站、工做單始發站、目的站等來源的基礎數據 |
由於行政比較大,必然進行劃分。成爲不少個小分區。 |
什麼是定區?將不少分區。統一制定派去人員負責。成爲定區。在定區中管理客戶信息。客戶能夠被關聯定區上,自動獲取客戶對應負責的派送員 |
數據庫設計 |
根據文檔需求,使用PowerDesigner工具,繪製PDM (一對多,在多方加入外鍵) |
收派標準、取派員信息管理、區域管理、分區管理、定區管理 |
![]() |
![]() |
![]() |
數據庫表 |
收派標準表 |
取派員表 |
區域信息表 |
分區信息表 |
定區信息表 |
數據庫表 |
![]() |
收派標準的CRUD |
展現頁面(bc_standard 收派標準表) |
![]() |
增長業務分析: |
1.數據庫表 bc_standard表中主鍵爲:varchar 字符串類型,主鍵策略設爲UUID(主鍵爲字符串,UUID策略可讓字符串自增。assigned委派策略,字符串主鍵不會自增): |
<id name="id" type="java.lang.String"> <column name="id" length="32" /> <generator class="uuid" /> </id> |
2.在企業數據開發中,刪除數據大多使用邏輯刪除。(在數據表設置標記爲0:未刪除、1:已經刪除),而不會使用物理刪除 |
![]() |
3.頁面只須要輸入name、minweigth、maxweight能夠完成標準信息錄入 |
4.界面原型 |
![]() |
<div class="easyui-window" title="添加收派標準" id="addStandardWindow" collapsible="false" minimizable="false" maximizable="false" style="top:100px;left:200px"> <div region="north" style="height:31px;overflow:hidden;" split="false" border="false" > <div class="datagrid-toolbar"> <a id="save" icon="icon-save" href="javascript:commitStandardForm();" class="easyui-linkbutton" plain="true" >保存</a> </div> </div> <div region="center" style="overflow:auto;padding:5px;" border="false"> <form id="standardForm" action="${pageContext.request.contextPath }/standard_save.action" method="post"> <table class="table-edit" width="80%" align="center"> <tr class="title"> <td colspan="2">收派標準信息 <input type="hidden" name="id" id="id" /> </td> </tr> <tr> <td>標準名稱</td> <td><input id="name" name="name" type="text" class="easyui-validatebox" data-options="required:true" /></td> </tr> <tr> <td>最小重量</td> <td><input id="minweight" name="minweight" type="text" class="easyui-numberbox" /></td> </tr> <tr> <td>最大重量</td> <td><input id="maxweight" name="maxweight" type="text" class="easyui-numberbox" /></td> </tr> </table> </form> </div> |
<a>標籤如何提交表單? |
<a id="save" icon="icon-save" href="javascript:commitStandardForm();" class="easyui-linkbutton" plain="true" >保存</a> |
對應的JavaScript方法: |
// 點擊保存按鈕,提交標準form function commitStandardForm(){ // 先判斷form 是否經過校驗,若是經過 ,提交表單 if($('#standardForm').form('validate')){// 執行EasyUI 校驗方法 // 經過校驗 $('#standardForm').submit(); }else{ // 沒經過校驗 $.messager.alert('警告','表單存在非法數據,請從新輸入','warning'); } } |
java代碼,完成表單數據的添加 |
public class StandardAction extends BaseAction implements ModelDriven<Standard> { } public interface StandardService { } public class StandardServiceImpl extends BaseService implements StandardService { } 將DAO 注入 BaseService 將Service 注入 BaseAction |
easyUI的datagrid控件的使用 |
HTML表格數據的使用 |
![]() |
效果圖: |
rownumbers:顯示行號 pagination:顯示分頁條工具 |
Datagrid加載遠程數據(Json) |
標籤寫法: |
![]() |
json格式: |
![]() |
最終效果: |
![]() |
使用JavaScript建立Datagrid(推薦) |
![]() |
Datagrid的屬性分析 |
![]() |
Datagrid分頁pagination屬性 |
數據表格控件,使用有兩種方式: |
第一種:對HTML Table數據 應用表格效果 |
第二種:對table應用Datagrid,數據是綁定在服務器返回json格式數據 |
數據格式:[{key:value,key:value},{key:value,key:value}] |
該數據格式,能夠將數據顯示出來,可是不能解決分頁顯示的需求 |
顯示的數據記錄數量和 記錄總數數量是不一樣的 |
total表明總記錄數。ajax rows表明當頁記錄數據 |
當Datagrid啓用pagination分頁屬時,自動攜帶的請求參數: |
page:當前頁碼 rows:每頁多少條 |
響應參數: |
total:總記錄數。rows:當前數據 |
url:"${pageContext.request.contestPath}/xxxx.action" 發送請求 |
java代碼封裝實體類 |
/** * 封裝分頁查詢 結果數據 * * @author seawind * */ @SuppressWarnings("all") public class PageResponseBean { private long total; // 總記錄數 private List rows; // 當前頁數據記錄 } |
Datagrid插件的formatter列屬性控制絕具體的顯示 |
![]() |
jQueryEasyUI的Datagrid在制定的field,只能和返回對象之間屬性匹配,不能和返回對象的關聯對象的屬性匹配,關聯數據的複雜屬性則沒法顯示,則能夠自定義formatter |
![]() |
![]() |
收派標準修改 |
傳統實現方式:先根據Id查詢,回顯form數據,在進行修改 |
使用Datagrid,全部數據 緩存到客戶端瀏覽器,以對象的方式保存,點擊修改時,無需查詢服務器,直接從Datagrid獲取數據,回顯到from表單中 |
數據表格雙擊事件,點擊數據所在行,進行修改 |
onDblClickRow :doDblClickRow |
// 修改數據 function doDblClickRow(rowIndex, rowData){ // rowIndex行號,rowData 雙擊行數據 // form回顯 $('#name').val(rowData.name); $('#minweight').numberbox('setValue', rowData.minweight); $('#maxweight').numberbox('setValue', rowData.maxweight); $('#id').val(rowData.id) ; // 彈出修改窗口 $('#addStandardWindow').window('open'); } |
新增和修改使用同一個action的同一個方法 |
使用方法:saveOrUpdate(),會報錯: |
![]() |
產生錯誤的緣由:新增數據,id有值,可是值爲空串 "",空串對於saveOrUpdate方法,Hibernate認爲有值,執行update操做 |
解決方案: |
hibernate認爲空串是無值的設置,在xxx.hbm.xml文件中,添加unsaved-value = "" 屬性 |
<id name="id" type="java.lang.String" unsaved-value=""> <column name="id" length="32" /> <generator class="uuid" /> </id> |
此時此刻,hibernate認爲null 或者空串,都是臨時對象,執行save操做 |
收派標準的刪除操做 |
點擊刪除按鈕,獲取全部選中行的id |
![]() |
function doDelete(){ // 判斷是否選擇表格數據 var array = $('#grid').datagrid('getSelections'); if(array.length == 0){ // 一行也沒選 $.messager.alert('警告','刪除數據要先選中!','warning'); return ; } // 提交刪除form $('#delForm').submit(); } |
刪除的時候,只須要將數據表中的屬性deltag標記爲1,則爲刪除 |
注意:stratus2接收前臺傳過來的id數組時候,將id自動封裝到屬性中,而且已逗號和空格隔開,例如:1, 2(逗號後面有一個空格) |
取派員和區域管理 |
EasyUI,ExtJS,Flex,jQuery插件jqGrid |
取派員管理 |
主要管理,取派貨物快遞人員信息。 |
![]() |
![]() |
收派員的實體類 |
![]() |
添加取派員時,選擇已經存在的取派標準 |
解決方案:Ajax獲取收派標準列表,顯示select下拉列表 |
步驟: |
$.post("${pageContext.request.contextPath}/standard_ajaxlist.action",function(data){ }); |
數據回顯: |
<input class="easyui-combobox" data-options="url:'${pageContext.request.contextPath}/standard_ajaxlist.action',valueField:'id',textField:'name'" /> //url:獲取json數據路徑 //valueField:就是返回json中那個屬性生成的option的value屬性 //textField:返回json中那個屬性生成option的文本信息。 |
添加收派標準功能 |
<input class="easyui-combobox" name="standard.id" data-options="url:'${pageContext.request.contextPath}/standard_ajaxlist.action',valueField:'id',textField:'name'" /> |
JavaScript單擊事件: |
// 爲保存按鈕添加 點擊事件 $('#save').click(function(){ // 進行form 校驗 if($('#staffForm').form('validate')){ // 經過校驗 $('#staffForm').submit(); }else{ // 校驗失敗 $.messager.alert('警告','表單存在非法數據項,請從新輸入','warning'); } }); |
java後臺代碼: |
public class StaffAction extends BaseAction implements ModelDriven<Staff> { } public interface StaffService { } public class StaffServiceImpl extends BaseService implements StaffService { } 將StaffDAO 注入 BaseService 將StaffService 注入 BaseAction |
取派員修改 |
雙擊修改,爲表格數據添加雙擊事件: |
![]() |
基於Datagrid修改,直接使用數據表格內部緩存數據,進行form回顯。 |
回顯form數據: |
![]() |
取派員管理,做廢功能 |
思路:在CheckBox兩端,添加from,提交form全部勾選的CheckBox自動提交 |
<form action="${pageContext.request.contextPath }/staff_delBatch.action" method="post"> <div region="center" border="false"> <table id="grid"></table> </div> </form> |
頁面控制選中提交 |
// 做廢操做 function doDelete(){ // 先判斷 用戶是否選擇 var array = $('#grid').datagrid('getSelections'); if(array.length == 0){ $.messager.alert('警告','刪除前必須選擇!','warning'); }else{ $('#delForm').submit(); } } |
Excel讀寫操做 POI |
POI包含: |
Excel 2003以前爲xls格式文件,Excel 2007文件格式爲xlsx,若是解析Excel,先分清Excel文件版本,若是2003以前,使用HSSH的API進行解析,若是是2007Excel使用XSSF進行解析 |
HSSF的Excel解析(針對Excel 2003以前版本) |
使用POI讀寫Excel:解析步驟:Excel文件 ---->HSSFWorkbook工做簿對象,--->Sheet(工做鋪)---->Row(行) ---->cell(單元格) |
讀取Excel內容: |
public class PoiTest throws Exception { //獲取xls文件 HSSFWorkbook hssf = new HSSFWorkbook( new InputStream("文件地址")); //獲取sheet HSSFSheet sheet = hssf.getSheet("Sheet1");//經過名稱獲取sheet HSSFSheet sheet = hssf.getSheetAt(0);//根據下標獲取Sheet //解析sheet中每一行 for( Row row: sheet ) { //遍歷sheet中的每一行 //打印每一行中的單元格中的數據 for( Cell cell : row ) { //判斷單元格的數據類型 if( cell.getCellType() == Cell.CELL_TYPE_STRING) { //打印每個單元格中的數據 System.out.println( cell.getStringCellValue() ); } //單元格內的數據保存格式爲數字則用: System.out.println( cell.getNumberCellValue() ); } } } |
生成Excel表格: |
public class PoiTest throws Exception { //建立一個xls文件 HSSFWorkbook hssf = new HSSFWorkbook(); //建立sheet Sheet sheet = hssf.createSheet("new sheet"); //向sheet中寫入數據 HSSFRow row = sheet.createRow(0);//建立第一行數據 //向row中單元格中進行數據輸入 row.createCell(0).setCellValue("產品"); row.createCell(1).setCellValue("價格"); //將Excel的數據輸入到硬盤上 hssf.write(new FileOutputStream("地址")); } |
區域管理 |
![]() |
上傳Excel文件和一鍵上傳 |
在Datagrid控件添加toolbar添加按鈕 |
{ id : 'button-import', text : '批量導入',//導入按鈕的文本 iconCls : 'icon-save' } |
文件上傳的基本要求: |
1.客戶端form post提交,enctype = multipart/form-data、input file必須提供name屬性、服務器 Apache commons-fileuload、COS、JSPSmarUpload |
Struts2 框架默認使用文件上傳 jakarta 是 apache commons-fileupload |
企業中製做文件上傳,大多使用一鍵上傳的效果 (頁面中只提供一個按鈕,點擊按鈕就能夠完成文件上傳 ) ,原理??? |
原理:一鍵上傳,每每和Ajax效果結合,頁面不刷新。按鈕點擊--->觸發隱藏form input file瀏覽文件動做--->選定文件,觸發onChange事件--->提交form表單,target指向隱藏的iframe--->服務器完成文件上傳,將結果更新隱藏iframe |
使用jQuery插件 Ocupload http://code.google.com/p/ocupload/ 下載cupload 1.1.2.js |
使用: |
![]() |
頁面導入: |
<!-- 導入一鍵上傳 js --> <script type="text/javascript" src="${pageContext.request.contextPath }/js/ocupload/jquery.ocupload-1.1.2.js"></script> |
對按鈕添加一鍵上傳效果 // 對批量導入添加一鍵上傳效果 $('#button-import').upload({ name : 'upload' , // <input type="file" name="upload" action : '${pageContext.request.contextPath}/region_importXls.action', // 表單提交路徑 onComplete : function(response){ $.messager.alert('信息','文件上傳成功!','info'); } }); |
服務器代碼: |
![]() |
![]() |
使用log4j記錄日誌: |
// 使用log4j 日誌記錄器,記錄日誌 private static final Logger LOG = Logger.getLogger(RegionAction.class); |
記錄日誌 try { regionService.saveRegion(region); } catch (Exception e) { // 導入region失敗,記錄日誌 LOG.error("區域導入失敗,編號:" + region.getId(), e); } |
PinYin4J的使用 |
需求: 漢字 轉換拼音而且首字母縮寫(城市簡碼)、漢字轉換拼音全品(城市編碼) |
手動將jar包放入maven庫: |
mvn install:install-file -DgroupId=pinyin4j -DartifactId=pinyin4j -Dversion=2.5.0 -Dpackaging=jar -Dfile=D:\itcast\xxx.jar |
PinYin4J的用法: |
![]() |
分區管理 |
![]() |
添加: |
![]() |
// 爲添加分區 ,保存按鈕添加點擊 事件 $('#save').click(function(){ // 判斷form 校驗是否經過 if($('#subareaForm').form('validate')){ // 經過校驗 $('#subareaForm').submit(); }else{ // 提示 $.messager.alert('警告','表單存在非法數據項,請從新輸入!','warning'); } }); |
多條件查詢: |
![]() |
Excel下載 |
function doExport() { location.href="${pageContext.request.contextPath}/subarea_exportXls.action"; } |
![]() |
問題:如何將內存中的Excel對象,轉換輸入流?先將數據寫入內存緩存區中,在經過輸入流讀進來ByteArrayOutputStream |
![]() |
定區管理業務 |
![]() |
![]() |
定區管理(和分區管理的區別):區域管理針對天然行政區,行政區域比較大,不可能讓取派員負責整個行政區域,須要進行分區,將行政區域細分,成爲不少歌小區域(分區),須要爲分區指派 去拍人員,在分配區域和取派員的對應關係,可能幾個取派員共同負責幾個分區(定區) |
定區就是:取派員 負責幾個固定區域,負責物流取貨和送貨 |
---定區 關聯分區、關聯取派員 |
---定區 須要管理客戶信息,當客戶下單時,自動進行分單操做(爲客戶自動找到負責派去人員) |
添加定區: |
![]() |
定區添加負責人(取派員):略 |
定區關聯分區:略 |
定區添加功能: |
<form id="decidedzoneForm" action="${pageContext.request.contextPath }/decidedzone_saveOrUpdate.action" method="post"> |
點擊保存按鈕,提交form 表單 // 爲添加修改定區 ,保存按鈕 添加 click事件 $('#save').click(function(){ // 提交前,先校驗 form 輸入 if($('#decidedzoneForm').form('validate')){ $('#decidedzoneForm').submit(); }else{ $.messager.alert('警告','表單存在非法數據項,請從新輸入','warning'); } }); |
WebService:遠程數據訪問,分佈式的數據,去另外的系統上尋找數據 |
![]() |
遠程調用技術Hessian |
使用Hessian,完成兩個系統的結合,(兩個系統的相互調用) |
Hessian是一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI(java Remote Method Invocation 方法遠程調用)的功能,相比webService,Hessian更簡單,快捷。採用的是二進制RPC((RemoteProcedureCallProtocol)——遠程過程調用協議)協議),應爲採用二進制協議,因此它很適合發送二進制數據。 |
1.RMI是java首選遠程調用協議。很是高效穩定,特別是在數據結構複雜,數據量大的狀況下,與其餘通信協議的差距尤其明顯,可是不能誇語言 |
2.HttpInvoker使用java的序列化技術傳輸對象。與RMI在本質上是一致的,不能跨語言。 |
3.Hessian性能比RMI慢20%,可是經過二進制傳輸,跨語言。 |
4.Burap採用xml格式傳輸,僅在傳輸1條數據時速度尚可,一般狀況下,他的耗時是RMI的3倍 |
5.WebService通信耗時是RMI的10倍,傳輸xml數據,基於soap協議(Axis二、CXF) |
Dubbo是阿里巴巴的服務框架,經過RPC實現服務的傳輸和輸入功能,和Spring結合。調用 淘寶、支付寶的藉口,使用dubbo,dubbo底層爲Hessian |
定區關聯客戶,選擇Hessian |
Hessian例子: |
官網案例:http://hessian.caucho.com/doc/hessian-overview.xtp |
1.編寫服務器代碼: |
public interface HelloService { public String sayHello(String name); } public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "hello hessian , name: " + name; } } |
2.將Hessian藉口註冊爲一個Hessian服務: |
![]() |
運行tomcat服務器: |
![]() |
客戶端的編寫: |
若是是java客戶端,直接將服務器端的接口文件複製到客戶端便可(客戶端只須要interface接口,不須要接口的實現類) |
![]() |
編寫Hessian測試程序: |
// 編寫Hessian客戶端 String url = "http://localhost:8080/myserver/hello"; HessianProxyFactory hessianProxyFactory = new HessianProxyFactory(); // 建立接口代理程序 HelloService proxy = (HelloService) hessianProxyFactory.create(HelloService.class, url); String result = proxy.sayHello("itcast"); System.out.println(result); |
項目中的用法 |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
發佈Hessian 服務, 配置web.xml |
![]() |
編寫客戶端代碼: |
將 cn.itcast.crm.service(接口) 、 cn.itcast.crm.domain(實體類) 複製 客戶端代碼中 |
![]() |
客戶端 Hessian 和Spring整合 |
![]() |
在客戶端編寫Junit測試: |
![]() |
![]() |
選擇一個定區,點擊上面 關聯客戶按鈕,須要顯示form表單 |
查詢全部未制定的定區的客戶,查詢當前選定區已經關聯的客戶。 |
// 在關聯客戶的方法 function doAssociations(){ // 先判斷用戶是否選擇 定區數據 var rowData = $('#grid').datagrid('getSelected'); if(rowData == null){ // 未選中 $.messager.alert('警告','定區關聯客戶前,必需要先選中一個定區','warning'); }else{ // 已經選中 // 顯示關聯客戶的窗口 $('#customerWindow').window('open'); } } |
![]() |
須要查詢全部未關聯定區的客戶和已經關聯當前定區的客戶 |
提交表單,將客戶關聯到定區上,經過Hessian,將定區信息保存到CRM(Customer relationship management 客戶關係管理系統)中 |
頁面提交數據: |
![]() |
關聯操做,左移和右移 |
<input type="button" value="》》" id="toRight"><br/> <input type="button" value="《《" id="toLeft"> 爲左移和右移添加點擊事件 // 添加左右 移動關聯客戶事件 $('#toRight').click(function(){ // 將未關聯 移到 已經關聯 $('#associationSelect').append($('#noassociationSelect option:selected')); }); $('#toLeft').click(function(){ $('#noassociationSelect').append($('#associationSelect option:selected')); }); |
點擊關聯客戶,提交form表單 |
// 點擊關聯客戶,提交表單 $('#associationBtn').click(function(){ // 關聯select 中全部option 選中 $('#associationSelect option').attr('selected','selected'); // 提交表單 $('#customerDecidedZoneId').val($('#grid').datagrid('getSelected').id); // 爲定區隱藏域設置id $('#customerForm').submit(); }); |
取派業務分析 |
![]() |
業務受理: 受理環節,是宅急送業務的開始,做爲服務前端,客戶經過電話、網絡等多種方式進行委託,業務受理員經過與客戶交流,獲取客戶的服務需求和具體委託信息,將服務指令輸入公司系統。 業務受理後,業務人員須要將客戶信息,錄入爲 「業務通知單」(表明客戶一份快遞須要,客戶快遞申請) ,系統進行自動分單,產生「工單」(取派員取貨的任務) , 系統自動識別客戶對應定區的負責人員, 由指定取派員到客戶家取貨。
工做單功能 「工做單」(面向物流的數據,真是物流信息)
|
工做單: |
![]() |
![]() |
查臺轉單:當自動下單成功,可是因爲基礎信息錯誤,而致使分配的取貨人員錯誤,經過查臺轉單來解決此類失誤。 (分錯取派員,更換取派員 ) |
人工調度:在自動下單的時候,因爲取件地址沒法匹配取件人員,就轉入人工調度。 (業務人員,手動爲客戶,指定取派員 ) |
須要的數據庫 |
![]() ![]() |
![]() ![]() |
![]() |
![]() |
業務員輸入的內容(並非客戶輸入的) |
![]() |
頁面內容:頁面存在大form 表單 ,使用EasyUI form 控件 |
<!-- validatebox (驗證框) :非空、長度 --> <input type="text" class="easyui-validatebox" required="true" /> <!-- numberbox (數字框) :對validatebox擴展,只容許輸入數字 --> <input type="text" class="easyui-numberbox" required="true" /> <!-- combobox (下拉框) : 能夠對 <input> 元素,生成下拉列表 --> <!-- datebox (日曆控件) : 選擇日期 --> <input type="text" class="easyui-datebox" data-options="required:true,editable:false"/> |
整個頁面的數據都被包含在form標籤中: |
![]() |
檢查業務受理form表單,元素name屬性和 NoticeBill 實體類是否一致,爲form添加 點擊提交事件 |
// 點擊「新單」按鈕,將業務通知單 保存 $('#save').click(function(){ //from表單的Id if($("#noticebillForm").form('validate')){ $('#noticebillForm').submit(); }else{ $.messager.alert('警告','表單存在非法數據項!','warning'); } }); |
業務受理提交後的業務需求: |
在業務受理時,產生業務通知單的數據,在保存到數據庫的時候,須要進行自動分單的操做 一、 用客戶取貨地址經過遠程調用,訪問CRM,比較CRM系統中客戶地址 ,查找客戶對應定區編碼,再經過定區編碼得到 取派員信息 二、 若是不能在CRM中找到客戶對應定區,使用當前發件人地址去匹配分區地址 ,就能夠經過分區去管理定區編碼,匹配到取派員
若是自動分單成功,須要在數據表 qp_workbill 生成一條工單記錄 ! 若是自動分單失敗,進入人工調度環節 |
![]() |
![]() |
CRM系統中的客戶表: |
![]() |
修改CRM系統的對外訪問的接口 |
![]() |
修改crm系統中的實現類: |
![]() |
分區數據表---->addresskey(關鍵字)用於解析position地址,position(完整地址) |
![]() |
public class NoticeBillServiceImpl extends BaseService implements NoticeBillService { @Override public void saveNoticeBill(NoticeBill noticeBill) { // 將業務通知單數據保存到數據庫 noticeBillDAO.save(noticeBill); // 自動分單 // 1 、使用當前取件地址,去查詢CRM系統 定區編碼 String decidedZoneId = customerService.findDecidedZoneIdByCustomerAddress(noticeBill.getPickaddress()); if (decidedZoneId == null) { // 未查到 // 二、匹配分區 關鍵字 String[] addressArray = noticeBill.getPickaddress().split(" "); // 北京市海淀區 xxx路 1號樓 if (addressArray.length >= 2) { String addresskey = addressArray[1]; // 取第二個元素 做爲關鍵字 List<Subarea> list = subareaDAO.findByNamedQuery("Subarea.findByAddresskey", addresskey); // 只匹配到惟一的一個分區,並且這個分區已經關聯到定區 if (list.size() == 1 && list.get(0).getDecidedZone() != null) { // 自動分單成功 // 查到 (自動分單成功) DecidedZone decidedZone = list.get(0).getDecidedZone(); // 通知單 noticeBill.setStaff(decidedZone.getStaff()); noticeBill.setOrdertype("自動"); // 工單信息 WorkBill workBill = new WorkBill(); workBill.setNoticeBill(noticeBill); workBill.setStaff(decidedZone.getStaff()); workBill.setType("新"); workBill.setPickstate("新單"); workBill.setBuildtime(new Timestamp(System.currentTimeMillis())); workBill.setAttachbilltimes(0); workBill.setRemark(noticeBill.getRemark()); workBillDAO.save(workBill); } else { // 人工調度 noticeBill.setOrdertype("人工"); } } else { // 人工調度 noticeBill.setOrdertype("人工"); } } else { // 查到定區信息 (自動分單成功) DecidedZone decidedZone = decidedZoneDAO.findById(decidedZoneId); // 通知單 noticeBill.setStaff(decidedZone.getStaff()); noticeBill.setOrdertype("自動"); // 工單信息 WorkBill workBill = new WorkBill(); workBill.setNoticeBill(noticeBill); workBill.setStaff(decidedZone.getStaff()); workBill.setType("新"); workBill.setPickstate("新單"); workBill.setBuildtime(new Timestamp(System.currentTimeMillis())); workBill.setAttachbilltimes(0); workBill.setRemark(noticeBill.getRemark()); workBillDAO.save(workBill); } } } |
工單操做: |
![]() |
工單操做: |
1.查詢出系統全部的工單 |
2.追單:客戶已經提交業務申請,取派員好久都沒有去客戶家中取件。系統在工單表中將attrachbilltimes(追單次數 +1),在實際業務中利用短信平臺,催取貨員取貨 |
3.銷單:在取派員取件的過程當中,客戶選擇銷單,不進行快遞業務。系統在工單表中將type列設置爲 「銷」, |
工單數據表: |
![]() |
![]() |
調度模塊: |
1.查臺轉單:業務通知單 自動分單 成功 ,可是分錯人了, 須要爲通知單 從新手動更換 取派員 ! |
2.人工調度:業務通知單 自動分單失敗,進入人工調度,須要手動爲通知單指定取派員,生成工單! |
![]() |
結果: 已經將客戶的貨物,取回了物流公司!!! |
工做單管理(真正物流單) |
工做單實體類文檔; |
![]() |
實體類: |
![]() |
![]() |
Datagrid 行編輯模式 |
<script type="text/javascript"> $(function(){ $('#grid').datagrid({ // 工具欄 toolbar : [ { id : 'edit', text: '開始編輯第2行', iconCls : 'icon-edit', handler : function(){ $('#grid').datagrid('beginEdit',1);// 開始對一行進行編輯。 索引1 表明第二行 } }, { id : 'save', text: '保存', iconCls : 'icon-save', handler : function(){ $('#grid').datagrid('endEdit',1);// 結束對一行進行編輯。 索引1 表明第二行 } }, { id : 'cancel', text: '取消', iconCls : 'icon-cancel', handler : function(){ $('#grid').datagrid('cancelEdit',1);// 取消對一行進行編輯。 索引1 表明第二行 } } , { id : 'insert', text: '插入一行', iconCls : 'icon-add', handler : function(){ $('#grid').datagrid('insertRow',{ // 在第一行進行插入 index : 0 , row : {} }); } } ], // 表頭 (列信息) columns : [[ { field : 'code', title : '編號', width : 200 }, { field : 'name', title : '商品名稱', width : 200, editor: { type:'validatebox', // 編輯器類型 options: { // 編輯器屬性 required:true } } }, { field : 'price', title : '商品價格', width : 200 } ]], // 遠程數據 url : 'data.json', // 其它屬性 rownumbers:true, pagination:true, /* 當用戶完成編輯一行時觸發 rowIndex:編輯行的索引,從 0 開始 rowData:編輯行對應的記錄 changes:更改的字段/值對 */ onAfterEdit : function(rowIndex, rowData, changes){ alert('編輯完成'); } }); }); </script> |
![]() |
<script type="text/javascript"> // 成員變量,用來保存當前正在編輯行的行號,控制用戶當前只能同時編輯一行 var editIndex ; // 點擊新增一行 function doAdd(){ // 判斷當前是否正在編輯 if(editIndex != undefined){ $("#grid").datagrid('endEdit',editIndex); // 結束當前行編輯 // 觸發onAfterEdit 函數 } // 判斷當前已經沒有編輯行 if(editIndex==undefined){ // 在數據表格第一行 ,插入一個空行 $("#grid").datagrid('insertRow',{ index : 0, row : {} }); // 打開第一行編輯狀態 $("#grid").datagrid('beginEdit',0); // 將編輯的行號,保存成員變量 editIndex = 0; } } // 點擊保存 function doSave(){ $("#grid").datagrid('endEdit',editIndex ); // 執行 doAfterEdit 事件 } function doCancel(){ if(editIndex!=undefined){ $("#grid").datagrid('cancelEdit',editIndex); $("#grid").datagrid('deleteRow',editIndex); editIndex = undefined; } } //工具欄 var toolbar = [ { id : 'button-add', text : '新增一行', iconCls : 'icon-edit', handler : doAdd }, { id : 'button-cancel', text : '取消編輯', iconCls : 'icon-cancel', handler : doCancel }, { id : 'button-save', text : '保存', iconCls : 'icon-save', handler : doSave }]; // 定義列 var columns = [ [ { field : 'id', title : '工做單號', width : 120, align : 'center', editor :{ type : 'validatebox', options : { required: true } } }, { field : 'arrivecity', title : '到達地', width : 120, align : 'center', editor :{ type : 'validatebox', options : { required: true } } },{ field : 'product', title : '產品', width : 120, align : 'center', editor :{ type : 'validatebox', options : { required: true } } }, { field : 'num', title : '件數', width : 120, align : 'center', editor :{ type : 'numberbox', options : { required: true } } }, { field : 'weight', title : '重量', width : 120, align : 'center', editor :{ type : 'validatebox', options : { required: true } } }, { field : 'floadreqr', title : '配載要求', width : 220, align : 'center', editor :{ type : 'validatebox', options : { required: true } } }] ]; $(function(){ // 先將body隱藏,再顯示,不會出現頁面刷新效果 $("body").css({visibility:"visible"}); // 收派標準數據表格 $('#grid').datagrid( { iconCls : 'icon-forward', fit : true, border : true, rownumbers : true, striped : true, pageList: [30,50,100], pagination : true, toolbar : toolbar, url : "${pageContext.request.contextPath}/workordermanage_pageQuery.action", idField : 'id', columns : columns, onDblClickRow : doDblClickRow, onAfterEdit : function(rowIndex, rowData, changes){ editIndex = undefined; // 將當前正在編輯行 設置undefined // 提交ajax請求,將編輯行數據,以ajax方式,發送到服務器,完成保存 $.post("${pageContext.request.contextPath}/workordermanage_saveOrUpdate.action",rowData , function(data){ // 判斷data.result 是否 爲 success if(data.result == "success"){ $('#grid').datagrid('reload'); }else{ $.messager.alert('保存失敗',data.msg, 'error'); } }); } }); }); function doDblClickRow(rowIndex, rowData){ alert("雙擊表格數據..."); console.info(rowIndex); $('#grid').datagrid('beginEdit',rowIndex); editIndex = rowIndex; } // 搜索函數 function doSearch(value,name){ // alert("搜索項:"+name + ", 搜索內容:" + value); // 將查詢條件 緩存到 datagrid $('#grid').datagrid('load',{ conditionName : name, conditionValue : value }); } </script> </head> <body class="easyui-layout" style="visibility:hidden;"> <div data-options="region:'north'"> <!-- 編寫搜索框 --> <!-- prompt 默認提示內容 menu 搜索條件下拉選項 searcher 點擊搜索按鈕執行js函數名稱 --> <input id="ss" class="easyui-searchbox" style="width:300px" data-options="prompt:'請輸入您的查詢內容',menu:'#mm',searcher:doSearch"/> <div id="mm"> <div data-options="name:'arrivecity'">按照到達地搜索</div> <div data-options="name:'product'">按照貨物名稱搜索</div> </div> </div> <div region="center" border="false"> <table id="grid"></table> </div> </body> |
工做單快速錄入頁面: |
![]() |
Lucene(搜索)索引庫優化原理: |
會針對目標內容,先進行分詞創建全文索引 ,在用戶查找時,先查詢索引庫中詞條,根據詞條找到數據記錄id ,再根據id 查找數據庫記錄 ! |
![]() |
工做單搜索功能: |
1.對工做單使用 like模糊查詢時,實際上 數據庫內部索引沒法使用 ,須要逐條比較查詢內容,效率比較低 |
2.在數據量不少狀況下, 提供模糊查詢性能,使用lucene全文索引庫技術 |
最適合 場景 : 論壇和貼吧 |
將WEB-INF/lib目錄下的jar包導入maven庫中(僅限於當前工程使用) |
![]() ![]() |
查看Lucene產生的索引文件內容須要的工具:進入cmd,輸入該命令,運行jar包 |
![]() |
path:將分詞器產生的索引庫的目錄地址放在path欄中。 |
![]() |
自定義權限控制 |
權限模型數據模型 |
![]() |
![]() |
角色管理 |
![]() |
![]() |
保存角色,要上傳在Tree插件勾選菜單的id: |
首先獲取在Tree節點勾選的id,將衆多的id放在隱藏input中提交給後臺 |
角色表: |
![]() |
角色-功能權限 的中間表 |
![]() |
// 點擊保存 $('#save').click(function(){ if($("#roleForm").form('validate')){ // 得到勾選ztree節點 var treeObj = $.fn.zTree.getZTreeObj("functionTree"); var nodes = treeObj.getCheckedNodes(true); // 將多個勾選id 轉換爲字符串,用, 分隔 var ids = []; for(var i=0;i<nodes.length; i++){ ids.push(nodes[i].id);// 將id 加入數組 } // 放入form 隱藏域 $('#functionIds').val(ids.join(",")); $("#roleForm").submit(); }else{ $.messager.alert('警告','表單存在非法數據項','warning'); } }); }); |
添加角色的業務:新增一個角色的同時,給角色受權:修改角色,功能表的中間關聯表:添加一個角色,授不少權限 |
@Override public void saveRole(Role role, String functionIds) { // 將role信息保存角色表 roleDAO.save(role); // 持久態 // 創建 role 和 function聯繫,向role_function 中間表插入數據 if (functionIds != null) { String[] ids = functionIds.split(","); for (String id : ids) { Function function = funtionDAO.findById(id); // 功能權限 role.getFunctions().add(function); // 多對多關聯,向中間表插入數據 } } } |
用戶管理: |
![]() |
![]() |
![]() |
![]() |
public class User implements java.io.Serializable { // Fields private String id; // 編號 修改生成策略 uuid private String username; // 用戶名 private String password; // 密碼 private Double salary; // 工資 private Date birthday; // 生日 private String gender; // 性別 private String station; // 單位 private String telephone; // 電話 private String remark; // 電話 } |
使用代理進行權限攔截 |
基於 自定義註解 + 反射 + 代理 實現方法級別細粒度權限控制 |
URL粗粒度權限控制, 好比admin登錄,訪問 /admin/* 開始頁面 |
思路:1.自定義註解。2.爲目標方法建立代理。3.在代理中經過反射解析信息,控制權限 |
自定義註解 |
/** * 自定義註解 * * @author seawind * */ @Retention(RetentionPolicy.RUNTIME) // 運行時使用 @Target(value = { ElementType.METHOD }) // 修改方法 @Inherited // 使用註解應用具備繼承性 public @interface Privilege { String value(); // 這個屬性表明訪問業務方法 須要權限 } |
在須要進行權限控制業務方法上,使用該註解 |
// 業務方法 --- 添加用戶 @Privilege("添加用戶") public String save() { // 調用業務層 保存用戶 userService.saveUser(user); return "saveSUCCESS"; } |
攔截器攔截自定義註解: |
/** * 自定義 登錄攔截器 * * @author seawind * */ public class LoginInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { // 從Session 中獲取登錄用戶信息 User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user"); if (user == null) { // 沒有登錄 // 設置錯誤信息 ActionSupport action = (ActionSupport) invocation.getAction(); action.addActionError("你還未登錄或者長時間未使用,請從新登錄!"); return "login"; // 登錄頁面 } else { // 已經登錄 return invocation.invoke(); } } } |
/** * 權限控制攔截器 * * @author seawind * */ @SuppressWarnings("all") public class PrivilegeInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { // 假設 用戶已經登錄 // 一、 判斷目標Action業務方法上,是否具備Privilege 註解 Class c = invocation.getAction().getClass(); // 目標Action的Class對象 String methodName = invocation.getProxy().getMethod(); // 目標業務方法名稱 Method method = c.getDeclaredMethod(methodName); // 判斷是否具備註解 if (method.isAnnotationPresent(Privilege.class)) { // 有註解 ,須要權限 // 二、 得到註解中須要權限 Privilege privilege = method.getAnnotation(Privilege.class); String needPrivilege = privilege.value(); // 三、判斷當前用戶是否具備該權限 User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user"); if (PrivilegeUtils.checkHasPrivilege(user, needPrivilege)) { // 有權限 return invocation.invoke(); } else { // 沒有權限 return "noprivilege"; } } else { // 無註解,不須要權限 return invocation.invoke(); } } } |
工具類: |
/** * 判斷 用戶是否具備 訪問權限 * * @param user * @param needPrivilege * @return */ public static boolean checkHasPrivilege(User user, String needPrivilege) { // admin 直接放行 if (user.getUsername().equals("admin")) { return true; } // 正常判斷流程 Role role = user.getRole(); if (role == null) { // 當前登陸用戶沒角色,沒權限 return false; } else { // 有角色 Set<Function> functions = role.getFunctions(); for (Function function : functions) { if (function.getName().equals(needPrivilege)) { // 知足權限 return true; } } return false; } } |
實現細粒度顯示控制 ,控制到頁面內部每個按鈕的顯示,使用 自定義標籤來實現 |
![]() |
步驟一: 自定義標籤類(使用簡單標籤庫 ) |
/** * 自定義標籤類 * * @author seawind * */ public class PrivilegeTag extends SimpleTagSupport { // 接收屬性 private String value; // 顯示內容須要的權限 public void setValue(String value) { this.value = value; } @Override public void doTag() throws JspException, IOException { User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user"); if (PrivilegeUtils.checkHasPrivilege(user, value)) { // 具備權限, 顯示標籤體內容 this.getJspBody().invoke(null); // 等價 // this.getJspBody().invoke(this.getJspContext().getOut()); } } } |
步驟二: 配置標籤描述文件 tld |
![]() |
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"> <tag> <name>privilege</name> <tag-class>cn.itcast.bos.web.tag.PrivilegeTag</tag-class> <body-content>scriptless</body-content> <!-- 定義屬性 --> <attribute> <name>value</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <!-- 接收EL --> </attribute> </tag> |
步驟三: 在jsp導入標籤庫 |
<%@taglib uri="http://www.itcast.cn/tag" prefix="itcast"%> <itcast:privilege value="收派標準"> { id : 'button-add', text : '增長', iconCls : 'icon-add', handler : doAdd }, </itcast:privilege> |
應用權限以後,控制頁面顯示 |
1.動態菜單:根據用戶權限,顯示菜單(粗粒度的控制) |
思想:從Session中獲取用戶,根據session中的用戶關聯查詢出菜單 |
![]() |
2.動態功能按鈕(細粒度的控制) |
思想:自定義標籤,控制頁面按鈕是否顯示 |
ztree寫法: |
// 初始化ztree菜單 $(function() { var setting = { data : { key : { title : "t" // 鼠標在停留在菜單上提示 }, simpleData : { // 簡單數據 enable : true, pIdKey: "parentId", } }, callback : { onClick : onClick } }; function onClick(event, treeId, treeNode, clickFlag) { if (treeNode.click != false) { open(treeNode.name, treeNode.page); } } |
JPBM工做流 |
![]() |
jpbm流程圖: |
![]() |
保存後生成兩個文件: |
1. process.jpdl.xml 流程描述文件 |
2.process.png 流程圖片 |
全部流程 基本辦理模式都是類似的, 先登錄系統, 查詢個人我的任務, 辦理我的任務, 流程流轉 !! |
什麼是JBPM ? |
JBPM,全稱是Java Business Process Management(業務流程管理) |
是一款開源工做流框架 |
企業主流工做流產品: JBPM、OSWorkFlow、 OFBIZ、 |
OFBIZ 不少大公司在用 ,構建大中型企業級、跨平臺、跨數據庫、跨應用服務器的多層、分佈式電子商務類WEB應用系統的框架。 |
企業主流JBPM版本, 3.x 、4.x |
JBPM 從5.x 開始分流 ,原來JBPM開發團隊,離開JBOSS公司 ,原開發團隊寫工做流框架叫作Activity (是對JBPM 延續 ,數據庫技術使用 MyBatis )、JBOSS公司另一個團隊推出 JBPM5 (和 JBPM4沒有任務關係 ) |
圖形和xml對應的節點: |
![]() |
<process name="holiday" xmlns="http://jbpm.org/4.4/jpdl"> <start name="start1" g="211,21,48,48"> <transition name="to 員工請假申請" to="員工請假申請" g="-95,-17"/> </start> <end name="end1" g="206,288,48,48"/> <task name="員工請假申請" g="188,105,92,52"> <transition name="to 部門經理審批" to="部門經理審批" g="-95,-17"/> </task> <task name="部門經理審批" g="192,192,92,52"> <transition name="to end1" to="end1" g="-47,-17"/> </task> </process> |
jbpm的配置文件 |
jbpm.cfg.xml (JBPM框架核心配置文件) |
jbpm.hibernate.cfg.xml (hibernate 配置文件、修改數據庫鏈接參數) |
修改 hibernate 鏈接參數時 ,改成mysql參數 |
<property name="hibernate.dialect"> org.hibernate.dialect.MySQL5InnoDBDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="hibernate.connection.url"> jdbc:mysql:///jbpm01 </property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">abc</property> <property name="hibernate.hbm2ddl.auto">update</property> |
注意: 方言必需要用 org.hibernate.dialect.MySQL5InnoDBDialect,不然在流程結束時會報一個異常! |
建立jpbm |
![]() |
![]() |
tasks:任務。 |
![]() |
將xml文件和png圖片放在桌面上,將整兩個文件打包成zip壓縮包。將zip壓縮包上傳項目便可 |
![]() |
上傳後數據庫: |
![]() |
流程管理模塊 |
![]() |
![]() |
中轉流程 |
![]() |
![]() |
工做單 預留字段: |
![]() |
具體畫出流程圖: |
![]() |
<task name="中轉環節" g="196,99,92,52" candidate-groups="業務員" form="page_zhongzhuan__transferinfo.action"> <transition name="to 入庫" to="入庫" g="-47,-17"/> </task> <task name="入庫" g="381,118,92,52" candidate-groups="倉庫管理員" form="page_zhongzhuan__instore_complete.action"> <transition name="to 出庫" to="出庫" g="-47,-17"/> </task> <task name="出庫" g="383,198,92,52" candidate-groups="倉庫管理員" form="page_zhongzhuan__outstore_complete.action"> <transition name="to 配送簽收" to="配送簽收" g="-71,-17"/> </task> <task name="配送簽收" g="382,277,92,52" candidate-groups="取派員" form="page_zhongzhuan__receiveinfo_complete.action"> <transition name="to end1" to="end1" g="-47,-17"/> </task> |