1. 程式人生 > >JavaWeb專案練習--支付模組、後臺部分模組(手風琴下拉選單、上傳技術)

JavaWeb專案練習--支付模組、後臺部分模組(手風琴下拉選單、上傳技術)

易寶支付

在這裡插入圖片描述

線上支付的兩種形式:
	1.	電商與銀行直連!
		安全
		不收手續費
		不與小電商合作!

	2.	第三臺支付平臺
		支付寶
		易寶
		財富通
	好處:
		不安全
		收手續費(1%)
		小電商可以與其合作!

需要在第三方註冊賬戶
	需要認證!
	我們有一個易寶的測試賬戶
	錢轉過去就要不回來了!

易寶支付
	1 去銀行
		易寶給了我們一個網址(支付閘道器),重定向到這個地址即可!
		還需要給這個地址後新增13+1個引數!
			https://www.yeepay.com/app-merchant-proxy/node?
			p0_Cmd=Buy
			&p1_MerId=10001126856
			&p2_Order=123456
			&p3_Amt=1234.56
			&p4_Cur=CNY
			&p5_Pid=
			&p6_Pcat=&
			p7_Pdesc=
			&p8_Url=http://localhost:8080/bookstore/OrderServlet?method=back
			&p9_SAF=
			&pa_MP=
			&pd_FrpId= ICBC-NET-B2C
			&pr_NeedResponse=1
			&hmac=dd17580a3ca176ba62d6d348583ba88b

易寶回撥:
點對點:易寶直接訪問電商,這裡沒有客戶端什麼事了。
•	這種方式是必須要使用的,我們這種方式是收不到的!因為我們沒有固定IP
•	易寶有一個重發機制,如果它訪問你,你不給它回信息,它會一直重發!
•	電商需要返回一個以SUCCESS開頭的字串即可!
		引導客戶端瀏覽器重定向到電商。是讓客戶端訪問電商!
•	可以不使用的!

hmac:13引數值+keyValue(金鑰) + 演算法(md5)
	13引數值:自己設定的!
	keyValue:易寶在我們註冊後發給我們的,這個東東只有我們和易寶知道!
	底層為md5的演算法:PaymentUtil.buildHmac(14個),它返回hmac


1) 支付(去銀行)
	重定向!
	13+1個引數!

在這裡插入圖片描述

2) 支付(銀行回饋)
	校驗訪問者是否為易寶
	修改訂單的狀態

在這裡插入圖片描述

