本项目为连锁店餐饮系统。前台C#,服务器采用Java开发,oracle数据库;通过webservice实现前后台的交互,后台以提供服务的方式,供前端调用并进行数据库操作。
商家根据餐饮副食店实际情况提出了新的需求,希望能够通过在店内放置触摸屏的方式,使会员顾客能够以触摸屏的方式选择商品并放入自己的购物车,之后售货员只需要读取卡内的购物车信息即可刷卡消费,这样大大节约了顾客挑选商品的时间,使整个购物流程并行化程度提高。由于顾客消费的时间大多集中在早中晚,并行化会使店内的销售量显著提高。
经过一周的时间终于完成了模块的更新,下面我将分4个方面详细的描述整个流程,并附上关键部分源码,分享给大家共同学习。
1.功能模块介绍;2,触屏设置实现(前台、服务器、数据库);3,触屏显示实现;4,触屏结算实现
一 功能模块介绍:
展柜设置(包括4个子模块)
1.1 触屏设置(管理员操作)
本子模块包括4项功能:
——选择:根据门店编号mdbh从数据库消费记录表和物料表中获取外卖记录,显示在combox中;
——插入:管理员从combox中选取要显示的商品图片,获取该图片的绝对路径,以记录的方式插入到config.txt文本中,每个图片代表一条记录
——删除:删除TXT文档中对应的条目。
——保存:将当前Imagelistview中的商品图片信息保存成TXT文档,存储在项目的指定目录下。
1.2 触屏显示(顾客操作)
本模块中,自动加载“触屏设置”中保存的TXT文档,读取其中的记录并将对应的图片全屏显示;双击图片会进入商品细节页面,在本界面,顾客可以选择”加入购物车“或”返回“
(注:顾客通过本显示屏可以看到本店内所有的商品信息,并有选择的加入到自己卡的购物车中)
1.3 触屏结算(售货员操作)
本模块包括3项功能:
——读卡:售货员点击“读卡”,系统将卡内的购物车信息显示在购物车栏中;
——修改:删除菜品:售货员可以对购物车中的商品调整数量或删除
——结算:对IC卡的购物车商品结算,同时删除数据库中对应的购物车信息,执行卡金额操作,并打印小票。
1.4 图片下载(管理员操作)
通过FTP的方式,管理员可以从指定的FTP目录中选择图片,加入本门店的图片库中,供展柜设置使用。
总体界面设计:
1.5 总体流程:
前台:主要是画界面,写webservice调用方法,逻辑实现;
服务器端:若无对应的服务,则注册webservice服务(1.在sun-jaxws.xml 中通过endpoint注册;2.在对应的服务中加入注解,@webservice和@HandlerChain和@webMethod,注意名字要和xml中对应;注册完服务之后,通过具体的方法执行Sql语句操作数据库,反馈到前台,显示。
当思路清晰、宏观设计之后,具体的实现就是搬砖的过程,我们只需要一步步的根据需求完成模块即可。Let’s go!
二 触屏设置
2.1界面设计:
2.2自动加载物料表,Combox显示
1.通过调用getYLData方法从数据库中获取物料数据存入List中
private void initYL(String lb) { 物料编号.Visible = true; ylComb.Visible = true; ylAddBtn.Visible = true; getYLData(lb); //设置物料表ylList if (ylList != null) { foreach (DataBean bean in ylList) { //物料列表 this.ylComb.Items.Add(bean.GetValue("WL_WLBH") + "|" + bean.GetValue("WL_WLMC") + "|" + bean.GetValue("WL_3lMC") + "|" + bean.GetValue("WL_JLDW") + "|" + bean.GetValue("SL")); } } }
2.通过webservice调取服务MaterialService中的getDataFromXSJL方法获取物料数据,存入ArrayList中。
private void getYLData(string ALMC) { ServiceDelegate serviceDelegate = ServiceDelegateFactory.Instance(); //ArrayList materialList = new ArrayList(); try { ylList = (ArrayList)serviceDelegate.CommonInvoke("MaterialService", "getDataFromXSJL", ALMC); } catch (Exception ex) { if (ex is WebServiceException) { if ((ex as WebServiceException).ExceptionType == EnumDefine.WebServiceException.ConnectException) { PopMessage.Error("EXC0009E", ConfigUtil.GetItemValue("ErrorMessageTitle")); } else { PopMessage.Error("EXC0006E", ConfigUtil.GetItemValue("ErrorMessageTitle")); } } else { throw ex; } return; } if (serviceDelegate.MessageList != null) { ServiceMessageBean popMessage = (ServiceMessageBean)serviceDelegate.MessageList[0]; if (popMessage.Parameters != null) { PopMessage.Error(popMessage.MessageCode, ConfigUtil.GetItemValue("ErrorMessageTitle"), popMessage.Parameters); } else { PopMessage.Error(popMessage.MessageCode, ConfigUtil.GetItemValue("ErrorMessageTitle")); } serviceDelegate.MessageList.Clear(); } }
3.服务器代码
MaterialService中的getDataFromXSJL方法:
public List getDataFromXSJL(String mdbh) throws Exception { List<?> resultList = dao.selectDataFromKCDL(mdbh); if (resultList == null || resultList.isEmpty()) { messages.add(new ServiceMessage(CommonConst.RESULTNULL)); return null; } else { return resultList; } } @Override public List<?> selectDataFromKCDL(String mdbh) throws SysException { StringBuffer sb = new StringBuffer(); List<Object> argsList = new ArrayList<Object>(); sb.append("select "); sb.append(" WL_WLBH,WL_WLMC,WL_3LMC, WL_JLDW,sum(DXFJL_SL) AS SL"); sb.append(" from "); sb.append(" T_JCZL_WL left join T_KWGL_DXFJL on "); sb.append(" WL_WLBH=DXFJL_CPBH AND substr(DXFJL_XFLSH,0,?)=?"); sb.append(" where "); sb.append(" WL_2lMC=? "); sb.append(" group by WL_WLBH,WL_WLMC,WL_3LMC, WL_JLDW "); // sb.append(" WL_WLMC "); argsList.add(mdbh.length()); argsList.add(mdbh); argsList.add("外卖"); AppLog.logSystemInfo(WlDao.class, "selectDataBy1LMC", sb.toString() .replace('\'', '"')); List<?> list = (List<?>) sqlRunner .executeQuery(sb.toString(),argsList); return WsCommon.listConvert(list, sb.toString()); }
2.3 插入和删除
操作和2.2类似.
2.4 保存
分析:声明configPic.txt配置文件和图片的存放位置,创建文件流和字节流,将2.2中插入的菜品所对应的图片的绝对路径存入txt文件中,然后将txt文件中保存的信息在触屏显示中读取。
private void saveBtn_Click(object sender, EventArgs e) { string filePath = Application.StartupPath + "/Pic/used/configPic.txt"; string imagePath = Application.StartupPath + "/Pic/"; if (dataGridView1.Rows.Count < 1) return; imageListView1.Items.Clear(); FileStream fs = new FileStream(filePath, FileMode.Create); StreamWriter sw = new StreamWriter(fs); foreach (DataGridViewRow row in dataGridView1.Rows) { sw.WriteLine(imagePath+row.Cells["WL_WLBH"].Value.ToString() + ".jpg"); } sw.Flush();//保护硬盘 //关闭流 sw.Close(); fs.Close(); this.toolStripStatusLabel.Text = "保存成功!"; String[] fileLines = System.IO.File.ReadAllLines(filePath); imageListView1.Items.AddRange(fileLines);//触屏显示 }
三 触屏显示
分析:1.显示txt中的图片信息2.增加双击事件,即双击图片可以查看其细节信息,并进入对应的窗体3.在商品细节窗体中可以把其“保存”进购物车,并返回。
具体实现类似触屏设置,略。
四 触屏销售
4.1 模块展示:
分析:本模块设计的内容较多,主要包括1.读卡2.结算3.打印小票
4.2 读卡
分析:关键点:1. CardOperation和CardDbOperation读取卡号和卡内数据(具体读卡操作略过,如有需要可以私信博主)2.通过方法getDataByGWC(ickh);获取卡内的购物车数据3.绑定数据源到dgv,注意将dgv中列的NAME属性和DataPropertyName属性一一对应,否则绑定失败。
private void readBtn_Click(object sender, EventArgs e) { //点餐前检查是否有IC卡放置好 DataBean dataTable1 = null; CardOperation co = new CardOperation(); CardDbOperation codb = new CardDbOperation(); string ickh = co.getICKH(); this.ickh_txb.Text = ickh; if (ickh.Length < 2) { MessageBox.Show("请放好消费卡!"); return; } dataTable1 = codb.getICCard(ickh); this.knye_txb.Text = dataTable1.GetValue("IC_CZYE").ToString(); ///读卡之后,在购物车篮显示商品 ///1.读取数据库getAllData返回map<key,value> ///2.将map显示到dgv上 getDataByGWC(ickh); this.dinnerDetailDgv.DataSource = ServiceDelegate.ListToDataTable(resultList); int i = 0; foreach (DataGridViewRow row in dinnerDetailDgv.Rows) { DataGridViewRow dgr = dinnerDetailDgv.Rows[i]; dgr.Cells["IC_ICKH"].Value = dinnerDetailDgv.Rows[i].Cells["IC_ICKH"].Value.ToString().Trim(); i++; } RefreshTotal(); }
//获取门店订单 private void getDataByGWC(string ickh) { ServiceDelegate serviceDelegate = ServiceDelegateFactory.Instance(); try { resultList = (ArrayList)serviceDelegate.CommonInvoke("CardService", "GetCaseByICKH", ickh); } catch (Exception ex) { return; } if (resultList == null) { ServiceMessageBean popMessage = (ServiceMessageBean)serviceDelegate.MessageList[0]; if (popMessage.Parameters != null) { PopMessage.Error(popMessage.MessageCode, ConfigUtil.GetItemValue("ErrorMessageTitle"), popMessage.Parameters); } else { PopMessage.Error(popMessage.MessageCode, ConfigUtil.GetItemValue("ErrorMessageTitle")); } serviceDelegate.MessageList.Clear(); } }
4.3 结算
///1.获取dgv上面的金额总数 ///2.操作卡内余额 ///3.删除该IC卡的GWC数据 ///4.打印小票,获取cgv上的数据
if (dinnerDetailDgv.Rows.Count <= 0) { MessageBox.Show("购物车中无商品!"); return; } //判断卡中金额是否足够,是则减掉金额,并作出卡和数据库的操作 CardOperation co = new CardOperation(); try { ickh = co.getICKH(); } catch (Exception ex) { MessageBox.Show("读卡错误!"); return; } DataBean dataBean = new DataBean(); dataBean = getICCard(ickh); if (dataBean == null) { MessageBox.Show("该卡不存在!"); return; } if (dataBean != null) { string dqzt = (string)dataBean.GetValue("IC_DQZT"); if (dqzt != "已开卡") { MessageBox.Show("该卡当前状态为" + dqzt + ",不可消费!"); return; } } string a = (string)dataBean.GetValue("IC_CZYE").ToString(); double knye = NumberUtil.toDouble(dataBean.GetValue("IC_CZYE").ToString()); //取得datagridview数据 ids = new string[dinnerDetailDgv.Rows.Count]; names = new string[dinnerDetailDgv.Rows.Count]; counts = new string[dinnerDetailDgv.Rows.Count]; pricePerUnits = new string[dinnerDetailDgv.Rows.Count]; prices = new string[dinnerDetailDgv.Rows.Count]; string kplx = (string)dataBean.GetValue("IC_KPLX"); xfje = 0; for (int i = 0; i < dinnerDetailDgv.Rows.Count; i++) { // 逐条遍历数据 ids[i] = (string)dinnerDetailDgv.Rows[i].Cells["GWC_CPBH"].Value; names[i] = (string)dinnerDetailDgv.Rows[i].Cells["CPML_CPMC"].Value; counts[i] = (string)dinnerDetailDgv.Rows[i].Cells["GWC_CPSL"].Value; pricePerUnits[i] = (string)dinnerDetailDgv.Rows[i].Cells["CPML_HYJ"].Value; if (kplx.Equals("会员卡")) { prices[i] = (string)dinnerDetailDgv.Rows[i].Cells["CPML_HYJ"].Value; xfje += NumberUtil.toDouble((string)dinnerDetailDgv.Rows[i].Cells["CPML_HYJ"].Value) * NumberUtil.toDouble(counts[i]); } } //判断余额是否足够 if (xfje > knye) { MessageBox.Show("消费金额:" + xfje + " 可用余额:" + knye + " 。IC卡余额不足,请充值!"); return; } //更改IC卡内的余额 dataBean.setValue("IC_CZYE", knye - xfje); if (dataBean.GetValue("IC_BZ") == null) dataBean.setValue("IC_BZ", "备注信息"); DataBean dataBean_MXFJL = new DataBean(); dataBean_MXFJL.ColumnName = new string[17] { "MXFJL_LSH", "MXFJL_MDLSH", "MXFJL_MDBH", "MXFJL_ICKH", "MXFJL_XSYBH", "MXFJL_XFSJ", "MXFJL_XFJE", "MXFJL_XFBTJE", "MXFJL_XFCZJE", "MXFJL_BTYE", "MXFJL_CZYE", "MXFJL_JEDW", "MXFJL_JSZT", "MXFJL_JSDH", "MXFJL_BZ", "MXFJL_JSFS", "MXFJL_XFLX" }; string czsj = CommonUtil.formatDateToStrDB(DateTime.Now); dataBean_MXFJL.ColumnValue = new string[17] { userInfo.ShopId + czsj, "1001", userInfo.ShopId, ickh, userInfo.UserId, czsj, xfje.ToString(), "0", "0", "0", (knye - xfje).ToString(), "元", "未结算", "0", "0", "1", "0" }; ArrayList paramList = new ArrayList(); int n = dinnerDetailDgv.Rows.Count; for (int i = 0; i < n; i++) { DataBean dataBean_detail = new DataBean(); dataBean_detail.ColumnName = new string[12] { "DXFJL_LSH", "DXFJL_XFLSH", "DXFJL_CPBH", "DXFJL_DJ", "DXFJL_JJDW", "DXFJL_SL", "DXFJL_JLDW", "DXFJL_CPMC", "DXFJL_JE", "DXFJL_CQ", "DXFJL_ZCDJ", "DXFJL_BZ" }; dataBean_detail.ColumnValue = new string[12] { i.ToString(), userInfo.ShopId + czsj, ids[i].ToString(), prices[i].ToString(), "元", counts[i].ToString(), "JLDW", names[i].ToString(), "0", "0", "0", "b" }; paramList.Add(dataBean_detail); } // 卡操作 // je = co.CardSettlement(xfje); //**更改远程数据库***** //更新IC卡内余额,插入消费记录以及消费明细 ServiceDelegate serviceDelegate = ServiceDelegateFactory.Instance(); try { // serviceDelegate.CommonInvoke("CardService", "cardConsume", new Object[3] { dataBean, dataBean_MXFJL, paramList }); // MessageBox.Show("员工发卡成功!"); } catch (Exception ex) { if (ex is WebServiceException) { if ((ex as WebServiceException).ExceptionType == EnumDefine.WebServiceException.ConnectException) { // 警告提示:网络出现问题,Web服务超时或无法连接 PopMessage.Error("COM0002W", ConfigUtil.GetItemValue("MessageTitle")); } else { // 警告提示:连接Web服务时发生系统异常 PopMessage.Error("COM0003W", ConfigUtil.GetItemValue("MessageTitle")); } } if (ex is WebServiceException) { if ((ex as WebServiceException).ExceptionType == EnumDefine.WebServiceException.ConnectException) { PopMessage.Error("EXC0009E", ConfigUtil.GetItemValue("ErrorMessageTitle")); } else { PopMessage.Error("EXC0010E", ConfigUtil.GetItemValue("ErrorMessageTitle")); } } else { throw ex; } return; } if (serviceDelegate.MessageList != null) { ServiceMessageBean popMessage = (ServiceMessageBean)serviceDelegate.MessageList[0]; if (popMessage.Parameters != null) { PopMessage.Error(popMessage.MessageCode, ConfigUtil.GetItemValue("ErrorMessageTitle"), popMessage.Parameters); } else { PopMessage.Error(popMessage.MessageCode, ConfigUtil.GetItemValue("ErrorMessageTitle")); } serviceDelegate.MessageList.Clear(); } double knye_new = knye - xfje; MessageBox.Show("欢迎光临,您此次消费共计" + xfje + "元,卡内余额为" + knye_new + "元,已结算成功!");
服务器端cardService的cardConsume方法:
// 业务 : 消费 public void cardConsume(DataBean cardData, DataBean mxfjlData, ArrayList<DataBean> xfjlDetailList) { ICardDaoIF icDao = new ICardDao(); MxfjlDaoIF dao = new MxfjlDao(); DxfjlDaoIF xjjyDao = new DxfjlDao(); // KeHuDaoIF kehuDao = new KeHuDao(); int ic = 0; int czjl = 0; int xjjy = 0; int kehu = 0; try { if (cardData != null) { ic = icDao.updateICardData(cardData); if (ic != 1) { messages.add(new ServiceMessage(CommonConst.UPDATEFAIL)); return; } } if (mxfjlData != null) { czjl = dao.insertDatas(mxfjlData); if (czjl != 1) { messages.add(new ServiceMessage(CommonConst.INSERTFAIL)); return; } } if (xfjlDetailList != null && !xfjlDetailList.isEmpty()) { for (int i = 0; i < xfjlDetailList.size(); i++) { xjjy = xjjyDao.insertDatas(xfjlDetailList.get(i)); if (xjjy != 1) { messages.add(new ServiceMessage(CommonConst.INSERTFAIL)); return; } } } } catch (Exception e) { messages.add(new ServiceMessage(CommonConst.SYSTEM_ANOMALY)); return; } } @Override public int insertICardData(DataBean param) throws SysException { // 执行插入的Sql StringBuffer sb = new StringBuffer(); // 放置参数的List List<Object> argsList = new ArrayList<Object>(); // 根据业务组合生成Sql语句 sb.append("INSERT INTO T_KWGL_IC" + "(IC_ICKH,IC_WLBH,IC_MM,IC_CKRBH,IC_KPLX,IC_FKMDBH,IC_FKRBH," + "IC_FKSJ,IC_BTYE,IC_CZYE,IC_JEDW,IC_DQZT,IC_BZ,IC_ZK)VALUES"); sb.append(" (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); argsList.add(param.getValue("IC_ICKH")); argsList.add(param.getValue("IC_WLBH")); argsList.add(param.getValue("IC_MM")); argsList.add(param.getValue("IC_CKRBH")); argsList.add(param.getValue("IC_KPLX")); argsList.add(param.getValue("IC_FKMDBH")); argsList.add(param.getValue("IC_FKRBH")); argsList.add(param.getValue("IC_FKSJ")); argsList.add(param.getValue("IC_BTYE")); argsList.add(param.getValue("IC_CZYE")); argsList.add(param.getValue("IC_JEDW")); argsList.add(param.getValue("IC_DQZT")); argsList.add(param.getValue("IC_BZ")); argsList.add(param.getValue("IC_ZK")); AppLog.logSystemInfo(ICardDao.class, "insertICardData", sb.toString() .replace('\'', '"')); return sqlRunner.executeUpdate(sb.toString(), argsList); }
4.4 打印小票
分析:在结算之后,自动执行打印小票程序。主要是格式的编排,创建LPTControls对象,传入待打印字符串,操作小票机
//********打印小票*********** double spsl = 0; LPTControls lpt = new LPTControls(); //string mycommanglines = System.IO.File.ReadAllText("./print.txt");//print.txt里写了条码机的命令 String printLines1 = ""; String printLines2 = ""; String printLines3 = ""; printLines1 = "\n 门店\n"; printLines1 += " " + userInfo.ShopId + " " + userInfo.ShopName + "\n"; printLines1 += "-------------------------------------\n"; printLines1 += " 收 款 员: " + userInfo.UserId + "\n"; printLines1 += " 卡 号: " + ickh.ToString() + "\n"; printLines1 += "-------------------------------------\n"; printLines1 += " 单号 :" + czsj + "\n"; printLines1 += " 品名 " + " 数量 " + "单价 " + "金额" + "\n"; printLines1 += "-------------------------------------\n"; for (int i = 0; i < dinnerDetailDgv.Rows.Count; i++) { double dj; try { dj = NumberUtil.toDouble((string)dinnerDetailDgv.Rows[i].Cells["CPML_HYJ"].Value); } catch (Exception ex) { throw ex; } if (((string)dinnerDetailDgv.Rows[i].Cells["CPML_CPMC"].Value).Length > 6) { spsl += NumberUtil.toDouble((string)dinnerDetailDgv.Rows[i].Cells["GWC_CPSL"].Value); int Slength = ((string)dinnerDetailDgv.Rows[i].Cells["CPML_CPMC"].Value).Length; for (int j = 0; j < Slength; j = j + 4) { int addLength = j + 4; if (addLength < Slength) { printLines2 += " " + ((string)dinnerDetailDgv.Rows[i].Cells["CPML_CPMC"].Value).Substring(j, addLength) + "\n"; } else { printLines2 += " " + (((string)dinnerDetailDgv.Rows[i].Cells["CPML_CPMC"].Value).Substring(j, Slength - j)).PadRight(6, '\u3000'); } } printLines2 += " " + ((string)(dinnerDetailDgv.Rows[i].Cells["GWC_CPSL"].Value)).PadRight(4, '\u3000') + (dj.ToString()).PadRight(8, ' ') + dinnerDetailDgv.Rows[i].Cells["CPML_HYJ"].Value + "\n"; } else { spsl += NumberUtil.toDouble((string)dinnerDetailDgv.Rows[i].Cells["GWC_CPSL"].Value); printLines2 += " " + ((string)dinnerDetailDgv.Rows[i].Cells["CPML_CPMC"].Value).PadRight(6, '\u3000') + " " + ((string)(dinnerDetailDgv.Rows[i].Cells["GWC_CPSL"].Value)).PadRight(4, '\u3000') + (dj.ToString()).PadRight(8, ' ') + (string)(dinnerDetailDgv.Rows[i].Cells["price"].Value) + "\n"; } // 其中 \u3000代表全角空格 //printLines2 += " "+dinnerDetailDgv.Rows[i].Cells[1].Value + " " + dinnerDetailDgv.Rows[i].Cells[2].Value + " " + dj.ToString() + " " + dinnerDetailDgv.Rows[i].Cells[3].Value + "\n"; } printLines3 += "-------------------------------------\n"; printLines3 += " 数量合计:" + " " + spsl.ToString() + " 件" + "\n"; printLines3 += " 金额合计:" + " " + xfje.ToString() + " 元" + "\n"; printLines3 += " 卡内余额:" + " " + knye_new.ToString() + " 元" + "\n"; printLines3 += "-------------------------------------\n"; printLines3 += " 期待您再次光临 \n"; printLines3 += "\n\n\n\n\n\n\n\n"; RawPrinterHelper.printLines1 = printLines1; RawPrinterHelper.printLines2 = printLines2; RawPrinterHelper.printLines3 = printLines3; RawPrinterHelper.count = 0; MessageBox.Show(printLines1 + printLines2 + printLines3); lpt.Open(); lpt.Write(printLines1 + printLines2 + printLines3); lpt.Close(); xfje = 0; }
结算成功:
本博文为博主原创
转载请注明转自:blog.csdn.net/mengzhengyu1025