在前兩篇文章
【帶并發(fā)限制的異步調(diào)度器Ⅰ】原始Demo
【帶并發(fā)限制的異步調(diào)度器Ⅱ】點(diǎn)餐排隊(duì)系統(tǒng)Demo
的基礎(chǔ)上,這次加入了圖形化界面,這樣就升級(jí)成一個(gè)完整的項(xiàng)目。我把這三篇文章總結(jié)為以下三種遞進(jìn)的階段:
①單通道的任務(wù)調(diào)度器
②多通道的任務(wù)調(diào)度器,無(wú)頁(yè)面的應(yīng)用場(chǎng)景演示Demo,并增加了通道數(shù)及任務(wù)運(yùn)行數(shù)的初始化配置
③多通道的任務(wù)調(diào)度器,有頁(yè)面的應(yīng)用場(chǎng)景操作,可配置初始化信息并持久化保存,可查看某一任務(wù)的運(yùn)行情況,可實(shí)時(shí)添加及移除任務(wù)
第三階段的技術(shù)總結(jié)如下:
- React組件圖形化界面,形成最原始的可視化頁(yè)面
- Egg.js企業(yè)級(jí)Node.js框架,提供接口請(qǐng)求以及安全防護(hù)
- Mongoose數(shù)據(jù)庫(kù)存儲(chǔ),持久化配置信息
- React-redux提供組件間交互的狀態(tài)管理,store機(jī)制給所有組件(隔層組件以及不同頁(yè)面組件)提供全局?jǐn)?shù)據(jù)
- Ant Design模態(tài)框提供便于交互的組件服務(wù)
第三階段的功能點(diǎn)總結(jié)如下:
- 在“初始化”界面添加配置桌子信息,可移除添加的信息,可編輯添加的信息(添加相同人數(shù)的桌子信息即可覆蓋原信息),可點(diǎn)擊“完成配置”持久化配置數(shù)據(jù),以供下次啟動(dòng)試用,可立即切換到“點(diǎn)餐排隊(duì)”使用配置信息



- 進(jìn)入系統(tǒng)時(shí),系統(tǒng)會(huì)自動(dòng)請(qǐng)求配置數(shù)據(jù)接口初始化操作頁(yè)面需要用到的全局?jǐn)?shù)據(jù)store(“點(diǎn)餐排隊(duì)”和“初始化”使用的是store里的同一個(gè)數(shù)據(jù)源)


-
點(diǎn)擊初始化后,會(huì)出現(xiàn)后續(xù)操作按鈕,點(diǎn)擊“查看桌子使用信息”,會(huì)出現(xiàn)桌子的使用信息情況
操作按鈕

-
點(diǎn)擊“用餐入座”,相應(yīng)的顧客就會(huì)在N人桌里占一個(gè)位子,如果有空閑桌,可以直接用餐(若按原始Demo的說(shuō)法,就是執(zhí)行任務(wù)隊(duì)列有空缺,任務(wù)直接進(jìn)入執(zhí)行任務(wù)隊(duì)列運(yùn)行,若已滿,則會(huì)進(jìn)入待執(zhí)行任務(wù)隊(duì)列),若沒(méi)有,則會(huì)拿到一個(gè)排隊(duì)編號(hào)等待入座
添加用餐信息

- 當(dāng)拿到編號(hào)時(shí),可點(diǎn)擊“查看某桌排隊(duì)情況”,輸入相應(yīng)編號(hào)來(lái)查看此編號(hào)目前所處的情況,具體情況如下:
- 無(wú)此桌點(diǎn)餐信息
- 正在用餐
- 排隊(duì)情況


- 排隊(duì)情況
- 當(dāng)某桌用餐結(jié)束時(shí),可點(diǎn)擊“結(jié)束用餐”,輸入桌子編號(hào),把執(zhí)行任務(wù)從正在用餐隊(duì)列里移除



以上就是此系統(tǒng)的全部功能,完整項(xiàng)目放在github上,如有需要請(qǐng)自取。
總結(jié)下開(kāi)發(fā)此系統(tǒng)遇到的問(wèn)題:
- 服務(wù)端無(wú)法接受 post 請(qǐng)求,并且請(qǐng)求接口報(bào)錯(cuò)
403 :message: 'invalid csrf token'
原因:Egg框架內(nèi)置中間件安全防護(hù),默認(rèn)開(kāi)啟防止 XSS 攻擊 和 CSRF 攻擊,所以前端請(qǐng)求post接口時(shí)需要在header請(qǐng)求頭里添加csrfToken,讓Egg幫忙驗(yàn)證,具體配置如下:
server端:
//config.default.js
//csrf配置
exports.security= {
csrf : {
headerName: 'x-csrf-token', // 自定義請(qǐng)求頭
}
}
client端:
'use strict'
import api from './api';
// 封裝獲取 cookie 的方法
function getCookie(name){
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
function saveSetting(deskData, successCB, failCB) {
return $.ajax({
url: api.COMPANY + '/setting',
method: 'POST',
contentType: 'application/json',
headers:{
// 前后端不分離的情況加每次打開(kāi)客戶(hù)端,egg會(huì)直接在客戶(hù)端的 Cookie 中寫(xiě)入密鑰 ,
//密鑰的 Key 就是 'scrfToken' 這個(gè)字段,所以直接獲取就好了
'x-csrf-token': getCookie("csrfToken"),
},
data: JSON.stringify({ data:deskData })
}).then(successCB, failCB);
}
- 開(kāi)發(fā)前端的操作頁(yè)面時(shí),對(duì)React-redux的store使用不當(dāng),導(dǎo)致不同頁(yè)面組件獲取store中的全局?jǐn)?shù)據(jù)出現(xiàn)問(wèn)題。
具體情況:當(dāng)把頁(yè)面組件從UI組件變?yōu)槿萜鹘M件(使用connect處理)時(shí),未能成功獲取到store數(shù)據(jù),但實(shí)際是錯(cuò)誤理解了React-redux和redux獲取全局?jǐn)?shù)據(jù)store的方式。
解決:React-redux子組件獲取store的方式是在mapStateToProps方法中處理的,而redux是通過(guò)this.context.store來(lái)獲取,以下是React-redux獲取store的項(xiàng)目代碼:
/app/view/container/DinnerPane.js
import { connect } from 'react-redux';
import dinnerPane from '../component/dinnerPane/dinnerPane';
function mapStateToProps(state) {
return {
deskData: state.deskData||{}
};
}
function mapDispatchToProps(dispatch) {
return {};
}
export default connect(mapStateToProps, mapDispatchToProps)(dinnerPane);
初始化store是在dinnerPane的上層容器Content里處理的,先請(qǐng)求配置數(shù)據(jù)get接口,然后dispatchinitDeskDataAction到store里更新數(shù)據(jù):
/app/view/container/content/Content.js
import { connect } from 'react-redux';
import Content from '../../component/Content/Content';
import { getSetting } from '../../utils/WebAPIUtils';
import { initDeskDataAction } from '../../action/initDataActionCreator'
function mapStateToProps(state) {
return {
deskData: state.deskData
};
}
function mapDispatchToProps(dispatch) {
return {
initDeskDataRequest: function (companyId, callback) {
return getSetting(companyId, function (result) {
callback(null, result);
}, function (err){
callback(err);
});
},
initDeskData: function(deskData){
dispatch(initDeskDataAction(deskData));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Content);