servlet

	/**
	 * 支付(去銀行)
	 * @param request
	 * @param response
	 * @return
	 * @throws ServletException
	 * @throws IOException
	 */
	public String pay(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		Properties props = new Properties();
		InputStream input = this.getClass().getClassLoader()
				.getResourceAsStream("merchantInfo.properties");
		props.load(input);
		/*
		 * 準備13引數
		 */
		String p0_Cmd = "Buy";
		String p1_MerId = props.getProperty("p1_MerId");
		String p2_Order = request.getParameter("oid");
		String p3_Amt = "0.01";
		String p4_Cur = "CNY";
		String p5_Pid = "";
		String p6_Pcat = "";
		String p7_Pdesc = "";
		String p8_Url = props.getProperty("p8_Url");
		String p9_SAF = "";
		String pa_MP = "";
		String pd_FrpId = request.getParameter("pd_FrpId");
		String pr_NeedResponse = "1";
		
		//計算hmac
		String keyValue = props.getProperty("keyValue");
		String hmac = PaymentUtil.buildHmac(p0_Cmd, p1_MerId, p2_Order, p3_Amt,
				p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP,
				pd_FrpId, pr_NeedResponse, keyValue);
		
		//連線易寶網址和 13+1 個引數
		StringBuilder url = new StringBuilder(props.getProperty("url"));
		url.append("?p0_Cmd=").append(p0_Cmd);
		url.append("&p1_MerId=").append(p1_MerId);
		url.append("&p2_Order=").append(p2_Order);
		url.append("&p3_Amt=").append(p3_Amt);
		url.append("&p4_Cur=").append(p4_Cur);
		url.append("&p5_Pid=").append(p5_Pid);
		url.append("&p6_Pcat=").append(p6_Pcat);
		url.append("&p7_Pdesc=").append(p7_Pdesc);
		url.append("&p8_Url=").append(p8_Url);
		url.append("&p9_SAF=").append(p9_SAF);
		url.append("&pa_MP=").append(pa_MP);
		url.append("&pd_FrpId=").append(pd_FrpId);
		url.append("&pr_NeedResponse=").append(pr_NeedResponse);
		url.append("&hmac=").append(hmac);
		
		//重定向到易寶
		response.sendRedirect(url.toString());
		return null;
	}
	
	/**
	 * 易寶回撥方法
	 * 		我們需要判斷呼叫此方法的是否為易寶
	 * @param request
	 * @param response
	 * @return
	 * @throws ServletException
	 * @throws IOException
	 */
	public String back(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		/*
		 * 1. 獲取11 + 1
		 */
		String p1_MerId = request.getParameter("p1_MerId");
		String r0_Cmd = request.getParameter("r0_Cmd");
		String r1_Code = request.getParameter("r1_Code");
		String r2_TrxId = request.getParameter("r2_TrxId");
		String r3_Amt = request.getParameter("r3_Amt");
		String r4_Cur = request.getParameter("r4_Cur");
		String r5_Pid = request.getParameter("r5_Pid");
		String r6_Order = request.getParameter("r6_Order");
		String r7_Uid = request.getParameter("r7_Uid");
		String r8_MP = request.getParameter("r8_MP");
		String r9_BType = request.getParameter("r9_BType");

		String hmac = request.getParameter("hmac");

		/*
		 * 2. 校驗訪問者是否為易寶!
		 */
		Properties props = new Properties();
		InputStream input = this.getClass().getClassLoader()
				.getResourceAsStream("merchantInfo.properties");
		props.load(input);
		String keyValue = props.getProperty("keyValue");

		boolean bool = PaymentUtil.verifyCallback(hmac, p1_MerId, r0_Cmd,
				r1_Code, r2_TrxId, r3_Amt, r4_Cur, r5_Pid, r6_Order, r7_Uid,
				r8_MP, r9_BType, keyValue);
		
		if(!bool) {//如果校驗失敗
			request.setAttribute("msg", "請走正常流程支付!");
			return "f:/jsps/msg.jsp";
		}
		
		/*
		 * 3. 獲取狀態訂單,確定是否要修改訂單狀態,以及新增積分等業務操作
		 */
		orderService.pay(r6_Order);//有可能對資料庫進行操作,也可能不操作!
		
		/*
		 * 4. 判斷當前回撥方式
		 *   如果為點對點,需要回饋以success開頭的字串
		 */
		if(r9_BType.equals("2")) {
			response.getWriter().print("success");
		}
		
		/*
		 * 5. 儲存成功資訊,轉發到msg.jsp
		 */
		request.setAttribute("msg", "支付成功!賣家發貨中···");
		return "f:/jsps/msg.jsp";
	}

merchantInfo.properties

p1_MerId=10001126856
keyValue=69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl
p8_Url=http\://localhost\:8080/bookstore/OrderServlet?method\=back
url=https\://www.yeepay.com/app-merchant-proxy/node

PaymentUtil

public class PaymentUtil {

	private static String encodingCharset = "UTF-8";
	
