|
|
@ -1,24 +1,28 @@ |
|
|
|
package com.epmet.service.impl; |
|
|
|
|
|
|
|
import com.alibaba.fastjson.JSON; |
|
|
|
import com.alibaba.fastjson.JSONArray; |
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|
|
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker; |
|
|
|
import com.epmet.commons.feignclient.dtos.JiMuPage; |
|
|
|
import com.epmet.commons.feignclient.dtos.JiMuResult; |
|
|
|
import com.epmet.commons.feignclient.dtos.form.JiMuReportFormDTO; |
|
|
|
import com.epmet.commons.feignclient.dtos.form.JimuReportExportRequestDTO; |
|
|
|
import com.epmet.commons.feignclient.dtos.result.JiMuReportDetailResultDTO; |
|
|
|
import com.epmet.commons.feignclient.dtos.result.JiMuReportResultDTO; |
|
|
|
import com.epmet.commons.feignclient.dtos.result.JimuReportDbDataResultDTO; |
|
|
|
import com.epmet.commons.feignclient.dtos.result.JimuReportFieldTreeResultDTO; |
|
|
|
import com.epmet.commons.feignclient.feigns.JiMuReportOpenFeignClient; |
|
|
|
import com.epmet.commons.mybatis.service.impl.BaseServiceImpl; |
|
|
|
import com.epmet.commons.tools.constant.FieldConstant; |
|
|
|
import com.epmet.commons.tools.constant.NumConstant; |
|
|
|
import com.epmet.commons.tools.constant.StrConstant; |
|
|
|
import com.epmet.commons.tools.constant.*; |
|
|
|
import com.epmet.commons.tools.dto.form.DictListFormDTO; |
|
|
|
import com.epmet.commons.tools.dto.result.DictListResultDTO; |
|
|
|
import com.epmet.commons.tools.exception.EpmetErrorCode; |
|
|
|
import com.epmet.commons.tools.exception.EpmetException; |
|
|
|
import com.epmet.commons.tools.exception.ExceptionUtils; |
|
|
|
import com.epmet.commons.tools.redis.RedisUtils; |
|
|
|
import com.epmet.commons.tools.utils.ConvertUtils; |
|
|
|
import com.epmet.commons.tools.utils.EpmetRequestHolder; |
|
|
|
import com.epmet.commons.tools.utils.Result; |
|
|
|
import com.epmet.commons.tools.utils.*; |
|
|
|
import com.epmet.constant.CustomerFunctionConstant; |
|
|
|
import com.epmet.dao.IcCustomerReportDao; |
|
|
|
import com.epmet.dto.CustomerDTO; |
|
|
@ -33,8 +37,10 @@ import com.epmet.feign.EpmetAdminOpenFeignClient; |
|
|
|
import com.epmet.feign.OperCrmOpenFeignClient; |
|
|
|
import com.epmet.service.IcCustomerReportService; |
|
|
|
import com.epmet.service.IcReportFunService; |
|
|
|
import feign.Response; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.apache.commons.collections4.CollectionUtils; |
|
|
|
import org.apache.commons.io.IOUtils; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.http.HttpHeaders; |
|
|
@ -42,11 +48,20 @@ import org.springframework.stereotype.Service; |
|
|
|
import org.springframework.transaction.annotation.Transactional; |
|
|
|
import org.springframework.util.MultiValueMap; |
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import javax.servlet.ServletOutputStream; |
|
|
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
import java.io.*; |
|
|
|
import java.net.URLEncoder; |
|
|
|
import java.nio.file.Files; |
|
|
|
import java.nio.file.Path; |
|
|
|
import java.time.LocalDateTime; |
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
import java.util.*; |
|
|
|
import java.util.regex.Matcher; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.zip.ZipEntry; |
|
|
|
import java.util.zip.ZipOutputStream; |
|
|
|
|
|
|
|
/** |
|
|
|
* 客户报表关系表 |
|
|
@ -81,6 +96,7 @@ public class IcCustomerReportServiceImpl extends BaseServiceImpl<IcCustomerRepor |
|
|
|
|
|
|
|
/** |
|
|
|
* Desc: 报表集合 |
|
|
|
* |
|
|
|
* @param |
|
|
|
* @author zxc |
|
|
|
* @date 2022/8/8 10:38 |
|
|
@ -152,6 +168,7 @@ public class IcCustomerReportServiceImpl extends BaseServiceImpl<IcCustomerRepor |
|
|
|
|
|
|
|
/** |
|
|
|
* Desc: 报表编辑 |
|
|
|
* |
|
|
|
* @param formDTO |
|
|
|
* @author zxc |
|
|
|
* @date 2022/8/8 14:09 |
|
|
@ -196,6 +213,7 @@ public class IcCustomerReportServiceImpl extends BaseServiceImpl<IcCustomerRepor |
|
|
|
|
|
|
|
/** |
|
|
|
* Desc: 居民信息/房屋信息-查询报表 |
|
|
|
* |
|
|
|
* @param formDTO |
|
|
|
* @author zxc |
|
|
|
* @date 2022/8/8 15:20 |
|
|
@ -238,4 +256,226 @@ public class IcCustomerReportServiceImpl extends BaseServiceImpl<IcCustomerRepor |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void batchExport(String reportId, String token, String paramKey, HttpServletResponse response) { |
|
|
|
|
|
|
|
// 1. 首先去积木服务,查询出报表数据源的详细信息,包括报表url,isList;获取到哪一列是id(idFieldName)
|
|
|
|
|
|
|
|
// 根据报表id查询数据源列表,取出第一个
|
|
|
|
JiMuResult<List<List<JimuReportFieldTreeResultDTO>>> fResult = jiMuReportOpenFeignClient.fieldTree(reportId); |
|
|
|
String datasourceId = fResult.getResult().get(0).get(0).getDbId(); |
|
|
|
|
|
|
|
// 根据数据源ID查询数据源信息
|
|
|
|
JiMuResult<JimuReportDbDataResultDTO> dbData = jiMuReportOpenFeignClient.loadDbData(datasourceId); |
|
|
|
JimuReportDbDataResultDTO.ReportDB reportDb = dbData.getResult().getReportDb(); |
|
|
|
|
|
|
|
// api的url
|
|
|
|
String apiUrl = reportDb.getApiUrl(); |
|
|
|
// api方法
|
|
|
|
//String apiMethod = reportDb.getApiMethod();
|
|
|
|
// 是否是集合
|
|
|
|
//String isList = reportDb.getIsList();
|
|
|
|
// 返回的列表中,哪个字段是ID
|
|
|
|
String idFieldName = null; |
|
|
|
boolean isHttps = false; |
|
|
|
|
|
|
|
Matcher matcher = Pattern.compile("(http://|https://).+idFieldName=(\\w+).*").matcher(apiUrl); |
|
|
|
if (matcher.matches()) { |
|
|
|
String proto = matcher.group(1); |
|
|
|
if ("https://".equals(proto)) { |
|
|
|
isHttps = true; |
|
|
|
} |
|
|
|
idFieldName = matcher.group(2); |
|
|
|
} |
|
|
|
|
|
|
|
if (StringUtils.isBlank(idFieldName)) { |
|
|
|
throw new EpmetException(EpmetErrorCode.SERVER_ERROR.getCode(), "配置的业务api url缺少idFieldName列"); |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 调用该url的接口,获取到一个列表,根据idFieldName取出ID列
|
|
|
|
List<String> ids = listIds(apiUrl, isHttps, idFieldName); |
|
|
|
|
|
|
|
// 3. 然后以这一列作为查询条件,循环,继续调用该接口,得到单条数据,每一条数据都下载一个excel,最后将其打包为一个压缩包下载
|
|
|
|
Path storePath = makeTemporaryDownloadDir(reportId); |
|
|
|
|
|
|
|
// 4.生成压缩文件
|
|
|
|
Path zipFile = downloadAndComppress(storePath, reportId, paramKey, ids); |
|
|
|
|
|
|
|
// 5.下载
|
|
|
|
try (FileInputStream fis = new FileInputStream(zipFile.toFile())) { |
|
|
|
ServletOutputStream os = response.getOutputStream(); |
|
|
|
response.setCharacterEncoding("UTF-8"); |
|
|
|
response.setHeader("content-Type", "application/zip"); |
|
|
|
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFile.getFileName().toString(), "UTF-8")); |
|
|
|
|
|
|
|
IOUtils.copy(fis, os); |
|
|
|
} catch (Exception e) { |
|
|
|
e.printStackTrace(); |
|
|
|
} finally { |
|
|
|
try { |
|
|
|
Files.delete(zipFile); |
|
|
|
} catch (IOException e) { |
|
|
|
log.error("【报表批量导出】删除临时zip文件失败"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 批量下载 |
|
|
|
* @param storePath 本次导出文件的存储路径,子目录包括:zip文件,时间戳命名的子目录用于存放临时excel(导出完成会删掉) |
|
|
|
* @param ids |
|
|
|
* @return 压缩文件路径 |
|
|
|
*/ |
|
|
|
private Path downloadAndComppress(Path storePath, String reportId, String paramKey, List<String> ids) { |
|
|
|
// 请求头
|
|
|
|
Map<String, Object> headers = new HashMap<>(); |
|
|
|
headers.put(Constant.AUTHORIZATION_HEADER, EpmetRequestHolder.getHeader(Constant.AUTHORIZATION_HEADER)); |
|
|
|
|
|
|
|
// 请求体
|
|
|
|
JimuReportExportRequestDTO param = new JimuReportExportRequestDTO(); |
|
|
|
param.setExcelConfigId(reportId); |
|
|
|
param.getQueryParam().setCurrentPageNo("1"); |
|
|
|
param.getQueryParam().setCurrentPageSize(20); |
|
|
|
param.getQueryParam().setPageNo("1"); |
|
|
|
param.getQueryParam().setPageSize(20); |
|
|
|
param.getQueryParam().setParamKey(paramKey); |
|
|
|
param.getQueryParam().setToken(EpmetRequestHolder.getLoginUserAuthorizationToken()); |
|
|
|
|
|
|
|
final ArrayList<File> files = new ArrayList<>(); |
|
|
|
|
|
|
|
final String currentTimeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); |
|
|
|
|
|
|
|
// 创建存放xlsx的临时目录
|
|
|
|
Path xlsxStorePath = storePath.resolve(currentTimeStr); |
|
|
|
try { |
|
|
|
Files.createDirectories(xlsxStorePath); |
|
|
|
} catch (IOException e) { |
|
|
|
throw new EpmetException("【报表批量导出】生成临时目录失败:" + xlsxStorePath.toString()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 1. 循环下载所有id对应的excel
|
|
|
|
for (String id : ids) { |
|
|
|
param.getQueryParam().setId(id); |
|
|
|
final Response response = jiMuReportOpenFeignClient.exportAllExcelStream(param); |
|
|
|
// 取出文件后缀
|
|
|
|
final LinkedList<String> header = (LinkedList) response.headers().get("content-disposition"); |
|
|
|
final Matcher matcher = Pattern.compile("attachment;filename=(.+)(.xls|.xlsx)").matcher(header.get(0)); |
|
|
|
if (!matcher.matches()) { |
|
|
|
log.error("【报表批量导出】批量导出失败,后缀不匹配。文件名:{}, 报表ID:{}, 业务数据ID:{}", header.get(0), reportId, id); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
final File excelFile = xlsxStorePath.resolve("file_" + id + matcher.group(2)).toFile(); |
|
|
|
|
|
|
|
files.add(excelFile); |
|
|
|
try (FileOutputStream fos = new FileOutputStream(excelFile)) { |
|
|
|
IOUtils.copy(response.body().asInputStream(), fos); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("【报表批量导出】生成临时表格文件失败,文件名:{},报表ID:{}, 业务数据ID:{},错误信息:{}", header.get(0), reportId, id, ExceptionUtils.getErrorStackTrace(e)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 2,打包
|
|
|
|
Path zipFile = storePath.resolve("report_export_" + currentTimeStr + ".zip"); |
|
|
|
try (final FileOutputStream fos = new FileOutputStream(zipFile.toFile()); |
|
|
|
final ZipOutputStream zos = new ZipOutputStream(fos)) { |
|
|
|
|
|
|
|
// 循环每一个文件
|
|
|
|
for (File file : files) { |
|
|
|
try (final FileInputStream fis = new FileInputStream(file.getAbsolutePath())) { |
|
|
|
zos.putNextEntry(new ZipEntry("report_export_" + currentTimeStr + "/" + file.getName())); |
|
|
|
final byte[] buffer = new byte[10240]; |
|
|
|
for (int len; (len = fis.read(buffer)) > 0; ) { |
|
|
|
zos.write(buffer, 0, len); |
|
|
|
} |
|
|
|
} finally { |
|
|
|
zos.closeEntry(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
zos.flush(); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("【报表批量导出】压缩文件失败,错误信息:{}", ExceptionUtils.getErrorStackTrace(e)); |
|
|
|
throw new EpmetException(EpmetErrorCode.EPMET_COMMON_OPERATION_FAIL.getCode(), "压缩失败", "压缩失败"); |
|
|
|
} finally { |
|
|
|
// 删除临时文件
|
|
|
|
cleanTemporaryFiles(storePath.resolve(currentTimeStr)); |
|
|
|
} |
|
|
|
|
|
|
|
return zipFile; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 清理临时保存文件 |
|
|
|
*/ |
|
|
|
private void cleanTemporaryFiles(Path tempStorePath) { |
|
|
|
// 删除内部文件
|
|
|
|
for (File file : tempStorePath.toFile().listFiles()) { |
|
|
|
try { |
|
|
|
Files.delete(file.toPath()); |
|
|
|
} catch (IOException e) { |
|
|
|
log.error("【报表批量导出】删除临时文件失败:{}", ExceptionUtils.getErrorStackTrace(e)); |
|
|
|
throw new EpmetException(EpmetErrorCode.SERVER_ERROR.getCode(), null, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 删除文件夹
|
|
|
|
try { |
|
|
|
Files.delete(tempStorePath); |
|
|
|
} catch (IOException e) { |
|
|
|
log.error("【报表批量导出】删除临时目录失败:{}", ExceptionUtils.getErrorStackTrace(e)); |
|
|
|
throw new EpmetException(EpmetErrorCode.SERVER_ERROR.getCode(), null, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 创建临时目录 |
|
|
|
* |
|
|
|
* @param reportId |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
private Path makeTemporaryDownloadDir(String reportId) { |
|
|
|
|
|
|
|
String userId = EpmetRequestHolder.getLoginUserId(); |
|
|
|
try { |
|
|
|
return FileUtils.getAndCreateDirUnderEpmetFilesDir("report/batch_export/", reportId, userId); |
|
|
|
} catch (IOException e) { |
|
|
|
throw new EpmetException("报表】批量导出-创建临时目录失败,错误信息:" + ExceptionUtils.getErrorStackTrace(e)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 列出id列表,批量下载用 |
|
|
|
* |
|
|
|
* @param apiUrl |
|
|
|
* @param isHttps |
|
|
|
* @param idFieldName |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
public List<String> listIds(String apiUrl, boolean isHttps, String idFieldName) { |
|
|
|
apiUrl = apiUrl.replace("${id}", ""); |
|
|
|
|
|
|
|
Map<String, Object> headers = new HashMap<>(); |
|
|
|
headers.put(Constant.AUTHORIZATION_HEADER, EpmetRequestHolder.getHeader(Constant.AUTHORIZATION_HEADER)); |
|
|
|
Result<String> stringResult = HttpClientManager.getInstance().sendPost(apiUrl, isHttps, "{}", headers); |
|
|
|
|
|
|
|
JSONObject dataJsonObject = JSON.parseObject(stringResult.getData()); |
|
|
|
Object data = dataJsonObject.get("data"); |
|
|
|
|
|
|
|
JSONArray array = new JSONArray(); |
|
|
|
ArrayList<String> ids = new ArrayList<>(); |
|
|
|
|
|
|
|
// 这种可能是pageData的
|
|
|
|
if (data instanceof JSONObject) { |
|
|
|
JSONObject jo = (JSONObject) data; |
|
|
|
array = jo.getJSONArray("list"); |
|
|
|
} |
|
|
|
|
|
|
|
for (ListIterator<Object> it = array.listIterator(); it.hasNext(); ) { |
|
|
|
JSONObject e = (JSONObject) it.next(); |
|
|
|
ids.add(e.getString(idFieldName)); |
|
|
|
} |
|
|
|
return ids; |
|
|
|
} |
|
|
|
} |