从挑密小程序的开发看程序开发的演进

本文详细介绍了挑密小程序的开发过程,从需求分析到软件设计,再到编码与重构,最终实现自动化挑选敏感信息的功能,提高了工作效率。

从挑密小程序的开发看程序开发的演进

在接到开发任务之后,首先要做的事情就是需求分析,还有需求分析之后,要做的就是可行性分析,分析一下有没有技术难点

(一)需求描述
档案数字化扫描之后,会有一些涉密或者敏感信息挂接在档案系统里面,其中有一些档案是不对外开放的,我们需要将这些涉密或者敏感的数据挑选出来,挑选的依据就是客户给的一些敏感词。目前这个工作是实施员手工干的:实施员拿着用户给的敏感词,在系统中挨个去搜索,然后倒出。需要花大量的时间,还有可能出错,比如漏挑的情况。 现在要做的事情就是做一个程序,代替人工来挑

(二)需求分析
拿到这个需求之后,大体思考了一下,应该没有什么难度,无非就是遍历所有档案数据,然后和敏感词作比较,找到之后,写入到excel中,同时将原文拷贝出来。 具体如下:

  1. 配置的读取
  2. 数据的读取
  3. 敏感词比较
  4. 写入excel
  5. 拷贝原文
  6. 写日志,便于问题排查

上面这六个需求中只有4,5是用户的核心需求,剩下4个只是为其做铺垫。

(三)软件设计
针对需求上的6个功能,逐个来做软件设计

  1. 配置的读取
    在配置读取上,有两个可选方案,一个是读取excel文件,一个是读取json串。这两种方案各有好处
优缺点读取excel读取json
优点直观,简单表达性强, 可以支持非结构化数据
缺点表达性弱稍微有点门槛

以后后台程序标准的配置接口就是json串,在json之上可以再接入excel或者其他配置界面。 这样对后台架构没有什么影响。

  1. 数据读取
    数据的来源可以有多种,可以是数据库,也可以是数据文件。
    为了减少难度,这里是通过直接读取档案系统的数据库配置文件来实现的。

  2. 敏感词比较
    这个比较简单,将敏感词放入到一个数组里面,然后挨个去比较。

  3. 写入excel
    仅仅是写入excel比较好实现,如果要实现文件大小控制,需要考虑一下如何降低复杂度。

  4. 拷贝原文
    用的apache的io common包,拷贝文件只要一句话

  5. 写日志
    将程序进行到每个状态记入到日志,对于出错的数据,异常的数据,把标题和id写入到日志文件中。 每个库倒了多少条,有哪些敏感词被挑也要写入到日志文件。

