一、源碼路徑
https://github.com/weiganyi/ibooking
二、界面
通過瀏覽器訪問web網站,可以看到界面如下:
三、背景
這兩年來O2O的概念越來越火熱,O2O因爲能夠把線下的資源通過線上的信息溝通渠道進行連接,影響着很多實體行業未來的發展。
這個項目就是爲餐飲店提供線上外賣訂餐服務的web網站,具有O2O網站的特點。網站有顧客和管理員兩類角色,分別對於與餐飲店的顧客和餐飲店自身。用戶在網站上註冊後會留下用戶的地址和聯繫電話,這些信息會作爲送外賣時的用戶信息,然後用戶能夠在網站上看到餐飲店的所有菜品圖片,選中後到購物車下單,就會生成一個訂單,餐飲店後臺可以看到這些訂單,然後完成後續的餐品製作和送達服務。
系統後臺採用的是成熟的SSH框架來搭建服務,前端仍然是用jsp來拼接頁面,所以頁面還是在後臺生成的。由於本人是後臺開發,所以爲了美化界面採用bootstrap的css框架,在js框架方面只是使用了jquery,並沒有使用其他js框架。數據庫方面使用的mysql,對於用戶訪問量大的幾個表採用了redis做爲緩存,提高響應速度。
四、功能實現
1、用戶功能
1)菜品預定:菜品展示(主頁)、購物車、訂單列表、訂單詳情
2)註冊登錄:註冊、登錄、用戶信息
2、管理員功能
1)用戶管理
2)訂單管理
3)圖片管理
4)菜品管理
5)菜品類型管理
6)配置管理
五、總體設計思路
1、數據庫設計
1)其中ib_menu_type表存儲菜品類型信息,ib_menu表存儲菜品信息,ib_option表存儲配置信息,ib_user表存儲用戶信息,ib_shopping表存儲用戶購物車信息,ib_order表存儲訂單信息,ib_order_detail表存儲訂單詳情信息,這7個表爲關係型表,使用mysql數據庫存儲。它們的具體字段如下:
create table ib_menu_type(
menu_type_id int(4) not null primary key auto_increment,
menu_type_name char(255) not null);
create table ib_menu(
menu_id int(4) not null primary key auto_increment,
menu_name char(255) not null,
menu_price int(16) not null,
menu_pic_addr char(255) not null,
menu_type_id int(4) not null);
create table ib_option(
option_id int(4) not null primary key auto_increment,
option_name char(255) not null,
option_value char(255) not null);
create table ib_user(
user_id int(16) not null primary key auto_increment,
user_name char(255) not null,
user_passwd char(255) not null,
user_auth enum('admin', 'customer') not null,
user_tel char(255) not null,
user_addr char(255) not null);
create table ib_shopping(
shopping_id int(16) not null primary key auto_increment,
shopping_user_name char(255) not null,
shopping_menu_name char(255) not null,
shopping_menu_price int(16) not null,
shopping_amount int(16) not null,
shopping_remark char(255) not null);
create table ib_order(
order_id int(16) not null primary key auto_increment,
order_user_name char(255) not null,
order_time datetime not null,
order_admin_name char(255) not null,
order_accept int(1) not null);
create table ib_order_detail(
detail_id int(16) not null primary key auto_increment,
detail_order_id int(16) not null,
detail_menu_name char(255) not null,
detail_menu_price int(16) not null,
detail_amount int(16) not null,
detail_remark char(255) not null);
2)用戶訪問量大的menu、menutype和option三個表使用redis緩存,因爲這幾個表不會頻繁更新,不會造成緩存頻繁失效,同時對於不同的用戶,都會訪問這幾個表裏相同的字段,所以它們使用緩存是比較有意義的。
3)由於使用了緩存,redis和mysql之間的數據同步設計是要點。最開始,我採用的讀流程爲在redis啓動時先從mysql讀入表的所有記錄,後續直接從redis查詢記錄,寫流程爲先寫入redis,並將其放入一個隊列,隊列消費者讀取隊列後再寫入mysql,當mysql寫失敗時,要把redis寫入的內容回滾。但經過分析這種設計是有問題的,redis與mysql數據同步肯定會涉及一個主次問題,之前的設計相當於redis爲主,mysql爲次,這樣當mysql寫失敗時再回滾redis,這時redis可能已經有新數據寫入了,再回滾會造成用戶後寫入的數據被刷掉了。正確的寫流程應該以mysql爲主,先寫入mysql然後設置redis對應的鍵失效,這樣成功寫入mysql後,即便設置redis鍵失效操作失敗,由於緩存可以設置有效期,也只是會在緩存有效期內暫時影響讀到的數據,而數據由於已經寫入了mysql是能夠正確的保存的。
4)在寫操作時redis生成自增id,然後插入mysql,必須保證redis生成的自增id大於mysql表的auto_increment當前值才能成功。所以redis啓動時需要先讀取mysql對應表的auto_increment值並保存,以後每次新增記錄時,把auto_increment值加一併作爲id值,這樣就能夠保證總是大於mysql表的auto_increment當前值。
5)在事務可靠性方面,當需要同時修改兩個表的記錄時,redis和mysql需要各自使用事務。redis自身有事務機制可以使用,而mysql可以使用SSH框架的aop聲明式事務機制。
6)redis的數據格式爲了對應mysql表,需要有一條記錄表示mysql表的索引,如set/get "ib_menu:test:id" "6",這裏test是菜品名稱,也是ib_menu表的索引是菜品名,這樣就可以通過這條記錄取得test菜品的id值,然後對應這個id值有系列記錄表示mysql表的各個字段,如set/get "ib_menu:6:menu_price" "13",這裏就表示id爲6的記錄的menu_price字段值爲13。
2、Web前端設計
1)在html頁面的構造上,採用jsp腳本來完成。根據後臺servlet邏輯處理完後生成的java bean對象,在jsp文件內,通過java腳本或者jstl等jsp技術,獲取java bean對象拼裝成所需要的html頁面。
2)採用bootstrap來美化界面,採用bootstrap封裝的一些組件。
3)界面按鈕觸發ajax請求,頁面刷新方式有兩種,一種響應只更新頁面具體元素值,另一種是響應更新除了頁頭外的其餘部分。
3、Java後臺服務設計
1)後臺整體模塊採用MVC經典分成架構,分爲如下5層:
表現層:jsp頁面。
MVC Action層:每個頁面對應一個Action。
業務邏輯層:負責後臺業務邏輯實現。
DAO層:負責數據庫和緩存的讀寫邏輯實現。
數據對象層:對數據庫或緩存做對象映射。
2)對struts包含組件和特性的使用情況:
action:實現主要控制器邏輯。
國際化:使用全局國際化資源文件。
標籤:主要使用s:text, s:property, s:fielderror和控制標籤,structs的s:form比較弱,所以使用原生的並用bootstrap來增強。
類型轉換:請求參數解析和響應參數構造由類型轉換來實現,可能需要自定義轉換器。
輸入校驗:文本輸入需要增加校驗,使用全局校驗文件。
文件上傳:使用封裝的文件上傳框架,包括攔截器文件過濾功能。
攔截器:實現一個自定義的管理員訪問權限控制攔截器。
3)對hibernate數據庫的封裝的使用情況:
單表映射:ib_option使用單表映射。
關聯映射:ib_menu與ib_menu_type使用無連接表的單向N-1關聯。
hql查詢:使用hibernate特有的hql查詢語法,便於與HibernateDaoSupport框架集成。
開啓二級緩存:由於使用了redis做緩存,所以不使用hibernate內置的二級緩存功能。
4)對spring特性的使用情況:
ioc依賴注入:如果採用spring來注入struts action會造成xml冗餘,所以不使用這種方式,
使用spring啓動hibernate的sessionFactory,並用ioc注入的dao對象方式來進行數據庫表訪問。
aop切面注入:對多表操作使用xml方式聲明式事務機制。
5)線程安全方面的考慮,因爲HibernateTemplate是線程安全的,所以mysql訪問不需要加鎖,而redis訪問需要加鎖。
6)在服務部署上,使用nginx做反向代理,把請求轉發到後端的tomcat服務器上進行處理。這也是常用的部署方式,因爲目前系統文件比較少,所以沒有把圖片等靜態資源放到nginx下這種動靜分離的做法。
六、文件目錄介紹
bin:系統腳本,如重啓腳本
res\pic:圖片資源文件
res\tmp:刪除圖片時的臨時存放目錄
WEB-INF\jsp:jsp文件
WEB-INF\jsp\manager:後臺管理模塊的jsp文件
WEB-INF\lib:依賴的jar包
WEB-INF\src\ibooking\action:action的實現
WEB-INF\src\ibooking\action\authority:攔截器實現
WEB-INF\src\ibooking\action\base:action的父類
WEB-INF\src\ibooking\action\manager:後臺管理模塊的action實現
WEB-INF\src\ibooking\dao:dao層的父類
WEB-INF\src\ibooking\dao\impl:dao的實現
WEB-INF\src\ibooking\po:數據對象映射類
WEB-INF\src\ibooking\service:業務邏輯層父類
WEB-INF\src\ibooking\service\impl:業務邏輯層實現
WEB-INF\src\ibooking\util:輔助類實現
WEB-INF\src\ibooking\vo:視圖層對象
WEB-INF\src\ibooking\vo\manager:後臺管理模塊的視圖層對象
七、部署方法
1、源碼下載後,用eclipse編譯服務端源碼。
2、在服務器上安裝部署nginx和tomcat,配置nginx把所有請求轉發到tmcat,同時安裝部署好mysql和redis。
3、在tomcat/webapps下建立項目目錄ibooking,然後把編譯生成的目錄和文件拷貝到ibooking下。
4、把根目錄下的數據庫備份文件ibooking_mysql_db.sql導入mysql。
5、通過瀏覽器也可以訪問系統的Web部分,使用顧客和管理員角色相關功能。
(完)