	/**
	 * 生成hmac方法
	 * 
	 * @param p0_Cmd 業務型別
	 * @param p1_MerId 商戶編號
	 * @param p2_Order 商戶訂單號
	 * @param p3_Amt 支付金額
	 * @param p4_Cur 交易幣種
	 * @param p5_Pid 商品名稱
	 * @param p6_Pcat 商品種類
	 * @param p7_Pdesc 商品描述
	 * @param p8_Url 商戶接收支付成功資料的地址
	 * @param p9_SAF 送貨地址
	 * @param pa_MP 商戶擴充套件資訊
	 * @param pd_FrpId 銀行編碼
	 * @param pr_NeedResponse 應答機制
	 * @param keyValue 商戶金鑰
	 * @return
	 */
	public static String buildHmac(String p0_Cmd,String p1_MerId,
			String p2_Order, String p3_Amt, String p4_Cur,String p5_Pid, String p6_Pcat,
			String p7_Pdesc,String p8_Url, String p9_SAF,String pa_MP,String pd_FrpId,
			String pr_NeedResponse,String keyValue) {
		StringBuilder sValue = new StringBuilder();
		// 業務型別
		sValue.append(p0_Cmd);
		// 商戶編號
		sValue.append(p1_MerId);
		// 商戶訂單號
		sValue.append(p2_Order);
		// 支付金額
		sValue.append(p3_Amt);
		// 交易幣種
		sValue.append(p4_Cur);
		// 商品名稱
		sValue.append(p5_Pid);
		// 商品種類
		sValue.append(p6_Pcat);
		// 商品描述
		sValue.append(p7_Pdesc);
		// 商戶接收支付成功資料的地址
		sValue.append(p8_Url);
		// 送貨地址
		sValue.append(p9_SAF);
		// 商戶擴充套件資訊
		sValue.append(pa_MP);
		// 銀行編碼
		sValue.append(pd_FrpId);
		// 應答機制
		sValue.append(pr_NeedResponse);
		
		return PaymentUtil.hmacSign(sValue.toString(), keyValue);
	}
	
	/**
	 * 返回校驗hmac方法
	 * 
	 * @param hmac 支付閘道器發來的加密驗證碼
	 * @param p1_MerId 商戶編號
	 * @param r0_Cmd 業務型別
	 * @param r1_Code 支付結果
	 * @param r2_TrxId 易寶支付交易流水號
	 * @param r3_Amt 支付金額
	 * @param r4_Cur 交易幣種
	 * @param r5_Pid 商品名稱
	 * @param r6_Order 商戶訂單號
	 * @param r7_Uid 易寶支付會員ID
	 * @param r8_MP 商戶擴充套件資訊
	 * @param r9_BType 交易結果返回型別
	 * @param keyValue 金鑰
	 * @return
	 */
	public static boolean verifyCallback(String hmac, String p1_MerId,
			String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt,
			String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid,
			String r8_MP, String r9_BType, String keyValue) {
		StringBuilder sValue = new StringBuilder();
		// 商戶編號
		sValue.append(p1_MerId);
		// 業務型別
		sValue.append(r0_Cmd);
		// 支付結果
		sValue.append(r1_Code);
		// 易寶支付交易流水號
		sValue.append(r2_TrxId);
		// 支付金額
		sValue.append(r3_Amt);
		// 交易幣種
		sValue.append(r4_Cur);
		// 商品名稱
		sValue.append(r5_Pid);
		// 商戶訂單號
		sValue.append(r6_Order);
		// 易寶支付會員ID
		sValue.append(r7_Uid);
		// 商戶擴充套件資訊
		sValue.append(r8_MP);
		// 交易結果返回型別
		sValue.append(r9_BType);
		String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue);
		return sNewString.equals(hmac);
	}
	
	/**
	 * @param aValue
	 * @param aKey
	 * @return
	 */
	public static String hmacSign(String aValue, String aKey) {
		byte k_ipad[] = new byte[64];
		byte k_opad[] = new byte[64];
		byte keyb[];
		byte value[];
		try {
			keyb = aKey.getBytes(encodingCharset);
			value = aValue.getBytes(encodingCharset);
		} catch (UnsupportedEncodingException e) {
			keyb = aKey.getBytes();
			value = aValue.getBytes();
		}

		Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
		Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
		for (int i = 0; i < keyb.length; i++) {
			k_ipad[i] = (byte) (keyb[i] ^ 0x36);
			k_opad[i] = (byte) (keyb[i] ^ 0x5c);
		}

		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {

			return null;
		}
		md.update(k_ipad);
		md.update(value);
		byte dg[] = md.digest();
		md.reset();
		md.update(k_opad);
		md.update(dg, 0, 16);
		dg = md.digest();
		return toHex(dg);
	}

	public static String toHex(byte input[]) {
		if (input == null)
			return null;
		StringBuffer output = new StringBuffer(input.length * 2);
		for (int i = 0; i < input.length; i++) {
			int current = input[i] & 0xff;
			if (current < 16)
				output.append("0");
			output.append(Integer.toString(current, 16));
		}

		return output.toString();
	}

	/**
	 * 
	 * @param args
	 * @param key
	 * @return
	 */
	public static String getHmac(String[] args, String key) {
		if (args == null || args.length == 0) {
			return (null);
		}
		StringBuffer str = new StringBuffer();
		for (int i = 0; i < args.length; i++) {
			str.append(args[i]);
		}
		return (hmacSign(str.toString(), key));
	}

	/**
	 * @param aValue
	 * @return
	 */
	public static String digest(String aValue) {
		aValue = aValue.trim();
		byte value[];
		try {
			value = aValue.getBytes(encodingCharset);
		} catch (UnsupportedEncodingException e) {
			value = aValue.getBytes();
		}
		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance("SHA");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
		return toHex(md.digest(value));

	}
	
