前言
疫情期间统计核酸检测情况在学校这种人数众多的情况下是非常常见的需求,这个demo实现了核酸检测情况的统计。我做这个demo也属于迫不得已了(果然,人被逼急了潜力就会被激发)。
详细代码可在github中查看GitHub地址
心路历程
我在成都读大学,最近开学正好处于疫情管控时期,我们学校规定学生到校必须进行核酸检测,而且每个班都要统计核酸检测情况。我是班委,统计工作自然就落到了我的身上。但好巧不巧,我最近事还特别多。时间安排上本就不够充裕,然而这个统计工作还是很费时间的工作。真是应了祸不单行。又加上因为疫情原因国庆假期还在校封闭管理,我就更郁闷了。虽然心情郁闷,但这个工作不能不干(妥妥的社畜了)。根据学校的各个统计要求。我发现这个工作虽然比较花时间,但工作内容就是简单、机械的重复动作。收集每个人的核酸结果并留档,统计每个人在管控期间核酸检查情况。我心想:都是重复性的操作,能不能写段代码帮我完成呢?这个业务逻辑应该不复杂。
我有了这个想法就把手上的其它事情稍微停了一下,开始做这个小demo。在熬夜制作分析,调查后有了大致解决思路并完成了核心代码的编写。在第二天又添加了简单的交互界面。再完成截屏收集这个需求后,我想能不能做统计核酸检测的需求呢?有了这个想法,我就又开始构思如何实现。因为这个没有收集截屏需求那么急,再加上还有其它事要做。所以这个需求的开发进度不是很快。后面陆陆续续又添加了一下功能。最终形成了这个小demo。
虽然这个demo并没有达到一个完整项目的水平,但作为一个提高工作效率的工具对我来说还是很有成就感的。尤其是在我把这个demo分享给其它班负责收集核酸统计结果的同学,让我感觉我熬的夜没有白费很有意义,这个demo很有意义。
开发过程
1. 想法萌生
在最开始,我并没有想到要做自动收集截屏的功能。仅仅是因为收集要求要把每个人的核酸检测结果留档保存,我想的是每个人建立一个对应的文件夹,便于管理。但一个班好歹都有几十个人啊,手动去建文件夹那很耗时间的。于是我就想用程序来自动给每个人建对应的文件夹。由于花名册是excel表,我便调查了一下能够处理excel表的相关工具包。我最开始了解到的是easyexcel,这个依赖对于建文件的要求能够很好的满足。代码并不是很难,只是对easyexcel的基本使用。
建好文件夹后,我认为easyexcel的功能还是很强大的。便想能不能把截屏也收集了呢?我分析了一下这个需求能不能实现。要利用easyexcel实习截屏的收集,那么就必须先将截屏获取,再将截屏存到对应的文件夹中。那么该如何获取截屏呢?我想到了QQ的收集表,收集表可以收集图片,那么这些图片是如何收集的呢?我测试了一下QQ的收集表,发现它是将图片存到腾讯的服务器中,然后再记录图片存放的URL地址,用于图片的查看和下载。到这,我大概有了收集截屏的思路。先利用QQ收集表发布截屏的收集,再利用收集表的内容收集截屏。
流程如下:
- 发布收集表
- 将收集表的收集结果导出为excel表格
- 运行代码通过截屏的URL保存图片
主要代码如下:
String excelPath="D:\\桌面\\核酸统计\\9.29.xlsx";//excel表格位置
EasyExcel.read(excelPath, Person.class, new AnalysisEventListener<Person>() {
@Override
public void invoke(Person person, AnalysisContext analysisContext) {
if (person.getCode()!=null){
System.out.println("正在下载"+person.getName()+"核酸截屏……");
String imgUrl=person.getImgUrl();//获取截屏url
//建立接受截屏的文件
String oldPath="D:\\桌面\\temp\\9.29.png",newPath="D:\\桌面\\temp\\"+person.getCode()+person.getName();
File oldFile=new File(oldPath),newFile=new File(newPath);
if (!newFile.exists()){
try {
newFile.mkdir();//创建对应人员文件夹
newPath=newPath+"\\9.29.png";
newFile=new File(newPath);
Files.copy(oldFile.toPath(),newFile.toPath());
} catch (IOException e) {
e.printStackTrace();
}
}else {
newPath=newPath+"\\9.29.png";
newFile=new File(newPath);
if (newFile.exists()) newFile.delete();
try {
Files.copy(oldFile.toPath(),newFile.toPath());
} catch (IOException e) {
e.printStackTrace();
}
}
String imgPath=newPath;//开始下载截屏
File img=new File(imgPath);
if (img.exists()) {
HttpURLConnection connection=null;
InputStream inputStream=null;
FileOutputStream outputStream=null;
try {
URL url=new URL(imgUrl);
connection= (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(10*1000);
connection.setReadTimeout(10*1000);
inputStream=connection.getInputStream();
outputStream=new FileOutputStream(img);
byte[] buffer=new byte[1024];
int len;
while ((len=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
System.out.println("下载完成");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection!=null) connection.disconnect();
if (outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("当天核酸收集完成");
}
}).sheet("9.29日核酸检测报告收集(收集结果)_202209302_3").doRead();
2. 系统构建
到这步之后,我就准备将这个段代码做成一个小工具,并且再添加几个功能。我开始构思大概要做成什么样子,所需的依赖有那些等等。最终我准备用SpringBoot做,因为SpringBoot基本可以满足我所需要的所有要求。不论是图形化界面,还是依赖管理这些。
在我开始系统的建立这个工具的时候,我准备还是使用easyexcel进行操作。但在功能实现的过程中,发现easyexcel更适合规模化处理数据,并不适合针对性的处理数据。我又去调查了其它的支持处理excel的工具,发现了同样可以处理excel的工具:POI、easypoi。这两个工具中的easypoi和easyexcel功能类似,其弊端也什么相似。easypoi和easyexcel将API封装的太好了,并没有暴露一下可以对excel针对性操作的接口,使得这两个依赖并不适合我的这个工具。因此,我选择了POI。在使用POI的时候,也并不是一帆风顺,首先就是遇到了POI依赖版本的不兼容问题。最终花了一定的时间才解决。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.2</version>
<scope>compile</scope>
</dependency>
在开发过程中,我发现当前的这个思路实现并不能实现,从而要不断的修改代码。我开始先测试当前思路是否可以实现该功能,能够实现之后,我才将代码补全到整个处理逻辑中。这样避免了很多不必要的麻烦。
在后续的开发中也遇到了很多障碍,如:POI处理未填充的单元格时的判断、多个文件流操作同一个文件对象时无法获取文件信息等。最终经过几天磕磕绊绊的开发,还是做成了我想要的工具。
主要业务逻辑代码如下:
/**
*
* @param handExcel 收集的excel表格
* @param holdPosition 统计结果保存位置
* @param imgPosition 截屏保存位置
* @return
*/
public boolean collectImg(String handExcel,String holdPosition,String imgPosition){
boolean flag=true;
File infoExcel=new File(handExcel),holdExcel=new File(holdPosition);
if (!infoExcel.exists()) return false;
List<Person> personList=getTodayPersonInfo(handExcel);//获取所有当天所有学生信息
savePhoto(personList,imgPosition);//保存所有学生核酸检测截屏
Map<String,List<Person>> map=classifyByBackTime(personList);//数据信息分类
if (holdExcel.isDirectory()){//统计结果文件的预处理
holdPosition=pretreatment("软工205核酸统计",holdPosition, map.keySet());
holdExcel=new File(holdPosition);
for (String backTime: map.keySet()){//填充基本信息
fillBasicInfoForSheet(backTime,holdPosition,map.get(backTime));
}
}
censusTodayCondition(holdPosition,map);//统计当天核酸情况
return flag;
}
前端太丑就不展示了。
3. 运行结果
总结
这个经历让我对软件开发有了新的认识。
- 软件开发要多实践,不能纸上谈兵。在平常的学习中,我们虽然学习了很多知识,甚至还自学了很多东西。但都只是浮于表面并没有真实的去应用这些知识,离真正的领悟这些知识还存在一段距离。
- 要善于发现需求,很多时候用户自身都没有发现或不知道自己具体需要什么需求,这对开发软件造成了很大的障碍。
- 在开发时要先想好、多构思。在知道要干什么后,要先想好具体怎么干,而不是直接开始写代码。直接写代码,可能会让开发在本不适合的实现方案中不断修正。最终可能会完成该需求,但代码质量并不高。
- 选择合适的技术支持,项目中的技术不一定要使用最新的(最新的技术并不意味着就很好,或者说最新的技术并不意味着就适合本项目)。选择最适合的技术才是对的。
发现需求,很多时候用户自身都没有发现或不知道自己具体需要什么需求,这对开发软件造成了很大的障碍。 - 在开发时要先想好、多构思。在知道要干什么后,要先想好具体怎么干,而不是直接开始写代码。直接写代码,可能会让开发在本不适合的实现方案中不断修正。最终可能会完成该需求,但代码质量并不高。
- 选择合适的技术支持,项目中的技术不一定要使用最新的(最新的技术并不意味着就很好,或者说最新的技术并不意味着就适合本项目)。选择最适合的技术才是对的。
- 项目的开发是在不断迭代的过程,通过不断的迭代,才能不断完善功能需求。