(四)编码阶段
编码阶段首先要做的不是如何代码优美,而是考虑到如何将功能实现,所以代码可以很乱,可以是面向过程来边写,但是每个函数必须要独立,争取做到每个函数就是一个“微服务”,做到这一点之后,后面重构代码就简单了。
通过idea的历史记录来重放一下代码历程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MCvASIY7-1572227346394)(en-resource://database/2401:1)]

  • 版本1,仅仅写了一个数据库连接的接口
package com.yundang.tools.sensitive;
import java.sql.Connection;
public class Main {    
    public Connection getConn(){        
        return null;    
    }    
    public static void main(String[] args) {      
    // write your code here            
    }
}
  • 版本2,后面陆续写了这些接口,都没有实现。
package com.yundang.tools.sensitive;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Main {

    public Connection getConn(){
        return null;
    }

    public String getRootPath(){
        return null;
    }

    public Map getDict(){
        return null;
    }

    //多个文件,以逗号隔开。
    public List<Map> getAssetList(){
        return null;
    }

    public List<Map> getFileList(String assetid){
        return null;
    }

    //查询有哪些category
    public List<String> getCategoryList(){
        return null;
    }

    public boolean iscontains(String title,List<String> filepath){

        return false;
    }

    public static void main(String[] args) {
	// write your code here

    }
}

  • 版本3,开始实现接口,首先实现的是是否包含敏感词接口,这个是核心业务,本程序主要功能就是要判断是否包含敏感信息,所以它首先实现。
    public boolean iscontains(String title,List<String> filepath){
        Map dict = getDict();
        filepath.forEach((p)->{
            String content = FileUtils.get(p);
            dict.forEach((k,v)->{
                if(title.indexOf(k)>-1){
                    return true;
                }
                if(content.indexOf(k)>-1){
                    return true;
                }
            }
        });

        return false;
    }

还加了一个组合方法,将判断是否包含敏感词、拷贝文件、写excel等核心业务打包放到一个方法中。

    //支持多线程
    public void deal(Map asset,List<String> filepath,List<Map> destasset){
        String title = (String)asset.get("filename");
        if(iscontains(title,filepath)){
            destasset.add(asset);
        }
        //写入到excel文件
        //拷贝文件到指定目录
        //日志文件,写入每个文件的状态。是否包含实体文件,文件个数。
    }
  • 版本4,这里犯了一个错误,过早的引入多线程机制,导致后面的复杂度上升。 实践证明,过早引入无关的设计,会增加系统的难度和可不预料性。
private static ExecutorService threadPool = 
Executors.newFixedThreadPool(1);
//线程池里面的线程数会动态变化,并可在线程线被移除前重用
  • 版本5,开始查库,将查库逻辑写出来,这里并没有真正把sql写出来,而是仅仅将接口和逻辑写出来了。 sql会在后面统一写。 同时写了拷贝文件逻辑
    public static void main(String[] args) {
	// write your code here
        Main m = new Main();
        List<String> catelist = m.getCategoryList();
        for(String categoryid:catelist){
            int countCatgegory = m.countCatgegory(categoryid);
            List<Map> assetlist = m.getAssetList(categoryid,pageNo,pageSize);
            assetlist.forEach((asset)->{
                List<Map> filelist = m.getFileList(asset.get("id").toString());
                asset.put("fileList",filelist);
                m.deal(asset);
            });
        }
        ...

//拷贝文件逻辑
    public void copyFile(){
        destasset.forEach((asset)->{
            List<Map> fileList = (List<Map>)asset.get("fileList");
            if(fileList==null){
                return;
            }
            fileList.forEach((file)->{
                String path = (String)file.get("path");
                String destpath = path.replaceAll(getRootPath(),getCurrentRootPath());
                new File(destpath).getParentFile().mkdirs();
                try {
                    FileUtils.copyFile(new File(path),new File(destpath));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        });

    }


  • 版本6 读取配置文件,读完之后,没有马上用过dom解析,而是转向去做excel文件的读取。
    这里面隐含的我一个心理: 尽快将不确定的东西确定下来,如果自己已经知道的、确定无误的,可以往后放放去实现。

配置信息到底是用excel还是json,如果遇到决策困难的时候,将你的几个决策在笔记本上写出来。然后逐个分析各自的优缺点。 你就自然而然的看到结果了。 庆幸当初选的是json,要选的excel做配置,后面写的东西就比较被动。例如筛选条件就不好加(数据筛选条件的需求我当时没想到,确实应该有)

    static Properties config = new Properties();

    static{
        InputStream in = Main.class.getResourceAsStream("/config.properties");
        try {
            config.load(in);
//            db=D:\\workspace\\btold\\bt\\WEB-INF\\classes\\SystemConfigFDAM.xml
//                    web=C:\\apache-Tomcat5.5\\webapps\\WeblishFS
            String dbpath = config.getProperty("db");
            String path = "/SysConfig/";
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	
  public static void main(String[] args) {
	// write your code here
        Main m = new Main();
        Connection conn = null;
        String exceltemplate = currentRootPath+"template.xls";
        try {
            FileInputStream finFileInputStream=new FileInputStream(exceltemplate);
            Workbook wookbook = null;
            try {
                wookbook = new XSSFWorkbook(exceltemplate);
            } catch (Exception ex) {
                wookbook = new HSSFWorkbook(new FileInputStream(exceltemplate));
            }
            String json = "";
//            {
//                "name":"文书案卷级",
//                    "archtypecode":"W",
//                    "categorytype":"0",
//                    "fieldcname":"全宗号,类别号,全宗单位,属类号,归档年度,归档单位,保管期限,目录号,案卷号,密级,案卷题名,起始时间,截止时间,共几件,共几页,著录人,著录时间,备注,存放位置"
//            },
            JSONArray array = JSON.parseArray(json);
            for(Object obj:array){
                JSONObject jo = (JSONObject)obj;
                Sheet sheet = wookbook.createSheet(jo.getString("name"));
            }
    
  • 版本7 以下版本是第二天早上8点之前就到了公司做的,当时在地铁上思考,如何将程序做简单,思考了微服务和面向对象的区别:面向对象和面向微服务的概念不一样,面向微服务是一个比面向对象更大的范畴,有点类似于道和术的概念—因为微服务可能是一个面向过程语言写的,它不关注你怎么去实现。
    简单一点来说,微服务类似于组件化开发,只是微服务的组件化的颗粒度比较大一些。

回到问题本身,这里最难的恐怕就excel的创建,如何让excel的新建sheet,写行,写文件不影响到总体程序的复杂度。 让写excel成为一个服务,我外面只管将数据给你,至于你怎么换sheet,怎么换文件,我外面的主程序不关心。 那是你自己的事情。

程序是由各个服务组成的,就像一个团队,团队之间每个人的分工很明确,你只要把需求告诉他,剩下的你就不用操心,他自动帮你完成。

这个道理我估计很多人都懂,但是能深刻认识到,并且执行到位的,估计没有几个人。

(五) 代码重构
在代码已经实现了之后,很多人经常会忘记重构一下代码,以为把功能做完就行了。 但实现功能的时候,我们是不需要考虑代码可读性的。 我把这一步称之为“装修完之后的到扫”,好比装修房子,装修完了,不能马上入住,你要先将建筑材料之类的东西清理干净,你才能入住。

  • 版本8 ,目前为止,已经将所有功能已经实现,但都写在一个类里面, 可读性不是太好,版本8将这个类拆分:
    1)业务层: 真正的业务处理,挑敏感词、拷贝文件、调用写excel服务。 Task.java
    2)配置层: 读取配置信息,类似于数据准备层,区别在于这里没有业务数据 Config.java
    3)数据准备层: 为主流程准备数据。 Main.java
    4)主流程层:主流程层里面主要是将各个模块组装起来到一起。 Main.java
  1. excel处理:封装底层poi操作,实现换sheet和换文件功能,对外提供些excel服务。DocExcel.java

(六) 待改进的地方

  1. 参数传递太多
  2. 写日志的没有用log4j,最后才写日志。

(七)总结
经常看到一些新人拿到一个需求的时候,没有一点思路,即使把功能完成之后,写的代码也很乱,期望这篇文档,能给你一点启发。

以下链接是本项目的
链接:https://pan.baidu.com/s/1DnPq3M-383jqxcnMUqS0Mg
提取码:6jra
复制这段内容后打开百度网盘手机App,操作更方便哦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值