@@ -0,0 +1,22 @@ | |||
package com.ningdatech.pmapi.common.model; | |||
import lombok.Data; | |||
/** | |||
* <p> | |||
* FreemarkerBatchExportDTO | |||
* </p> | |||
* | |||
* @author WendyYang | |||
* @since 2023/8/1 | |||
**/ | |||
@Data | |||
public class FreemarkerBatchExportDTO { | |||
private String fileName; | |||
private String template; | |||
private Object data; | |||
} |
@@ -2,6 +2,7 @@ package com.ningdatech.pmapi.common.util; | |||
import cn.hutool.core.io.IoUtil; | |||
import com.ningdatech.basic.exception.BizException; | |||
import com.ningdatech.pmapi.common.model.FreemarkerBatchExportDTO; | |||
import com.ningdatech.pmapi.meeting.entity.dto.ExpertFeeExportDTO; | |||
import com.ningdatech.pmapi.meeting.entity.dto.ExpertInfoDTO; | |||
import freemarker.template.Configuration; | |||
@@ -19,6 +20,9 @@ import java.net.URLEncoder; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipOutputStream; | |||
/** | |||
* <p> | |||
@@ -31,6 +35,16 @@ import java.util.ArrayList; | |||
@Slf4j | |||
public class FreemarkerWordUtil { | |||
private static void setDownFileName(HttpServletResponse response, String fileName) { | |||
String fileNameEncoded; | |||
try { | |||
fileNameEncoded = URLEncoder.encode(fileName, "UTF-8"); | |||
} catch (UnsupportedEncodingException e) { | |||
throw new RuntimeException("文件名编码异常"); | |||
} | |||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileNameEncoded); | |||
} | |||
private static final Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_31); | |||
static { | |||
@@ -69,21 +83,46 @@ public class FreemarkerWordUtil { | |||
public static void export(String fileName, Object data, HttpServletResponse response, String templateName) throws TemplateException, IOException { | |||
Template template = CONFIGURATION.getTemplate(templateName); | |||
StringWriter out = new StringWriter(); | |||
Writer writer = new BufferedWriter(out, 4096); | |||
template.process(data, writer); | |||
response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); | |||
response.setContentType(ContentType.APPLICATION_JSON.getMimeType()); | |||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8")); | |||
try (InputStream is = new ByteArrayResource(out.toString().getBytes(StandardCharsets.UTF_8)).getInputStream(); | |||
ServletOutputStream outputStream = response.getOutputStream()) { | |||
// 缓冲区 | |||
byte[] buffer = new byte[1024]; | |||
int bytesToRead; | |||
// 通过循环将读入的Word文件的内容输出到浏览器中 | |||
while ((bytesToRead = is.read(buffer)) != -1) { | |||
outputStream.write(buffer, 0, bytesToRead); | |||
setDownFileName(response, fileName); | |||
try (StringWriter out = new StringWriter(); | |||
Writer writer = new BufferedWriter(out, 4096)) { | |||
template.process(data, writer); | |||
try (InputStream is = new ByteArrayResource(out.toString().getBytes(StandardCharsets.UTF_8)).getInputStream(); | |||
ServletOutputStream outputStream = response.getOutputStream()) { | |||
// 缓冲区 | |||
byte[] buffer = new byte[1024]; | |||
int bytesToRead; | |||
// 通过循环将读入的Word文件的内容输出到浏览器中 | |||
while ((bytesToRead = is.read(buffer)) != -1) { | |||
outputStream.write(buffer, 0, bytesToRead); | |||
} | |||
} | |||
} | |||
} | |||
public static void exportBatch(HttpServletResponse response, List<FreemarkerBatchExportDTO> fbeList, String fileName) throws IOException, TemplateException { | |||
response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); | |||
response.setContentType("application/zip"); | |||
setDownFileName(response, fileName); | |||
try (ServletOutputStream os = response.getOutputStream(); | |||
ZipOutputStream zos = new ZipOutputStream(os)) { | |||
for (FreemarkerBatchExportDTO param : fbeList) { | |||
Template template = CONFIGURATION.getTemplate(param.getTemplate()); | |||
StringWriter out = new StringWriter(); | |||
try (Writer writer = new BufferedWriter(out, 4096)) { | |||
template.process(param.getData(), writer); | |||
} | |||
ZipEntry zipEntry = new ZipEntry(param.getFileName()); | |||
zos.putNextEntry(zipEntry); | |||
zos.write(out.toString().getBytes(StandardCharsets.UTF_8)); | |||
zos.closeEntry(); | |||
} | |||
} finally { | |||
response.flushBuffer(); | |||
} | |||
} | |||
@@ -1,5 +1,6 @@ | |||
package com.ningdatech.pmapi.expert.manage; | |||
import cn.hutool.core.util.StrUtil; | |||
import cn.hutool.json.JSONUtil; | |||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
@@ -8,7 +9,9 @@ import com.ningdatech.basic.util.CollUtils; | |||
import com.ningdatech.cache.lock.DistributedLock; | |||
import com.ningdatech.file.entity.vo.result.AttachFileVo; | |||
import com.ningdatech.file.service.FileService; | |||
import com.ningdatech.pmapi.expert.constant.ReviewResultEnum; | |||
import com.ningdatech.pmapi.expert.model.dto.ReviewTemplateOptionDTO; | |||
import com.ningdatech.pmapi.expert.model.dto.ReviewTemplateSettingsDTO; | |||
import com.ningdatech.pmapi.expert.model.entity.ExpertReview; | |||
import com.ningdatech.pmapi.expert.model.entity.ReviewTemplateSettings; | |||
import com.ningdatech.pmapi.expert.model.req.ExpertReviewDetailReq; | |||
@@ -199,4 +202,42 @@ public class ExpertReviewManage { | |||
return detail; | |||
} | |||
public List<ExpertReview> listFinalExpertReviews(Long meetingId) { | |||
LambdaQueryWrapper<ExpertReview> query = Wrappers.lambdaQuery(ExpertReview.class) | |||
.eq(ExpertReview::getIsFinal, Boolean.TRUE) | |||
.eq(ExpertReview::getMeetingId, meetingId); | |||
return expertReviewService.list(query); | |||
} | |||
public Map<Long, String> buildExpertReviewToStr(Long meetingId) { | |||
List<ExpertReview> reviews = listFinalExpertReviews(meetingId); | |||
if (reviews.isEmpty()) { | |||
return Collections.emptyMap(); | |||
} | |||
List<Long> templateIds = CollUtils.fieldList(reviews, ExpertReview::getTemplateId); | |||
List<ReviewTemplateSettings> ts = templateSettingsService.listByIds(templateIds); | |||
Map<Long, ReviewTemplateSettings> tsMap = CollUtils.listToMap(ts, ReviewTemplateSettings::getId); | |||
Map<Long, String> resMap = new HashMap<>(8); | |||
for (ExpertReview review : reviews) { | |||
ReviewTemplateSettings settings = tsMap.get(review.getTemplateId()); | |||
List<ReviewTemplateSettingsDTO> optionTemplates = JSONUtil.toList(settings.getContent(), ReviewTemplateSettingsDTO.class); | |||
List<ReviewTemplateOptionDTO> options = JSONUtil.toList(review.getContent(), ReviewTemplateOptionDTO.class); | |||
Map<Integer, ReviewTemplateOptionDTO> optionsMap = CollUtils.listToMap(options, ReviewTemplateOptionDTO::getQuestionSerialNo); | |||
StringBuilder str = new StringBuilder(); | |||
optionTemplates.forEach(ot -> { | |||
ReviewTemplateOptionDTO rto = optionsMap.get(ot.getSerialNo()); | |||
String optionsContent = ot.getOptions().stream() | |||
.filter(w -> rto.getOptionSerialNo().contains(w.getSerialNo())) | |||
.map(ReviewTemplateSettingsDTO.OptionDTO::getOption) | |||
.collect(Collectors.joining("、")); | |||
str.append("<w:br/>").append(ot.getTitle()).append(":").append(optionsContent).append(";"); | |||
}); | |||
str.append("<w:br/>").append("其他意见或建议").append(":") | |||
.append(StrUtil.blankToDefault(review.getOtherAdvice(), "--")).append(";"); | |||
str.append("<w:br/>").append("评审结果").append(":") | |||
.append(ReviewResultEnum.getByCode(review.getReviewResult()).getValue()).append(";"); | |||
resMap.put(review.getProjectId(), str.toString()); | |||
} | |||
return resMap; | |||
} | |||
} |
@@ -48,7 +48,7 @@ public class MeetingExportController { | |||
} | |||
@GetMapping("/expertInviteTable/{meetingId}") | |||
@ApiOperation("专家评审单导出") | |||
@ApiOperation("专家抽取表导出") | |||
public void exportExpertInviteTable(@PathVariable Long meetingId, HttpServletResponse response) { | |||
expertExportManage.expertInviteTable(meetingId, response); | |||
} | |||
@@ -2,6 +2,8 @@ package com.ningdatech.pmapi.meeting.entity.dto; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
/** | |||
* <p> | |||
* ExpertReviewTableDTO | |||
@@ -11,7 +13,9 @@ import lombok.Data; | |||
* @since 2023/7/24 | |||
**/ | |||
@Data | |||
public class ExpertReviewTableDTO { | |||
public class ExpertReviewTableDTO implements Serializable { | |||
private static final long serialVersionUID = 3383159744182234397L; | |||
private String projectName; | |||
@@ -21,4 +25,6 @@ public class ExpertReviewTableDTO { | |||
private String holdOrg; | |||
private String reviewResult; | |||
} |
@@ -3,16 +3,20 @@ package com.ningdatech.pmapi.meeting.manage; | |||
import cn.hutool.core.lang.Assert; | |||
import cn.hutool.core.lang.UUID; | |||
import cn.hutool.core.util.StrUtil; | |||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||
import com.ningdatech.basic.exception.BizException; | |||
import com.ningdatech.basic.util.CollUtils; | |||
import com.ningdatech.pmapi.common.model.FreemarkerBatchExportDTO; | |||
import com.ningdatech.pmapi.common.util.FreemarkerWordUtil; | |||
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; | |||
import com.ningdatech.pmapi.expert.manage.ExpertReviewManage; | |||
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService; | |||
import com.ningdatech.pmapi.meeting.entity.domain.Meeting; | |||
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; | |||
import com.ningdatech.pmapi.meeting.entity.domain.MeetingInnerProject; | |||
import com.ningdatech.pmapi.meeting.entity.domain.MeetingOuterProject; | |||
import com.ningdatech.pmapi.meeting.entity.dto.*; | |||
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum; | |||
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; | |||
import com.ningdatech.pmapi.meeting.service.IMeetingInnerProjectService; | |||
import com.ningdatech.pmapi.meeting.service.IMeetingOuterProjectService; | |||
@@ -29,11 +33,13 @@ import javax.servlet.http.HttpServletResponse; | |||
import java.io.IOException; | |||
import java.math.BigDecimal; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
import java.util.function.Function; | |||
import java.util.function.Supplier; | |||
import java.util.stream.Collectors; | |||
/** | |||
* <p> | |||
@@ -54,6 +60,7 @@ public class ExpertExportManage { | |||
private final IMeetingOuterProjectService meetingOuterProjectService; | |||
private final IExpertUserFullInfoService expertUserInfoService; | |||
private final IProjectService projectService; | |||
private final ExpertReviewManage expertReviewManage; | |||
//================================================================================================================== | |||
@@ -78,6 +85,16 @@ public class ExpertExportManage { | |||
static Supplier<String> wordName = () -> UUID.randomUUID().toString(true) + ".doc"; | |||
private List<MeetingExpert> listExpertsByAgreeOrLeave(Long meetingId) { | |||
Page<MeetingExpert> page = Page.of(1, 20); | |||
meetingExpertService.pageExpertByStatusAndMeetingId(page, meetingId, null, null); | |||
if (page.getTotal() == 0) { | |||
return Collections.emptyList(); | |||
} | |||
page.getRecords().removeIf(w -> !(ExpertAttendStatusEnum.AGREED.eq(w.getStatus()) || ExpertAttendStatusEnum.ON_LEAVE.eq(w.getStatus()))); | |||
return page.getRecords(); | |||
} | |||
//================================================================================================================== | |||
public void exportFeeForExpert(Long meetingId, HttpServletResponse response) { | |||
@@ -130,22 +147,43 @@ public class ExpertExportManage { | |||
public void expertReviewTable(Long meetingId, HttpServletResponse response) { | |||
try { | |||
Meeting meeting = meetingService.getById(meetingId); | |||
ExpertReviewTableDTO data = new ExpertReviewTableDTO(); | |||
data.setMeetingTime(DateUtil.localDateTimeFormat(meeting.getStartTime(), "yyyy年M月d日")); | |||
data.setMeetingAddress(meeting.getMeetingAddress()); | |||
data.setHoldOrg(meeting.getHoldOrg()); | |||
List<String> projectNames = new ArrayList<>(); | |||
String startTime = DateUtil.localDateTimeFormat(meeting.getStartTime(), "yyyy年M月d日"); | |||
List<FreemarkerBatchExportDTO> fbeList; | |||
if (meeting.getIsInnerProject()) { | |||
List<MeetingInnerProject> inners = meetingInnerProjectService.listByMeetingId(meetingId); | |||
Map<Long, String> reviewResultMap = expertReviewManage.buildExpertReviewToStr(meetingId); | |||
List<Long> projectIds = CollUtils.fieldList(inners, MeetingInnerProject::getProjectId); | |||
List<Project> projects = projectService.listByIds(projectIds); | |||
projects.forEach(w -> projectNames.add(w.getProjectName())); | |||
fbeList = projects.stream().map(w -> { | |||
FreemarkerBatchExportDTO fbe = new FreemarkerBatchExportDTO(); | |||
ExpertReviewTableDTO currData = new ExpertReviewTableDTO(); | |||
currData.setHoldOrg(meeting.getHoldOrg()); | |||
currData.setMeetingTime(startTime); | |||
currData.setMeetingAddress(meeting.getMeetingAddress()); | |||
currData.setProjectName(w.getProjectName()); | |||
currData.setReviewResult(reviewResultMap.get(w.getId())); | |||
fbe.setData(currData); | |||
fbe.setTemplate(EXPERT_REVIEW); | |||
fbe.setFileName(w.getProjectName() + ".doc"); | |||
return fbe; | |||
}).collect(Collectors.toList()); | |||
} else { | |||
List<MeetingOuterProject> inners = meetingOuterProjectService.listByMeetingId(meetingId); | |||
inners.forEach(w -> projectNames.add(w.getProjectName())); | |||
fbeList = inners.stream().map(w -> { | |||
FreemarkerBatchExportDTO fbe = new FreemarkerBatchExportDTO(); | |||
ExpertReviewTableDTO currData = new ExpertReviewTableDTO(); | |||
currData.setHoldOrg(meeting.getHoldOrg()); | |||
currData.setMeetingTime(startTime); | |||
currData.setMeetingAddress(meeting.getMeetingAddress()); | |||
currData.setProjectName(w.getProjectName()); | |||
fbe.setData(currData); | |||
fbe.setTemplate(EXPERT_REVIEW); | |||
fbe.setFileName(w.getProjectName() + ".doc"); | |||
return fbe; | |||
}).collect(Collectors.toList()); | |||
} | |||
data.setProjectName(StrUtil.join("、", projectNames)); | |||
FreemarkerWordUtil.export(wordName.get(), data, response, EXPERT_REVIEW); | |||
String exportFileName = meeting.getName() + "专家评审表.zip"; | |||
FreemarkerWordUtil.exportBatch(response, fbeList, exportFileName); | |||
} catch (TemplateException | IOException e) { | |||
log.error("专家评审单导出异常:{}", meetingId, e); | |||
throw BizException.wrap("专家评审单导出失败"); | |||
@@ -171,13 +209,17 @@ public class ExpertExportManage { | |||
data.setProjectName(StrUtil.join("、", projectNames)); | |||
data.setExperts(new ArrayList<>()); | |||
// 设置专家信息 | |||
List<MeetingExpert> experts = meetingExpertService.listAgreedExperts(meetingId); | |||
List<MeetingExpert> experts = listExpertsByAgreeOrLeave(meetingId); | |||
List<Long> expertIds = CollUtils.fieldList(experts, MeetingExpert::getExpertId); | |||
List<ExpertUserFullInfo> expertInfos = expertUserInfoService.listByUserId(expertIds); | |||
Map<Long, ExpertUserFullInfo> expertMap = CollUtils.listToMap(expertInfos, ExpertUserFullInfo::getUserId); | |||
experts.forEach(w -> { | |||
ExpertInfoDTO expert = new ExpertInfoDTO(); | |||
expert.setName(w.getExpertName()); | |||
if (ExpertAttendStatusEnum.ON_LEAVE.eq(w.getStatus())) { | |||
expert.setBank(w.getExpertName() + "(请假)"); | |||
} else { | |||
expert.setName(w.getExpertName()); | |||
} | |||
ExpertUserFullInfo expertUser = expertMap.get(w.getExpertId()); | |||
if (expertUser != null) { | |||
expert.setCompany(expertUser.getCompany()); | |||
@@ -217,7 +259,7 @@ public class ExpertExportManage { | |||
} | |||
data.setExperts(new ArrayList<>()); | |||
// 设置专家信息 | |||
List<MeetingExpert> experts = meetingExpertService.listAgreedExperts(meetingId); | |||
List<MeetingExpert> experts = listExpertsByAgreeOrLeave(meetingId); | |||
List<Long> expertIds = CollUtils.fieldList(experts, MeetingExpert::getExpertId); | |||
List<ExpertUserFullInfo> expertInfos = expertUserInfoService.listByUserId(expertIds); | |||
Map<Long, ExpertUserFullInfo> expertMap = CollUtils.listToMap(expertInfos, ExpertUserFullInfo::getUserId); | |||
@@ -225,7 +267,11 @@ public class ExpertExportManage { | |||
experts.forEach(w -> { | |||
ExpertInfoDTO expert = new ExpertInfoDTO(); | |||
expert.setNo(integer.incrementAndGet()); | |||
expert.setName(w.getExpertName()); | |||
if (ExpertAttendStatusEnum.ON_LEAVE.eq(w.getStatus())) { | |||
expert.setBank(w.getExpertName() + "(请假)"); | |||
} else { | |||
expert.setName(w.getExpertName()); | |||
} | |||
expert.setMobile(w.getMobile()); | |||
ExpertUserFullInfo expertUser = expertMap.get(w.getExpertId()); | |||
if (expertUser != null) { | |||
@@ -553,21 +553,36 @@ | |||
<w:sz w:val="30"/> | |||
<w:sz-cs w:val="30"/> | |||
</w:rPr> | |||
<w:t>${meetingTime},${holdOrg}组织专家在${meetingAddress}对${projectName}进行评审。专家组认真听取了项目业主方、建设方的情况汇报,审阅了项目材料,实地查看了设备及平台演示,专家组织质询、讨论后提出以下意见: | |||
<w:t>${meetingTime},${holdOrg}组织专家在${meetingAddress}对${projectName} | |||
进行评审。专家组认真听取了项目业主方、建设方的情况汇报,审阅了项目材料,实地查看了设备及平台演示,专家组织质询、讨论后提出以下意见: | |||
</w:t> | |||
</w:r> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="244" w:line-rule="auto"/> | |||
<w:spacing w:line="257" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="244" w:line-rule="auto"/> | |||
<w:ind w:first-line="500" w:first-line-chars="0"/> | |||
<w:rPr> | |||
<w:rFonts w:ascii="微软雅黑" w:h-ansi="微软雅黑" w:fareast="微软雅黑" w:cs="微软雅黑" | |||
w:hint="fareast"/> | |||
<w:sz w:val="30"/> | |||
<w:sz-cs w:val="30"/> | |||
</w:rPr> | |||
</w:pPr> | |||
<w:r> | |||
<w:rPr> | |||
<w:rFonts w:ascii="微软雅黑" w:h-ansi="微软雅黑" w:fareast="微软雅黑" w:cs="微软雅黑" | |||
w:hint="fareast"/> | |||
<w:sz w:val="30"/> | |||
<w:sz-cs w:val="30"/> | |||
</w:rPr> | |||
<w:t>${(reviewResult)!}</w:t> | |||
</w:r> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
@@ -644,73 +659,13 @@ | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
<w:spacing w:line="244" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||
<w:pPr> | |||
<w:pStyle w:val="a2"/> | |||
<w:spacing w:line="245" w:line-rule="auto"/> | |||
<w:spacing w:line="244" w:line-rule="auto"/> | |||
</w:pPr> | |||
</w:p> | |||
<w:p> | |||