//	public static void main(String[] args) {
//		System.out.println(hmacSign("AnnulCard1000043252120080620160450.0http://localhost/SZXpro/callback.asp榪?4564868265473632445648682654736324511","8UPp0KE8sq73zVP370vko7C39403rtK1YwX40Td6irH216036H27Eb12792t"));
//	}
}

後臺模組(部分示例)

後臺的內容,必須要設定許可權!
使用者可以訪問一個網站的哪些內容?
	dao:不行
	service:不行
	servlet:能!
	jsp:能!
  使用者可以訪問的只有WEB層!

1 分類管理
	分類管理:
	新增分類
	檢視所有分類
	刪除分類
	按id查詢
	修改分類
1.1 相關類建立
	domain:Category
	dao:CategoryDao
	service:CategoryService
	admin.web.servlet:AdminCategoryServlet(為管理員提供單獨的Servlet,然後給這個Servlet新增過濾器!)
1.2 檢視所有分類
	left.jsp上的選單修改一下指向:AdminCategoryServlet#findAll()
	findAll():
   呼叫service得到所有的分類List<Category>
	儲存到request域,轉發到/adminjsps/admin/category/list.jsp
	list.jsp:修改頁面,顯示所有分類!

1.3 新增分類
	add.jsp(表單頁面)
	AdminCatetgoryServlet#add():
	封裝表單資料;
	補全:cid
	呼叫service方法完成新增工作
	呼叫findAll()方法
	service#add(Category c):略
	dao#add(Catetgory c):略

1.4 刪除分類
	list.jsp(刪除連結) 
	AdminCategoryServlet#delete()
	獲取引數:cid
	呼叫service方法完成刪除!
	如果出現異常,儲存異常資訊,轉發到msg.jsp顯示
	呼叫findAll()
	service#delete(String cid):
	通過cid檢視該分類下的圖書本數,如果大於0,丟擲異常;
	如果等於0,刪除該分類;
1.5 修改分類
	修改分為兩步:1、載入分類, 2、修改分類
	第一步:載入分類
		list.jsp(修改連結)  
		AdminCategoryServlet#editPre()
		獲取cid
		通過cid來呼叫service方法,得到Category物件
		儲存到request域中,轉發到mod.jsp
		mod.jsp:把當前的Category物件顯示到表單中
	第二步:修改分類
		mod.jsp(提交表單)
		AdminCategoryServlet#edit()
		封裝表單資料
		呼叫service方法完成修改工作
		呼叫findAll()


2 圖書管理
	圖書管理(我的)
		檢視所有圖書
	按id查詢
	刪除圖書
	修改圖書
	新增圖書(上傳圖片)
