为了减少编程工作量,T_KSXX、T_BRXX、T_KSYS、T_HZXX的信息手工录入数据库,每个表至少录入6条记录,所有类型为CHAR(6)的字段数据从“000001”开始,连续编码且中间不得空缺。为病人开发的桌面应用程序要实现的主要功能具体如下:
功能(2)的界面如下所示,在光标停在“科室名称”输入栏时,可在输入栏下方弹出下拉列表框,显示所有科室的“科室编号”、“科室名称”和“拼音字首”,此时可通过鼠标点击或输入科室名称的拼音字首两种输入方式获得“科室编号”,用于插入T_GHXX表。注意,采用拼音字首输入时可同时完成下拉列表框的科室过滤,使得下拉列表框中符合条件的科室越来越少,例如,初始为“内一科”和“内二课”。其它输入栏,如“医生姓名”、“号种类别”、“号种名称”也可同时支持两种方式混合输入。
每种号种挂号限定当日人次,挂号人数超过规定数量不得挂号。一个数据一致的程序要保证:挂号总人数等于当日各号种的挂号人次之和,病人的账务应保证开支平衡。已退号码不得用于重新挂号,每个号重的GHRC数据应连续不间断,GHRC从1开始。若病人有预存金额则直接扣除挂号费,此时“交款金额”和“找零金额”处于灰色不可操作状态。
为医生开发的桌面应用程序要实现的主要功能具体如下:
挂号编号
号种类别
000001
章紫衣
2018-12-3011:52:26
专家号
000003
范冰冰
2018-12-3011:53:26
普通号
000004
刘德华
2018-12-3011:54:28
挂号人次
收入合计
感染科
李时珍
24
48
10
内一科
000002
扁鹊
23
保健科
华佗
20
病人应用程序和医生应用程序可采用主窗口加菜单的方式实现。例如,医生应用程序有三个菜单项,分别为“病人列表”、“收入列表”和“退出系统”等。
挂号时锁定票号可能导致死锁,为了防止死锁或系统响应变慢,建议大家不要锁死数据库表或者字段。程序编写完成后,同时启动两个挂号程序进行单步调试,以便测试两个病人是否会抢到同一个号、或者有号码不连续或丢号的现象。
思考题:当病人晚上11:59:59秒取得某号种的挂号价格10元,当他确定保存时价格在第2天00:00:00已被调整为20元,在编程时如何保证挂号费用与当天价格相符?
2.需求分析
一、系统设计
1.概要设计
介绍设计思路、原理。将一个复杂系统按功能进行模块划分、建立模块的层次结构及调用关系、确定模块间的接口及人机界面等。
要有总体结构、总体流程(图)。
图2.1总体流程图
图2.2医生查询流程图
图2.4病人挂号界面流程图
1.详细设计
图形用户界面用JavaFx来实现,使用intellijIDEA编辑器编辑代码,图形界面用JavaFXSceneBuilder1.1来设计,设计完成后绑定对应控件的id,然后在controller里面定义对应的控件,并使用即可,具体实现如下:
首先有一个全局的Stage,是总的舞台,其他的页面花在这个舞台上面。
1publicclassFXMLTestextendsApplication{2privateStagestage;3privatefinaldoubleMINIMUM_WINDOW_WIDTH=700.0;4privatefinaldoubleMINIMUM_WINDOW_HEIGHT=370.0;5privatestaticFXMLLoadermyloader=null;6publicStringBR_name;78@Override9publicvoidstart(StageprimaryStage)throwsException{1011}12publicstaticvoidmain(String[]args){13launch(args);14}1516}
继承自Application,重写start方法,在main方法里面调用就好。
Start方法里面启动舞台stage:
1@Override2publicvoidstart(StageprimaryStage)throwsException{3stage=primaryStage;4stage.setTitle("登陆界面");5stage.setMinWidth(MINIMUM_WINDOW_WIDTH);6stage.setMinHeight(MINIMUM_WINDOW_HEIGHT);7gotologin();8stage.setResizable(false);9stage.show();10}
publicvoidgotologin(){try{LoginControllerlogin=(LoginController)replaceSceneContent("登陆界面","FXML_LOGIN.fxml","Login.css",MINIMUM_WINDOW_WIDTH,MINIMUM_WINDOW_HEIGHT,0);login.setApp(this);}catch(Exceptionex){Logger.getLogger(FXMLTest.class.getName()).log(Level.SEVERE,null,ex);}}
为了提高开发效率,获取界面controller并加载到stage的部分功能统一用一个函数replaceSceneContent(),再把此application传入到controller,为了在界面控制类controller里面调用或者返回到总的application类,加载其他的页面。
1privateInitializablereplaceSceneContent(Stringtitle,Stringfxml,Stringcss,doublewidth,doubleheight,inttype)throwsException{2FXMLLoaderloader=newFXMLLoader();3InputStreamin=FXMLTest.class.getResourceAsStream(fxml);4loader.setBuilderFactory(newJavaFXBuilderFactory());5loader.setLocation(FXMLTest.class.getResource(fxml));6SplitPaneSpage;7AnchorPaneApage;8if(type==0){9Apage=(AnchorPane)loader.load(in);10Scenescene=newScene(Apage,width,height);11scene.getStylesheets().add(FXMLTest.class.getResource(css).toExternalForm());12stage.setTitle(title);13stage.setScene(scene);14stage.sizeToScene();15}elseif(type==1){16Spage=(SplitPane)loader.load(in);17Scenescene=newScene(Spage,width,height);18scene.getStylesheets().add(FXMLTest.class.getResource(css).toExternalForm());19stage.setTitle(title);20stage.setScene(scene);21stage.sizeToScene();22}else{2324}25myloader=loader;26return(Initializable)loader.getController();27}
使用界面设计器实现的界面如下:
启动之后的界面如下:
显然,使用了css样式文件之后界面变得更精美。
@FXMLpublicvoidLOGIN_M(ActionEventevent){inttype=choiceBox.getSelectionModel().getSelectedIndex();application.userlogin(account.getText(),password.getText(),type);}
调用父类的userlogin方法:
为了代码的简洁性,把数据库查询方法一律统一管理。
从上述代码可知,当用户信息匹配成功以后,界面需要重新加载,gotoYSmain()是加载医生界面,gotoMain()是加载病人挂号界面。
1.病人挂号模块
挂号模块提供给病人进行挂号,业务逻辑简单,用户只需要选择科室、科室医生、号种类别以及使用余额或者交款的方式进行付费以后即可得到挂号号码。输入框有自动过滤输入内容的功能,当用户输入科室名称或名称的大写下拉框会过滤显示对应的内容,也可以点击后根据下拉框的值来选择科室。科室选择完毕后,该科室的医生姓名会实时从数据库查询后,等待用户选择或输入,同样有过滤功能。下一步选择号种类别,有专家号和普通号,是根据医生是否专家来确定号种类别,医生是专家时,可以选择专家和普通号,当医生非专家时,只能选择普通号,输入框的值会在用户选择了医生之后会实时查询数据库在显示。此时,号中名称会显示出来,等待用户点击确定按钮,进行非空检查以后,根据票价从用户的余额去扣费,如果余额足够,则扣费完成后显示挂号号码,否则要求用户输入交款金额,再进行挂号。
第一步,启动挂号界面:
1publicvoidgotomain(){2try{3MainControllermain=(MainController)replaceSceneContent("医院挂号系统","FXML_MAIN.fxml","main.css",700,450,1);4main.setApp(this);5}catch(Exceptionex){6Logger.getLogger(FXMLTest.class.getName()).log(Level.SEVERE,null,ex);7}8}
在controller里面用到的数据类型有:
1privateHashMap
在initialize()方法里面对科室信息、医生信息以及病人余额等信息初始化,并对输入框的输入内容进行监听,当有变化时,进行数据更新:
根据医生名称设置号种类别:
1privatevoidsetHZLB_combo_Box(StringKSYS){2intisZJ=DataBaseUtil.isZJ(KSYS);3if(isZJ==1){4comboBox_HZLB.getItems().addAll("专家号","普通号");5}else{6comboBox_HZLB.getItems().addAll("普通号");7}8}
设置医生信息下拉框的值:
1privatevoidsetKSYScomboxInfo(){2KSYSinfo=DataBaseUtil.getKSYSinfo(KSinfo.get(Text_KSMC).trim());3Setset1=KSYSinfo.keySet();4Iteratoriter1=set1.iterator();5while(iter1.hasNext()){6Stringkey=(String)iter1.next();7comboBox_YSMC.getItems().addAll(key);8}9}
当点击确定按钮时,进行非空检查以及查询扣费等:
用到的一些小的工具方法:
病人登陆成功之后,获取名称以及预存金额,利用HASHMAP来保存得到的数据,方便处理:
获取科室信息:
获取科室医生信息:
根据号种获取票价:
开始挂号,根据数据库当前最新的挂号号码计算新的号码并返回:
根据号种名称获取号种编号:
判断医生是否专家:
最后挂号成功,并显示挂号号码,界面如下:
3.医生界面
1publicvoidgotoYSmain(){2try{3YiShengControllermain=(YiShengController)replaceSceneContent("医生治疗系统","FXML_YISHENG.fxml","main.css",700,450,0);4main.setApp(this);5}catch(Exceptionex){6Logger.getLogger(FXMLTest.class.getName()).log(Level.SEVERE,null,ex);7}8}
privateStringYSBH_in_Controller;privateObservableList
1@Override2publicvoidinitialize(URLlocation,ResourceBundleresources){3DatePicer_start.setValue(LocalDate.now());4DatePicer_end.setValue(DatePicer_start.getValue().plusDays(1));56for(inti=0;i<24;i++){7ComboBox_S_1.getItems().addAll(i+"点");8ComboBox_E_1.getItems().addAll(i+"点");9}10ComboBox_S_1.getSelectionModel().select(0);11ComboBox_E_1.getSelectionModel().select(0);1213for(inti=0;i<60;i++){14ComboBox_S_2.getItems().addAll(i+"分");15ComboBox_E_2.getItems().addAll(i+"分");16}17ComboBox_S_2.getSelectionModel().select(0);18ComboBox_E_2.getSelectionModel().select(0);192021}
当医生界面加载完成的同时,病人列表会自动加载到表格里面,当点击刷新按钮时,会更新病人列表:
1@FXML2publicvoidsetTableData(){3GHBH_1.setCellValueFactory(newPropertyValueFactory("GHBH"));4BRMC_1.setCellValueFactory(newPropertyValueFactory("BRMC"));5GhTime_1.setCellValueFactory(newPropertyValueFactory("GHTime"));6HzType_1.setCellValueFactory(newPropertyValueFactory("HZLB"));7data=DataBaseUtil.getBrLieBiao(YSBH_in_Controller);8BrTableView.setItems(data);9}
获取病人列表使用的是ObservableList,从数据库获取该医生对应数据的代码如下:
ObservableList需要适配器来实现数据的显示,需要一个类:
1packagesample;2publicclassBrColumn{3privateStringGHBH;4privateStringBRMC;5privateStringGHTime;6privateStringHZLB;78publicBrColumn(StringGHBH,StringBRMC,StringGHTime,StringHZLB){9this.GHBH=GHBH;10this.BRMC=BRMC;11this.GHTime=GHTime;12this.HZLB=HZLB;13}1415//get16publicStringgetGHBH(){17returnGHBH;18}1920publicStringgetBRMC(){21returnBRMC;22}2324publicStringgetGHTime(){25returnGHTime;26}2728publicStringgetHZLB(){29returnHZLB;30}3132//set33publicvoidsetGHBH(StringGHBH){34this.GHBH=GHBH;35}3637publicvoidsetBRMC(StringBRMC){38this.GHBH=BRMC;39}4041publicvoidsetGHTime(StringGHTime){42this.GHBH=GHTime;43}4445publicvoidsetHZLB(StringHZLB){46this.GHBH=HZLB;47}48}
数据库的查询有点复杂,因为需要嵌套查询,因此效率并不是想象中那么快,在学习了数据库课程以后,继续优化。适配器类如下:
一、软件测试
对照题目要求,构造测试例,给出程序界面截图,举证题目要求的功能(以及自行补充的功能)已实现。