3.宅急送(物流項目)

須要的其餘技術:
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
{
  @Resource( name = "userService" )
  protected UserService userService; }
public class LoginAction extends BaseAction implements ModelDriven<User> {
  public String execute()
  {
    userService.login( User user ); // 直接使用 userService
  }
} 將能夠複用代碼 抽取 BaseAction 中   
 Service層 
public abstract class BaseService 
{
  @Resource( name = "userDao" )
  protected GenericDAO<User> userDao; } public interface UserService {
  public User login( User user ); } public class UserServiceImpl extends BaseService implements UserService {
  public User login( User user )
  {
    
  }
}
BaseService 實現代碼複用, UserService 接口和Web層整合,UserServiceImpl 業務實現類
整合

將 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>
相關文章
相關標籤/搜索