2.1 相關類建立
	web.servlet.admin:
	AdminBookServlet;
	AdminAddBookServlet(新增圖書,包含上傳):上傳不能使用BaseServlet,因為BaseServlet中需要使用getParameter()方法,而上傳getParameter()方法就不能再使用了。
2.2 查詢所有圖書
	left.jsp(選單項(檢視圖書連結))
	AdminBookServlet#findAll()
	查詢所有圖書,儲存到request
	轉發到/adminjsps/admin/book/list.jsp
	list.jsp:迴圈遍歷所有圖書
2.3 載入圖書
	list.jsp(點選圖名或圖片)  
	AdminBookServlet#load()
	獲取bid,通過bid呼叫BookService方法得到Book物件
	儲存到request中,轉發到/adminjsps/admin/book/desc.jsp
	desc.jsp:把當前book物件顯示到表單中

2.4 新增圖書
	新增圖書分兩步:
	1.	載入所有分類,到add.jsp中顯示!
		left.jsp(選單項:新增圖書) 
		AdminBookServlet#addPre():
		查詢所有分類,儲存到request域,轉發到add.jsp
		在add.jsp中迴圈遍歷所有分類,顯示在<select>中
	
	2.	新增圖書
		上傳三步:
		建立工廠
		建立解析器
		解析request得到表單欄位!
		把表單欄位封裝到Book物件中
		儲存上傳檔案,把儲存的路徑設定給Book的image屬性。
		呼叫service方法儲存Book物件到資料庫中
		呼叫findAll()

2.5 刪除圖書
	book表與orderitem有關聯關係!
	刪除圖書不是真的資料庫表中刪除記錄,而是給book表新增一個del欄位,它是booleanod型別,表示是否已刪除!
	沒有被刪除的圖書,該列的值為false,否則為true
	處理問題:
		修改BookDao:所有與查詢相關的方法,都需要新增where條件,即del=false
		修改Book類,新增del屬性!
	
	刪除圖書:其實就是把表的del列修改為true!
	
		desc.jsp(del按鈕) 
		AdminBookServlet#del()
		獲取bid
		呼叫service方法完成刪除
		返回列表,即呼叫findAll()
	
2.6 編輯圖書
	dsec.jsp(編輯按鈕) 
	AdminBookServlet#edit()
	封裝表單資料(必須讓頁面保證把image傳遞過來)
	要求頁面必須新增一個隱藏欄位,把原來的圖片路徑傳遞過來!
	呼叫service方法完成刪除
	return findAll()

servet (上傳技術新增圖書)

public class AdminAddBookServlet extends HttpServlet {
	private BookService bookService = new BookService();
	private CategoryService categoryService = new CategoryService();
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		/*
		 * 一.把表達那資料封裝到Book物件中
		 * 		上傳三步:1.建立工廠、  2.得到解析器、 3. 使用解析器解析request物件得到List<FileItem> 
		 * 	
		 */
		//1.建立工廠
		DiskFileItemFactory factory = new DiskFileItemFactory();
		
		//2.使用工廠得到解析器
		ServletFileUpload sfu = new ServletFileUpload(factory);
		
