##需求及思路详解
根据产品及需求,我们需要实现对采购品各分类下的采购品进行导出功能。
(可能会出现的问题):①因为要对采购品各分类下采购品进行导出excel文件的功能,所以要考虑到根目录及子目录下采购品数量及导出的不同。②因为dba对数据要求每次查询不大于8mb,所以要对查询数据进行分批查询。③:文件名乱码问题。④:导入所需jar包到pom文件,因博主是由公司自己封装的esaypoi框架,故在此不列出。
##后台代码实现
##controller层#
@ApiOperation(value = "导出采购品到Excel文件", httpMethod = "GET")
@RequestMapping(value = "downCorpDataByExcel", method = RequestMethod.GET)
public void downCorpDataByExcel(@RequestParam(required = false) String name,
@RequestParam(required = false) String code,
@RequestParam(required = false) String spec,
@RequestParam(required = true) Long catalogId,
HttpServletResponse response,
HttpServletRequest request) {
TRegUser user = UserContext.getUser();
if (user == null) {
logger.warn("获取不到用户信息");
return;
}
CorpDirectorysQueryDto cdQuery = new CorpDirectorysQueryDto();
//公司id
cdQuery.setCompanyId(user.getCompanyId());
//采购品名称模糊查询
if (StringUtils.isNotBlank(name)) {
cdQuery.setNameLike(name.trim());
}
//采购品编码模糊查询
if (StringUtils.isNotBlank(code)) {
cdQuery.setCodeLike(code.trim());
}
//采购品规格型号模糊查询
if (StringUtils.isNotBlank(spec)) {
cdQuery.setSpecLike(spec.trim());
}
//采购品分类id(有前端从tree中获取)
if (catalogId != null) {
//判断当前目录是否有子目录
① boolean b = corpDirectoryService.hasExistChild(catalogId, user.getCompanyId());
logger.info("方法downCorpDataByExcel,公司{}目录{}是否有子目录:{}", user.getCompanyId(), catalogId, b);
if (b) {
//如果有,查询下当前子目录路径,通过路径去查询采购品列表
② CorpCatalogs corpCatalogs = corpCatalogService.findById(catalogId, user.getCompanyId());
//会查询like CONCAT(#{treepathLike},'%')
cdQuery.setTreepathLike(corpCatalogs.getTreepath());
} else {
cdQuery.setCatalogId(catalogId);
}
}
PageSet pageSet = new PageSet();
pageSet.setSortColumn("abandon asc,create_time desc");
String corpDataName = "采购品信息.xls";
//处理文件名乱码的问题
③ corpDataName = encodeDownloadFile(corpDataName, request.getHeader("User-Agent"));
response.setHeader("content-disposition", "attachment;filename=" + corpDataName);
//根据文件名自动获得文件类型
response.setContentType(request.getSession().getServletContext().getMimeType(corpDataName));
//告知服务器使用什么编码
response.setCharacterEncoding("UTF-8");
try {
④ HSSFWorkbook workbook = corpDirectoryService.createWorkbook(cdQuery, pageSet);
workbook.write(response.getOutputStream());
} catch (Exception e) {
logger.error("构建excel出错", e);
}
}
/**判断当前目录是否有子目录
*/
①:
public boolean hasExistChild(Long catalogId, Long companyId) {
ServiceResult booleanResult = dubboCorpCatalogsService.hasExistChild(catalogId, companyId);
if (!booleanResult.getSuccess()) {
logger.error("{}调用{}时发生未知异常,error Message:{}", "cn.bidlink.procurement.materials.app.service.CorpDirectoryService.hasExistChild",
" dubboCorpCatalogsService.hasExistChild(catalogId,companyId)", booleanResult.getCode() + "_" + booleanResult.getMessage());
throw new RuntimeException("err_code:" + booleanResult.getCode() + ",err_msg:" + booleanResult.getMessage());
}
Boolean result = booleanResult.getResult();
if (result == null) {
logger.warn("cn.bidlink.procurement.materials.app.service.CorpDirectoryService.hasExistChild时未获取到结果");
}
return result;
}
/**
如果有子目录,查询下当前子目录路径,通过路径去查询采购品列表
*/
②:
/**
* 获取采购品
* @param id
* @param companyId
* @return
*/
public CorpCatalogs findById(Long id, Long companyId){
if ( id == null || companyId == null ){
throw new RuntimeException("参数不能为空");
}
ServiceResult findResult = dubboCorpCatalogsService.findByPK(id, companyId);
if ( !findResult.getSuccess() ){
throw new RuntimeException( findResult.toString() );
}
return findResult.getResult();
}
/**
* 处理文件名乱码的问题 , 不通用
* @param filename 文件名字
* @param agent 浏览器信息
* @return 解决乱码后的文件名
*/
③:
private String encodeDownloadFile(String filename, String agent){
try {
if (StringUtils.isNotBlank( agent ) && agent.toUpperCase().indexOf("MSIE") > 0) {
filename = URLEncoder.encode(filename, "UTF-8");
} else {
filename = new String(filename.getBytes("UTF-8"), "ISO8859-1");
}
} catch (UnsupportedEncodingException e) {
logger.error("处理文件名乱码的问题时发生异常",e);
}
return filename;
}
④-1: /**
* 构建采购品数据数据
*
* @param cdQuery 查询对象
* @param pageSet 分页对象
* @return
*/
public HSSFWorkbook createWorkbook(CorpDirectorysQueryDto cdQuery, PageSet pageSet) {
logger.info("方法createWorkbook(CorpDirectorysQueryDto cdQuery,PageSet pageSet)查询采购品数据开始,参数:cdQuery:{}", cdQuery);
//先查询所导出的采购品数量
ServiceResult serviceResult = dubboCorpDirectorysService.getTotal(cdQuery);
Integer count = serviceResult.getResult();
List aList = new ArrayList<>();
Integer pageSize = 2000;
//数量大于2000时,分批查询数据库数据(防止查询数据量太大)
if (count >= 2000) {
int times = count / pageSize;
if (count % pageSize != 0) {
times = times + 1;
}
Integer pageNum = 1;
for (int i = 0; i < times; i++) {
pageSet.setPageNum(pageNum);
pageSet.setPageSize(pageSize);
ServiceResult> queryResult = dubboCorpDirectorysService.findByCondition(cdQuery, pageSet);
aList.addAll(queryResult.getResult());
pageNum++;
}
} else {
//数据量少于2000时直接放入集合
ServiceResult> queryResult = dubboCorpDirectorysService.findByCondition(cdQuery, pageSet);
logger.info("方法createWorkbook(CorpDirectorysQueryDto cdQuery,PageSet pageSet)查询采购品数据结束");
if (!queryResult.getSuccess()) {
logger.error("查询数据失败,失败信息:{}", queryResult);
throw new RuntimeException("查询数据失败");
}
aList = queryResult.getResult();
}
return createCorpDataExcel(aList);
}
④-2 /**
* 根据数据生成Excel
*
* @param corpDirectoryList 采购品列表
* @return Excel对象
*/
public HSSFWorkbook createCorpDataExcel(List corpDirectoryList) {
logger.info("方法createCorpDataExcel(List corpDirectoryList)构建Excel对象开始");
// 在内存中创建一个Excel文件,通过输出流写到客户端提供下载
HSSFWorkbook workbook = new HSSFWorkbook();
//设置文字的样式
HSSFCellStyle cellStyle = workbook.createCellStyle();
cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
HSSFFont font = workbook.createFont();
//HSSFColor.VIOLET.index //字体颜色
font.setColor(HSSFColor.BLACK.index);
font.setFontHeightInPoints((short) 12);
// 创建一个sheet页
HSSFSheet sheet = workbook.createSheet("采购品信息");
// 创建标题行
HSSFRow headRow = sheet.createRow(0);
headRow.createCell(0).setCellValue("采购品编码");
headRow.createCell(1).setCellValue("采购品名称");
headRow.createCell(2).setCellValue("规格型号");
headRow.createCell(3).setCellValue("技术参数");
headRow.createCell(4).setCellValue("计量单位");
headRow.createCell(5).setCellValue("所在分类");
headRow.createCell(6).setCellValue("用途");
headRow.createCell(7).setCellValue("计划单价");
headRow.createCell(8).setCellValue("备注");
headRow.createCell(9).setCellValue("状态");
//插入采购品数据
for (CorpDirectorys corpDirectorys : corpDirectoryList) {
HSSFRow dataRow = sheet.createRow(sheet.getLastRowNum() + 1);
//采购品编码
dataRow.createCell(0).setCellValue(corpDirectorys.getCode());
dataRow.createCell(1).setCellValue(trim(corpDirectorys.getName()));//采购品名称
dataRow.createCell(2).setCellValue(trim(corpDirectorys.getSpec()));//规格/型号
dataRow.createCell(3).setCellValue(trim(corpDirectorys.getTechParameters()));//技术参数/材质
dataRow.createCell(4).setCellValue(trim(corpDirectorys.getUnitName()));//计量单位
dataRow.createCell(5).setCellValue(trim(corpDirectorys.getCatalogName()));//所在分类
dataRow.createCell(6).setCellValue(trim(corpDirectorys.getPurpose()));//用途
dataRow.createCell(7).setCellValue(trim(corpDirectorys.getMarketPrice()));//计划单价
dataRow.createCell(8).setCellValue(trim(corpDirectorys.getDemo()));//备注
dataRow.createCell(9).setCellValue(CorpDirectorysAbandonEnum.ENABLE.getValue() == corpDirectorys.getAbandon() ? "已启用" : "已禁用");//备注
}
logger.info("方法createCorpDataExcel(List corpDirectoryList)构建Excel对象结束");
return workbook;
}
##dubbo接口的service层#
① //是否存在子分类
ServiceResult hasExistChild(Long id, Long companyId);
②//如果有子目录,查询下当前子目录路径,通过路径去查询采购品列表
ServiceResult findByPK(Long id, Long companyId);
③ //查询出需要导出采购品的数量
ServiceResult getTotal(CorpDirectorysQueryDto corpDirectorysQueryDto);
④//查询出需要导出采购品数据
ServiceResult> findByCondition(CorpDirectorysQueryDto corpDirectorysQueryDto, PageSet pageSet);
##dubbo接口的serviceImpl层#
① //是否存在子分类
@Override
public ServiceResult hasExistChild(Long id, Long companyId) {
ServiceResult result = new ServiceResult();
try{
CorpCatalogs corpCatalogs = new CorpCatalogs();
corpCatalogs.setParentId( id );
corpCatalogs.setCompanyId( companyId );
Long page_count = corpCatalogDao.findPage_count(corpCatalogs);
if ( page_count != null && page_count > 0 ){
result.setResult( true );
}else {
result.setResult( false );
}
}catch(Exception e){
log.error("调用{}方法 异常", "[hasExistChild]");
log.error("方法使用参数:[id:{},companyId:{}]", id,companyId);
log.error("异常信息:{}", e);
result.setErrMessage("调用 hasExistChild 方法异常,异常信息:" + e.getMessage());
}
return result;
}
②//如果有子目录,查询下当前子目录路径,通过路径去查询采购品列表
@Override
public ServiceResult findByPK(Long id, Long companyId){
ServiceResult result = new ServiceResult();
try{
CorpCatalogs resultEntity = null;
if ( id != null && companyId != null){
resultEntity = corpCatalogDao.getById(id, companyId);
}
result.setResult(resultEntity);
}catch(Exception e){
log.error("调用{}根据ID查询对应的条目时发生未知异常异常{},", "[findByPK]", e.getMessage());
log.error("[id, companyId]{}", id + ";" + companyId);
log.error("堆栈异常信息:{}", e);
result.setErrMessage("根据ID查询对应的条目时发生未知异常!");
}
return result;
}
③ //查询出需要导出采购品的数量
@Override
public ServiceResult getTotal(CorpDirectorysQueryDto corpDirectorysQueryDto) {
ServiceResult result = new ServiceResult<>();
try{
Integer planListCount = corpDirectorysDao.getTotal(corpDirectorysQueryDto);
result.setResult(planListCount);
}catch(Exception e){
log.error("调用{}方法 异常", "[DubboPlanServiceImpl.getMyPlanListCount]");
log.error("方法使用参数:");
log.error("异常信息:{}", e);
result.setErrMessage("调用getMyPlanListCount方法异常,异常信息:" + e.getMessage());
}
return result;
}
④//查询出需要导出采购品数据
@Override
public ServiceResult> findByCondition(CorpDirectorysQueryDto corpDirectorysQueryDto, PageSet pageSet) {
ServiceResult> serviceResult = new ServiceResult<>();
if ( corpDirectorysQueryDto == null ){
log.error("corpDirectorysQueryDto is null");
serviceResult.setErrMessage("参数不能为空");
return serviceResult;
}
try {
//第几页
int pageNum = 0 ;
//每页显示记录数
int pageSize = Integer.MAX_VALUE;
//设值
if ( pageSet != null && pageSet.getPageNum() != null){
pageNum = pageSet.getPageNum();
}
if ( pageSet != null && pageSet.getPageSize() != null ){
pageSize = pageSet.getPageSize();
}
//分页
Page> queryResult = PageHelper.startPage( pageNum , pageSize );
if(pageSet != null){
if(StringUtils.hasLength(pageSet.getSortColumn())) {
PageHelper.orderBy(pageSet.getSortColumn());
}
}
//查询
serviceResult.setResult( getCorpDirectorysDao().findByCondition(corpDirectorysQueryDto) );
serviceResult.setTotal( queryResult.getTotal() );
}catch (Exception e){
log.error("调用{}方法 异常", "DubboCorpDirectorysServiceImpl#findByCompanyId",e);
log.error("方法使用参数:[companyId:{}]", corpDirectorysQueryDto);
serviceResult.setErrMessage("调用findByCompanyId方法异常,异常信息:" + e.getMessage());
}
return serviceResult;
}
##dubbo接口的dao层#
① //是否存在子分类
Long findPage_count(CorpCatalogs corpCatalogs);
②//如果有子目录,查询下当前子目录路径
CorpCatalogs getById(@Param("id") Long id, @Param("companyId") Long companyId);
③ //查询出需要导出采购品的数量
Integer getTotal(CorpDirectorysQueryDto condition);
④//查询出需要导出采购品数据
List findByCondition(CorpDirectorysQueryDto condition);
##dubbo接口的sql#
① //是否存在子分类
②//如果有子目录,查询下当前子目录路径
③同①sql一样,但条件不一样。
④//查询出需要导出采购品数据
##Entity类以及DTO封装类(采购品)#(get,set函数省略)
public class CorpDirectorys extends CorpDirectorysKey implements Serializable {
private static final long serialVersionUID = -659270055955419540L;
/**
* 名称
*/
@NotNull(message = "采购品名称不能为空!")
@Length(min = 1, max = 200, message = "采购品名称长度在 {min}与{max}之间 !")
private String name;
/**
* 规格/型号
*/
@Length(min = 0, max = 256, message = "规格型号 {min}与{max}之间 !")
private String spec;
/**
* 货号
*/
@Length(min = 0, max = 768, message = "货号长度在 {min}与{max}之间 !")
private String pcode;
/**
* 制造商
*/
@Length(min = 0, max = 768, message = "制造商长度在 {min}与{max}之间 !")
private String productor;
/**
* 计量单位
*/
@NotNull(message = "计量单位不能为空!")
@Length(min = 1, max = 10, message = "计量单位描述长度在 {min}与{max}之间 !")
private String unitName;
/**
* 产地
*/
@Length(min = 0, max = 768, message = "产地长度在 {min}与{max}之间 !")
private String producingAddress;
/**
* 用途
*/
@Length(min = 0, max = 768, message = "用途长度在 {min}与{max}之间 !")
private String purpose;
/**
* 市场参考价格
*/
@Length(min = 0, max = 768, message = "市场参考价长度在 {min}与{max}之间 !")
private String marketPrice;
/**
* 备注
*/
@Length(min = 0, max = 500, message = "备注长度在 {min}与{max}之间 !")
private String demo;
/**
* 状态:1:启用,2:禁用
* @see CorpDirectorysAbandonEnum
*/
@Range( min = 1 , max = 2 , message = "大小不合法")
private Long abandon;
/**
* 品牌
*/
@Length(min = 0, max = 768, message = "品牌长度在 {min}与{max}之间 !")
private String brand;
/**
* 产品特性
*/
@Length(min = 0, max = 768, message = "产品特性长度在 {min}与{max}之间 !")
private String speciality;
/**
* 技术参数/材质
*/
@Length(min = 0, max = 500, message = "技术参数长度在 {min}与{max}之间 !")
private String techParameters;
/**
* 所在分类ID
*/
private Long catalogId;
/**
* 来源
* @see cn.bidlink.procurement.materials.dal.server.enums.CorpDirectorysSourceEnum
*/
private Integer source;
/**
* 所在分类名称,包含直接和间接分类名称,以/分隔,如/采购目录/汽车
* 如果分类为空,则此字段为空
*/
private String catalogName;
/**
* 分类路径,包含直接和间接上级分类ID,以#分隔,如#1#563437187#
* 如果分类为空,则此字段为空
*/
private String treePath;
/**
* 编码,唯一
*/
private String code;
private Integer unitPrecision;
private Integer pricePrecision;
private String imgCode;
private Date createTime;
private Long createUserId;
private String createUserName;
private Date updateTime;
private Long updateUserId;
private String updateUserName;
public class CorpDirectorysQueryDto implements Serializable{
/** 企业ID,必填*/
private Long companyId;
/** 编码*/
private String code;
/** 编码(模糊匹配)*/
private String codeLike;
/** 名称 */
private String name;
/** 名称 (模糊匹配)*/
private String nameLike;
/** 规格/型号*/
private String spec;
/** 规格/型号(模糊匹配)*/
private String specLike;
/** 制造商*/
private String productor;
/** 制造商(模糊匹配)*/
private String productorLike;
/** 计量单位 */
private String unitName;
/** 计量单位 (模糊匹配)*/
private String unitNameLike;
/** 技术参数/材质*/
private String techParameters;
/** 技术参数/材质(模糊匹配)*/
private String techParametersLike;
/**启用禁用**/
private Long abandon;
/***采购品目录路径模糊匹配***/
private String treepathLike;
private Long orgId;
private String treePath;
private List catalogIdList;
public String getTreePath() {
return treePath;
}
public void setTreePath(String treePath) {
this.treePath = treePath;
}
public String getTreepathLike() {
return treepathLike;
}
public void setTreepathLike(String treepathLike) {
this.treepathLike = treepathLike;
}
public Long getAbandon() {
return abandon;
}
public void setAbandon(Long abandon) {
this.abandon = abandon;
}
/**
* 分类ID
*/
private Long catalogId;
public class CorpDirectorysKey implements Serializable {
/**
* 主键
*/
private Long id;
/**
* 所属企业ID
*/
@NotNull(message = "所属企业ID不能为空!")
private Long companyId;
private static final long serialVersionUID = 1L;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCompanyId() {
return companyId;
}
public void setCompanyId(Long companyId) {
this.companyId = companyId;
}