哎,很惭愧,一个多月没写博客了。一直在等这个项目上线后更新博客,今天终于上线了(当然我希望需求不会再更改)。而且最近学习salseforce,学习路漫漫!加油!!
周报
这个周报的项目其实是给公司内部使用的。使用Mysql和Mongo两个数据库。
- Mysql是用来存储人员信息。
- Mongo是用保存周报的信息、以及一些相关的人员配置,周报配置。
增删改查就不说了,没意思。说说下面几个
- 设置管理员:用户不必每次新建周报都要选择分享人以及领导人。
- 导出周报:说起这个我就来气。就这个简单的东西非要搞得很奇怪。不导出周报信息,要导出人员信息= =,一会再具体说吧。
- 设置管理员:管理员有权限进行查询导出。
controller
/**
* 周报导出(以及导出前分页查询)
*
* @param queryBean
* 封装分页查询
* @param operation
* 判断是查询还是导出.值为'export'时是导出;为空或者任意字符串时,是分页查询。
* @return
*/
@RequestMapping(value = "/apply-export", method = RequestMethod.GET)
@ApiOperation(value = "周报导出及分页查询", notes = "分页查询时'operation'可以为空,或为任意字符串;当需要导出时,'operation'必须为'export'", responseReference = "Page", produces = "application/json")
public Rest getReports(QueryBean queryBean , @RequestParam String operation) {
QContext context = getContext();
// 判断是否有导出权限
if(!workReportService.isAdmin(context)) {
return Rest.info(Sys.StateCode.STATUS_CODE_FORBIDDEN, "对不起,没有导出权限");
}
WebParamInfo webParamInfo = getWebParamInfo(queryBean);
if(!StrUtils.isEmpty(operation) && "export".equals(operation)) {
HttpServletResponse response = getResponse();
HttpServletRequest request = getRequest();
workReportService.exportReport(context, webParamInfo, response, request);
return null;
}else {
Page<WorkReport> list = workReportService.findExportReportBypage(context, webParamInfo);
return Rest.page(Sys.StateCode.STATUS_CODE_SUCCESS, list);
}
}
其实整个项目没有什么难度,之所以做的慢,是因为数据错误(这个我不是负责,只是简单的看了看),以及导出速度太慢。一直在优化,影响最大的地方就是查询导出,因为要导出的是人员基本信息和职位信息,这是两个表,我需要去查当前周报创建人信息。在客户端查的时候,才几十毫秒,很快。但是一旦通过代码进行实现,就变成分钟级别,很恐怖。而且人真不多,9000而已。
我先说说几次优化吧。
最下面的代码是最终的解决方案。用了最笨的办法但是也是最有效的,时间换空间。将所有人员信息在创建周报的时候就存放在一个javabean中,导出的时候直接取周报就行。这样确实快了很多。用脚本测的时候查询周报的时候略慢,但是完全能接受了。
第一次:也就是刚写出来的时候。想的很简单,因为是多个周报么,每个周报都需要设置数据,如果每次都查询的话,那1000个周报,1000个人创建人,要我查1000次?太恐怖了,还不如一次性全部查出来。但是这个时候其中一个问题就出来了,几条记录怎么办?先试试查所有要多久吧,OK。Person person = personService.findall();这个查询速度真的太慢了。肯定是不行的。然后我试了试添加查询条件,查询几个人,还是很快的,稍微多一点就不行了。所以这个方法pass掉。
第二次:其实也是第一次的遗留问题,就算我导出1w条记,录,但是其实就是100个人创建的。就是一个创建人存在多条周报。OK,那我就设置一个集合,存放已经查询过得人的信息。每次查询之前先去找是否存在。
// creaters集合,存放创建人
Map<String, Person> creaters = new HashMap<String, Person>();
Person person =null;
String id = workReport.getCreateBy().getId();
// 判断当前周报创建人是否存在。如果不存在,再去查询,同时放进map集合中
if(creaters.get(id) == null) {
person = personService.findByItcode(context, workReport.getCreateBy().getItcode());
creaters.put(id, person);
}else {
person = creaters.get(id);
}
其实这么写,还是老问题,创建人多了,查询就变慢了。哎。。。因为这个我还恶补了一下java代码优化。把整个导出部分的代码都优化了一遍。效果甚微啊。代码很长,但是都是需要的字段,逻辑部分其实很少。
代码既然优化到此结束,查询的慢,那就是数据的问题了。抛出该person表中一大堆无用字段不提。好多的约束条件我也是第一次看到。这么多的约束条件,对查询速度肯定会有影响,而且当表大了之后,影响会更大。约束,更多是为了保证数据的正确性,一定程度上保证了安全性。而正确性、安全性和性能是天平的两个方面,就像鱼和熊掌,不可兼得。
Impl-最终的解决方案
/**
*代码中的exportPersonInformation就是放在周报中的字段,用来存放人员信息。
*/
@Override
public void exportReport(QContext context, WebParamInfo webParamInfo, HttpServletResponse response,
HttpServletRequest request) {
Criteria criteria = Criteria.where("org").is(context.getOrg()).and("status").is(SUBMIT);
Map<String, Object> param = webParamInfo.getParam();
Object filterObject = param.get("filterUsers");
List<String> userIds = getUserIds(filterObject);
if (userIds != null && userIds.size() > 0) {
criteria.and("createBy._id").in(userIds);
}
// 根据日期范围进行查询
Criteria dateCrita = getDateCrita(param);
criteria.andOperator(dateCrita);
Query query = new Query();
query.addCriteria(criteria);
querySetSort(query, webParamInfo);
List<WorkReport> workReports = findAll(context, query);
if (workReports != null && workReports.size() > 0) {
try {
Map<String, String> heads = new TreeMap<String, String>();
heads.put("A", "周报名称");
.
.
.
heads.put("Y", exportPersonInformation.getName());
List<Map<String, String>> datas = new ArrayList<Map<String, String>>();
datas.add(heads);// 添加表头
for (WorkReport workReport : workReports) {
datas.add(setData(workReport));// 添加数据
}
String date = DateUtils.convertDateToString(new Date());
String fileName = "周报导出" + date + ".xls";
response.setHeader("Content-disposition",
"attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
OutputStream outStream = response.getOutputStream();
ExcelExport export = new ExcelExport();
export.export(outStream, datas);// 导出excel
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Map<String, String> setData(WorkReport workReport) {
Map<String, String> map = new HashMap<String, String>();
ExportPersonInformation exportPersonInformation = workReport.getExportPersonInformation();
// 设置数据
map.put("A", DateUtils.getDateStr(workReport.getStartDate()) + "~" + DateUtils.getDateStr(workReport.getEndDate()) + "的周报");// 周报名称
.
.
.
map.put("Y", *****);
return map;
}