		try {
			//3.使用解析器解析request,得到FileItem集合
			List<FileItem> fileItemList = sfu.parseRequest(request);
			/*
			 * 把fileItemList中的資料封裝到Book物件中(包含上傳內容,所以不能一鍵封裝)
			 * 
			 * 解決:將所有的普通文字表單遍歷進map,然後對map一鍵封裝
			 * 做法:
			 * 1.把所有的普通文字表單資料封裝到map中
			 * 2.把map中資料封裝到Book物件中
			 */
			
			//遍歷:將所有普通文字欄位資料放入map
			Map<String,String> map = new HashMap<String, String>();
			for (FileItem fileItem : fileItemList) {
				if(fileItem.isFormField()){  //判斷是否為普通文字欄位資料
					map.put(fileItem.getFieldName(), fileItem.getString("UTF-8"));
				}
			}
			 //一鍵封裝map
			Book book = CommonUtils.toBean(map, Book.class); 
			
			//對book設定uuid
			book.setBid(CommonUtils.uuid());
			
			//將Map中cid封裝到Category物件中,再將category設定給book
			Category category = CommonUtils.toBean(map, Category.class);
			book.setCategory(category);
			
			/*
			 * 二、儲存上傳的檔案
			 * 1.得到儲存目錄路徑、檔名
			 * 2.使用檔名和目錄建立目標檔案
			 * 3.儲存上傳檔案到目標檔案中
			 */
			//得到圖片的儲存路徑
			String savepath = this.getServletContext().getRealPath("/book_img");
			//得到表單項集合中第一個元素的name,並新增uuid字首避免檔名衝突
			String fileName = CommonUtils.uuid()+"_"+fileItemList.get(1).getName();
			
			/*
			 * 校驗檔案的副檔名
			 */
			if(!fileName.toLowerCase().endsWith("jpg")) {
				request.setAttribute("msg", "您上傳的圖片不是JPG副檔名!");
				request.setAttribute("categoryList", categoryService.findAllCategory());
				request.getRequestDispatcher("/adminjsps/admin/book/add.jsp")
						.forward(request, response);
				return;
			}
			
			
			//2.使用路徑和檔名 建立目標檔案
			File destFile = new File(savepath,fileName);
			
			//3.儲存上傳檔案到目標檔案中
			fileItemList.get(1).write(destFile);
			
			//三、把圖片的路徑設定為Book的image
			book.setImage("book_img/" + fileName);
		
			//使用BookService完成儲存
			bookService.add(book);
			
			//轉發請求另一個servlet,呼叫其中findAll,對新增圖書進行顯示
			request.getRequestDispatcher("/admin/AdminBookServlet?method=findAllBook").forward(request, response);
		} catch (Exception e) {
		
		}
	}
}

jsp

在這裡插入圖片描述

手風琴式下拉選單使用

<script language="javascript">

	/*
	 * 手風琴式下拉選單指令碼使用規定:
	 * bar1:引數名必須與物件名相同
	 * ITCAST網路圖書商城:大標題
	 */
	
	var bar1 = new Q6MenuBar("bar1", "ywnxbx網路圖書商城");
	function load() {
		//設定配色方案(四種):0、1、2、3 (內部為陣列下標)
		bar1.colorStyle = 2;  
		
		//設定圖片所在目錄 (手風琴式下拉選單加減號圖片)
		bar1.config.imgDir = "<c:url value='/menu/img/'/>";
		
		/*
		 * 選單間是否相互排斥  
		 *	false(不排斥):擴充套件選單可以全部開啟、
		 *	true(排斥):只能開啟一項擴充套件選單,已開啟的會被自動關閉)
		 */
		bar1.config.radioButton=false;
		
		/*
			add方法:
			第一個引數:被新增的選單
			第二個引數:新增項
			第三個引數:新增項的內容(請求地址)
			第四個引數:顯示內容的框架名稱
		 */
		bar1.add("分類管理", "檢視分類", "<c:url value='/admin/AdminCategoryServlet?method=findAll'/>", "body");
		bar1.add("分類管理", "新增分類", "<c:url value='/adminjsps/admin/category/add.jsp'/>", "body");
	
		bar1.add("圖書管理", "檢視圖書", "<c:url value='/admin/AdminBookServlet?method=findAllBook'/>", "body");
		bar1.add("圖書管理", "新增圖書", "<c:url value='/admin/AdminBookServlet?method=addPre'/>", "body");
	
		bar1.add("訂單管理", "所有訂單", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
		bar1.add("訂單管理", "未付款訂單", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
		bar1.add("訂單管理", "已付款訂單", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
		bar1.add("訂單管理", "未收貨訂單", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
		bar1.add("訂單管理", "已完成訂單", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
	
		//獲取div元素
		var d = document.getElementById("menu");
		
		//將選單物件轉換成字串物件 賦給div
		d.innerHTML = bar1.toString();
	}
	</script>
	
	</head>
	
	<body onload="load()" style="margin: 0px; background: rgb(254,238,189);">
	<div id="menu"></div>
	
	</body>