柳孔明,趙波,張一平
(中國(guó)人民解放軍95806部隊(duì),北京100076)
“全軍各級(jí)要強(qiáng)化練兵備戰(zhàn)鮮明導(dǎo)向,堅(jiān)定不移把軍事訓(xùn)練擺在戰(zhàn)略位置、作為中心工作,抓住不放,抓出成效?!毙履暌潦迹醒胲娢e行2018年開(kāi)訓(xùn)動(dòng)員大會(huì),習(xí)主席向全軍發(fā)布了訓(xùn)令,全軍各級(jí)掀起了體能訓(xùn)練的熱潮。由于體能訓(xùn)練考核科目涉及較多,且考核成績(jī)按照人員類(lèi)別、性別、年齡段有不同的考核科目,且給出了不同的成績(jī)計(jì)算標(biāo)準(zhǔn),這就給工作人員根據(jù)成績(jī)計(jì)算出各科目的得分,并最終給出綜合評(píng)價(jià)結(jié)果帶來(lái)較大的困難。如果采取人工方式通過(guò)Excel表進(jìn)行匯集統(tǒng)計(jì)計(jì)算,再依照模板填寫(xiě)到Word文檔中去,會(huì)花費(fèi)大量人力物力,并且效率低下,增加了出錯(cuò)的概率,會(huì)影響領(lǐng)導(dǎo)對(duì)考核成績(jī)的信息的及時(shí)把握、分析和判斷。
對(duì)軍人體能訓(xùn)練考核中人工計(jì)算成績(jī)中存在的現(xiàn)實(shí)問(wèn)題,通過(guò)設(shè)計(jì)和開(kāi)發(fā)專(zhuān)門(mén)的信息系統(tǒng)可以輕松解決。本文基于通過(guò)自動(dòng)匹配成績(jī)區(qū)間來(lái)計(jì)算考核分?jǐn)?shù)的基本思路,采用DevExpress界面開(kāi)發(fā)控件庫(kù),利用C#語(yǔ)言開(kāi)發(fā)了軍人體能考核成績(jī)計(jì)算系統(tǒng),并進(jìn)行了大量的測(cè)試,在實(shí)際應(yīng)用中極大提高了計(jì)算效率和正確率。本文將對(duì)系統(tǒng)軟件的設(shè)計(jì)與開(kāi)發(fā)的關(guān)鍵技術(shù)進(jìn)行簡(jiǎn)要闡述。
軍人體能考核成績(jī)計(jì)算系統(tǒng)旨在利用計(jì)算機(jī)技術(shù)實(shí)現(xiàn)軍人體能訓(xùn)練考核成績(jī)計(jì)算信息化、標(biāo)準(zhǔn)化,減輕重復(fù)繁重的工作負(fù)擔(dān),同時(shí)達(dá)到體能標(biāo)準(zhǔn)查詢(xún)、按照成績(jī)計(jì)算得分及評(píng)價(jià)、按照模板進(jìn)行結(jié)果輸出等簡(jiǎn)單易操作性。進(jìn)行系統(tǒng)設(shè)計(jì)時(shí),考慮到成績(jī)數(shù)據(jù)中有些符號(hào)如分鐘(′)、秒(″)在錄入過(guò)程中可能由于中英文字符難以區(qū)分等因素造成輸入不規(guī)范,程序中采取最大兼容方式規(guī)避了可能出現(xiàn)的問(wèn)題。系統(tǒng)主要實(shí)現(xiàn)了考核成績(jī)的導(dǎo)入、得分與評(píng)價(jià)計(jì)算、數(shù)據(jù)按照模板的導(dǎo)出等主要功能。
系統(tǒng)建立了體型標(biāo)準(zhǔn)表(T_TX)、體脂標(biāo)準(zhǔn)表(T_TZ)、引體向上標(biāo)準(zhǔn)表(T_YTXS)、仰臥起坐標(biāo)準(zhǔn)表(T_YWQZ)、曲臂懸垂標(biāo)準(zhǔn)表(T_QBXC)、俯臥撐標(biāo)準(zhǔn)表(T_FWC)、蛇形跑標(biāo)準(zhǔn)表(T_SX2)、3000 米跑標(biāo)準(zhǔn)表(T_3000)、成績(jī)統(tǒng)計(jì)表(T_SCORE)等。系統(tǒng)中涉及的數(shù)據(jù)表較多,由于篇幅限制,這里只給出體型標(biāo)準(zhǔn)表(T_TX)簡(jiǎn)約數(shù)據(jù)表結(jié)構(gòu)(主要字段信息),如表1所示。
表1 體型標(biāo)準(zhǔn)表(T_TX)簡(jiǎn)約數(shù)據(jù)表結(jié)構(gòu)
軍人體能考核成績(jī)計(jì)算系統(tǒng)是典型的信息系統(tǒng),系統(tǒng)采用C/S架構(gòu),以C#作為系統(tǒng)的程序設(shè)計(jì)語(yǔ)言,以Visual Studio 2013為系統(tǒng)集成開(kāi)發(fā)平臺(tái),系統(tǒng)數(shù)據(jù)庫(kù)采用Microsoft Office Access。Microsoft公司的Access是一個(gè)桌面關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),能夠制作窗體和報(bào)表,并且能夠與Excel進(jìn)行數(shù)據(jù)交換。Access適合開(kāi)發(fā)小型數(shù)據(jù)庫(kù)應(yīng)用系統(tǒng)。該系統(tǒng)采用Access 2000進(jìn)行開(kāi)發(fā)。
系統(tǒng)的設(shè)計(jì)采取軟件工程中“自下而上”的方法,實(shí)現(xiàn)各個(gè)業(yè)務(wù)功能。系統(tǒng)開(kāi)發(fā)主要包括后臺(tái)數(shù)據(jù)庫(kù)的建立和維護(hù)以及前端應(yīng)用程序的開(kāi)發(fā)兩個(gè)方面,對(duì)于前者要求建立起數(shù)據(jù)一致性和完整性強(qiáng)、數(shù)據(jù)安全性好的庫(kù)。而對(duì)于后者則要求應(yīng)用程序功能完備,易使用等特點(diǎn)。
(1)Excel數(shù)據(jù)的導(dǎo)入
因?yàn)槭褂肊xcel來(lái)操作數(shù)據(jù)很方便,在數(shù)據(jù)登記時(shí)采用Excel登記數(shù)據(jù)可以給用戶(hù)帶來(lái)很多方便,因此為了便于數(shù)據(jù)流轉(zhuǎn)需要,我們需要提供一個(gè)入口給用戶(hù)導(dǎo)入所需要的數(shù)據(jù)。
C#操作Excel的傳統(tǒng)方式主要有以下幾種方式:①采用OleDB讀取Excel文件,把Excel文件當(dāng)做一個(gè)數(shù)據(jù)源來(lái)進(jìn)行數(shù)據(jù)的讀取操作;②通過(guò)引用COM組件Microsoft.Office.Interop.Excel.dll讀??;③將Excel文件轉(zhuǎn)化成CSV文件,利用文件流的方式讀取。但是上述方法都較為過(guò)時(shí),并且或者操作比較繁瑣,或者對(duì)組件版本依賴(lài)性較強(qiáng),客戶(hù)機(jī)通用性差。這里采用NPOI動(dòng)態(tài)庫(kù)進(jìn)行開(kāi)發(fā),NPOI是開(kāi)源的POI項(xiàng)目.NET版,可以用來(lái)讀寫(xiě)Excel等Office文檔,在處理Excel文件方面,NPOI可以同時(shí)兼容*.xls和*.xlsx格式,具有開(kāi)源免費(fèi)、不依賴(lài)Office組件、簡(jiǎn)單方面、性能穩(wěn)定等優(yōu)點(diǎn)。程序中的關(guān)鍵代碼如下:private void ReadExcelData()
{
FileStream fs=null;
NPOI.SS.UserModel.IWorkbook workbook=null;
ISheet sheet=null;
var filePath=openFileDialog.FileName;
ExcelPath=filePath;
try
{
//讀取文件的sheet頁(yè)
using(fs=File.OpenRead(filePath))
{
if(filePath.IndexOf(".xlsx")>0)//Excel 2007版本
workbook=new XSSFWorkbook(fs);
else if(filePath.IndexOf(".xls")>0)//Excel 2003版本
workbook=new HSSFWorkbook(fs);
((DevExpress.XtraEditors.Repository.RepositoryItemComboBox)
this.barEditItem1.Edit).Items.Clear();
if(workbook!=null)
{
object dafaultSheet="";
for(int i=0;i { sheet=workbook.GetSheetAt(i);//循環(huán)讀取每個(gè) sheet頁(yè)面 ((DevExpress.XtraEditors.Repository.RepositoryItemComboBox)this.barEditItem1.Edit).Items.Add(sheet.SheetName);//加載各個(gè)sheet表的名稱(chēng)到下拉框中供選擇 if(i==0) dafaultSheet=sheet.SheetName; } if(workbook.NumberOfSheets==1)//如果只有一個(gè)sheet頁(yè),則默認(rèn)加載數(shù)據(jù) this.barEditItem1.EditValue=dafaultSheet; else//如果有多個(gè)頁(yè)面,則提示用戶(hù)從下拉框中選擇需要加載的數(shù)據(jù)源 MessageBox.Show("請(qǐng)?jiān)谙吕蛑羞x擇需要計(jì)算的Sheet表單,并提交計(jì)算!"); } } } } (2)LINQ to SQL技術(shù)實(shí)現(xiàn)根據(jù)考核成績(jī)計(jì)算分?jǐn)?shù) 使用LINQ to SQL技術(shù),主要由以下幾個(gè)優(yōu)點(diǎn):一是把查詢(xún)語(yǔ)法直接融入C#語(yǔ)言,關(guān)鍵詞高亮顯示,支持類(lèi)型檢查,允許使用debugger調(diào)試,便于程序開(kāi)發(fā);二是把之前復(fù)雜的查詢(xún)工作封裝起來(lái),語(yǔ)法更加清晰易懂,可讀性較高;三是通過(guò)LINQ查詢(xún)內(nèi)存數(shù)據(jù),避免了多次與數(shù)據(jù)庫(kù)的連接延遲,極大提升了程序的執(zhí)行效率。 需要特別指出的是,在計(jì)算過(guò)程中針對(duì)考核成績(jī)大于100分的分?jǐn)?shù)按照各考核項(xiàng)目的給出的處理方法做了處理;針對(duì)考核成績(jī)下限,按照不同類(lèi)別的人員進(jìn)行了成績(jī)歸零處理。以仰臥起坐分?jǐn)?shù)計(jì)算為例,程序中的關(guān)鍵代碼如下: private void CalculateScore() { ...(省略部分代碼) //// 2.2 計(jì)算仰臥起坐分?jǐn)?shù) var result2=from b in dt_YWQZ.AsEnumerable() where b.Field where b.Field //where a.Field select b; double max_YWQZ=0;string score_YWQZ="0"; foreach(var item in result2) { if(p.YWQZ continue; else if (max_YWQZ < Convert.ToDouble(item["V_NUM"])) { max_YWQZ=Convert.ToDouble(item["V_NUM"]); score_YWQZ=item["SCORE"].ToString(); } //MessageBox.Show(item["ID"].ToString()+"******"+item["V_NUM"].ToString());// } this.gridView1.SetRowCellValue(i,"仰臥起坐分?jǐn)?shù)",score_YWQZ); //如果超出100分 if(score_YWQZ=="100") { this.gridView1.SetRowCellValue(i,"仰臥起坐分?jǐn)?shù)",(100+Math.Floor((p.YWQZ-max_YWQZ)/2)).ToString()); } else if(Convert.ToDouble(score_YWQZ)<65)//按照人員類(lèi)別計(jì)算不及格人員分?jǐn)?shù) { if(p.RYLB=="一類(lèi)") this.gridView1.SetRowCellValue(i,"仰臥起坐分?jǐn)?shù)","0"); else if(p.RYLB=="二類(lèi)"&&Convert.ToDouble(score_YWQZ)<60) this.gridView1.SetRowCellValue(i,"仰臥起坐分?jǐn)?shù)","0"); else if(p.RYLB=="三類(lèi)"&&Convert.ToDouble(score_YWQZ)<55) this.gridView1.SetRowCellValue(i,"仰臥起坐分?jǐn)?shù)","0"); } ...(省略部分代碼) } (3)考核分?jǐn)?shù)表的輸出 通過(guò)Aspose.Words類(lèi)庫(kù),利用Word模板實(shí)現(xiàn)靜態(tài)的創(chuàng)建和填充Word文檔(即:通過(guò)Bookmark書(shū)簽填充文檔內(nèi)容),可以不依賴(lài)Word組件的安裝,方便地實(shí)現(xiàn)數(shù)據(jù)按照既定模板的導(dǎo)出功能。這需要事先制作好成績(jī)輸出的Word模板,并在模板中添加相應(yīng)的書(shū)簽標(biāo)記。這里按照模板要求,做了各個(gè)表格位置的標(biāo)簽,有xh1-xh10(對(duì)應(yīng)各行的序號(hào)),…,zp1-zp10(對(duì)應(yīng)各行的總評(píng))。特別需要指出的是,由于需要計(jì)算的人數(shù)不同,而在標(biāo)準(zhǔn)輸出模板中每頁(yè)最多可以輸出10人成績(jī),這里采用了按照10人一組(最后不滿(mǎn)10人的為一組)輸出成為一個(gè)單獨(dú)文檔,再將各個(gè)文檔合并成一個(gè)文檔的方法實(shí)現(xiàn)。同時(shí),為了輸出數(shù)據(jù)的標(biāo)準(zhǔn)化,系統(tǒng)設(shè)置了數(shù)據(jù)的輸出標(biāo)準(zhǔn)統(tǒng)一格式,增添了數(shù)據(jù)可讀性。程序中的關(guān)鍵代碼如下: private static void CreateDocumentByBookmark(string template?Path,string targetPath) { string templatePath=Application.StartupPath+@"模板.docx";//模板文件 string savePath=null;//最終生成的文件(如果是多個(gè)文件的話(huà) 需要合成) SaveFileDialog saveFileDialog1=new SaveFileDialog(); saveFileDialog1.Filter="Word文檔(*.docx)|*.docx"; saveFileDialog1.RestoreDirectory=true; if(saveFileDialog1.ShowDialog()==DialogResult.OK)//選擇輸出的文件名稱(chēng)和路徑 { savePath=saveFileDialog1.FileName;//輸出的文件名 //創(chuàng)建word文檔 if(File.Exists(templatePath)) { int docCount=(int)Math.Ceiling(this.gridView1.Row?Count/10.0);//計(jì)算需要拆分的word數(shù)量 for(int j=0;j { //加載word模板文件 Aspose.Words.Document doc=new Aspose.Words.Document(templatePath); Aspose.Words.DocumentBuilder builder=new As?pose.Words.DocumentBuilder(doc); if(j { for(int i=0;i<10;i++) //按照每行的書(shū)簽逐個(gè)表格添加數(shù)據(jù) { //1序號(hào) string bookmarkName="xh"+(i+1).ToString();//計(jì)算出需要填寫(xiě)的書(shū)簽名 builder.MoveToBookmark(bookmarkName);//通過(guò)移動(dòng)光標(biāo)到指定的書(shū)簽名,寫(xiě)入內(nèi)容 builder.Write((j*10+i+1).ToString());//將相應(yīng)的內(nèi)容填寫(xiě)到指定的標(biāo)簽位置 //2姓名 …此處省略相似代碼 } } else { //將數(shù)據(jù)逐行加入到文檔中 寫(xiě)標(biāo)簽 for(int i=0;i { //按照每行的書(shū)簽逐個(gè)表格添加數(shù)據(jù) //1序號(hào) string bookmarkName="xh"+(i+1).ToString(); builder.MoveToBookmark(bookmarkName);builder.Write((j*10+i+1).ToString()); //2姓名 ……此處省略相似代碼 } } //添加表頭單位信息 string headmarkName="dm"; builder.MoveToBookmark(headmarkName); builder.Write((this.barEditItem2.EditValue.ToString())); doc.Save(Application.StartupPath+"\temp\"+j.ToString()+".docx",Aspose.Words.SaveFormat.Docx);//文檔的保存,每10行保存為1個(gè)單獨(dú)word文檔 } //進(jìn)行多個(gè)文檔的合成最終輸出文檔 Aspose.Words.Document docx=new Aspose.Words.Document(); docx.RemoveAllChildren(); for(int i=0;i { string fp=Application.StartupPath+"\temp\"+i.ToString()+".docx"; FileStream fs=new FileStream(fp,FileMode.Open); Aspose.Words.Document doc=new Aspose.Words.Document(fs); docx.AppendDocument(doc,Aspose.Words.Import?FormatMode.UseDestinationStyles); fs.Dispose();fs.Close(); } docx.Save(savePath, Aspose.Words.SaveFormat.Docx);//保存合成結(jié)果 } } } 系統(tǒng)運(yùn)行的界面效果如圖1所示,其中黃色背景的列為要導(dǎo)入(或輸入)的數(shù)據(jù)列,屬公共考核科目或個(gè)人信息內(nèi)容;紅色背景列為男軍人專(zhuān)有考核項(xiàng)目;綠色背景列為女軍人專(zhuān)有考核項(xiàng)目;無(wú)背景列為系統(tǒng)計(jì)算并自動(dòng)填充的考核分?jǐn)?shù)列;藍(lán)色背景列為最終得分統(tǒng)計(jì)及總評(píng)結(jié)果列。結(jié)果顯示非常直觀。 圖1 成績(jī)計(jì)算功能主界面 圖2為系統(tǒng)計(jì)算結(jié)果按照預(yù)定模板輸出的登記表。 圖3為體型標(biāo)準(zhǔn)數(shù)據(jù)查看界面,界面采用了Dev?Express控件,可以方便的實(shí)現(xiàn)數(shù)據(jù)的按列分組,按關(guān)鍵字進(jìn)行查詢(xún)篩選等功能。 本文針對(duì)軍人體能考核成績(jī)計(jì)算過(guò)程中存在的實(shí)際困難,詳細(xì)分析系統(tǒng)所需關(guān)鍵技術(shù),利用計(jì)算機(jī)技術(shù)和數(shù)據(jù)庫(kù)技術(shù),設(shè)計(jì)并實(shí)現(xiàn)了軍人體能考核成績(jī)計(jì)算系統(tǒng),并對(duì)系統(tǒng)進(jìn)行了詳細(xì)測(cè)試。系統(tǒng)目前已經(jīng)順利運(yùn)行,切實(shí)滿(mǎn)足了單位的實(shí)際需要,提高了考核成績(jī)的及計(jì)算輸出效率和正確率,極大降低了人工作業(yè)難度,可以在廣大軍警院校及部隊(duì)推廣使用。 圖2 計(jì)算結(jié)果輸出表3 系統(tǒng)運(yùn)行效果
4 結(jié)語(yǔ)