Merge branch 'feature/生成签名pdf' into release/2.9.6.2310.01

# Conflicts:
#	src/com/engine/salary/web/SalaryBillController.java
This commit is contained in:
钱涛 2023-10-07 15:19:38 +08:00
commit d98933832a
9 changed files with 410 additions and 95 deletions

View File

@ -0,0 +1,4 @@
genPdf=1
hasSign=1
toPdfToolPath=H:\\tools\\wkhtmltox\\bin\\wkhtmltopdf.exe
genPath=D:\\

View File

@ -0,0 +1,15 @@
package com.engine.salary.constant;
import weaver.general.BaseBean;
public class HrmSalaryPayrollConf {
public static final BaseBean baseBean = new BaseBean();
public static final boolean GEN_PDF = "1".equals(baseBean.getPropValue("hrmSalaryPayroll", "genPdf"));
public static final boolean HAS_SIGN = "1".equals(baseBean.getPropValue("hrmSalaryPayroll", "hasSign"));
public static final String TO_PDF_TOOL_PATH = baseBean.getPropValue("hrmSalaryPayroll", "toPdfToolPath");
public static final String GEN_PATH = baseBean.getPropValue("hrmSalaryPayroll", "genPath");
}

View File

@ -1,9 +1,11 @@
package com.engine.salary.entity.salaryBill.bo;
import cn.hutool.core.io.FileUtil;
import com.cloudstore.dev.api.bean.MessageBean;
import com.cloudstore.dev.api.bean.MessageType;
import com.cloudstore.dev.api.util.Util_Message;
import com.engine.salary.annotation.SalaryFormulaVar;
import com.engine.salary.constant.HrmSalaryPayrollConf;
import com.engine.salary.constant.SalaryArchiveConstant;
import com.engine.salary.constant.SalaryBillConstant;
import com.engine.salary.constant.SalaryTemplateSalaryItemSetGroupConstant;
@ -19,17 +21,24 @@ import com.engine.salary.enums.salarybill.SalaryTemplateTextContentPositionEnum;
import com.engine.salary.enums.salarybill.SalaryTemplateWhetherEnum;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.pdf.HtmlToPdf;
import com.fapiao.neon.util.Base64Utils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weaver.common.MessageUtil;
import weaver.conn.RecordSet;
import weaver.email.EmailWorkRunnable;
import weaver.file.ImageFileManager;
import weaver.hrm.company.SubCompanyComInfo;
import weaver.hrm.resource.ResourceComInfo;
import java.io.IOException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -38,6 +47,8 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import static com.engine.salary.constant.HrmSalaryPayrollConf.HAS_SIGN;
public class SalaryBillBO {
private static final Logger log = LoggerFactory.getLogger(SalaryBillBO.class);
@ -255,23 +266,101 @@ public class SalaryBillBO {
}
}
public static void sendEmail(Map<String, Object> e, Map<String, DataCollectionEmployee> allEmployeeMap, SalaryBillSendDTO salaryBillSendParam) {
StringBuilder emailContent = new StringBuilder();
emailContent.append("<div class='container' style='position: relative; overflow: auto;'>");
public static void sendEmail(Map<String, Object> e, SalaryBillSendDTO salaryBillSendParam) {
String content = genPayrollHtmlContent(e, salaryBillSendParam);
// 消息接收者
String receivers = Optional.ofNullable(e.get("email")).orElse("").toString();
String title = getBillTitle(salaryBillSendParam.getSalaryTemplate().getTheme(), salaryBillSendParam.getSalaryDate(), Long.valueOf(e.get("employeeId").toString()));
SalaryBillBO.buildEmailContent(emailContent, e, salaryBillSendParam);
// 构建水印内容
buildEmailWatermarkContent(emailContent, e, salaryBillSendParam);
emailContent.append("</div>");
if (StringUtils.isNotBlank(receivers)) {
// MessageUtil.sendEmail(receivers, title, emailContent.toString());
EmailWorkRunnable.threadModeReminder(receivers, title, emailContent.toString());
EmailWorkRunnable.threadModeReminder(receivers, title, content);
}
}
public static void genPdf(Map<String, Object> e, SalaryBillSendDTO salaryBillSendParam) {
String content = genPayrollHtmlContent(e, salaryBillSendParam);
String yyyyMM = SalaryDateUtil.getFormatYearMonth(salaryBillSendParam.getSalaryDate());
Long sendId = salaryBillSendParam.getSalarySend().getId();
Object id = e.getOrDefault("id", 1L);
String htmlPath = HrmSalaryPayrollConf.GEN_PATH + yyyyMM + File.separator + sendId + File.separator + id + ".html";
FileUtil.del(htmlPath);
File touch = FileUtil.touch(htmlPath);
FileUtil.appendUtf8String(content, touch);
String pdfPath = HrmSalaryPayrollConf.GEN_PATH + yyyyMM + File.separator+ sendId + File.separator + id + ".pdf";
FileUtil.del(pdfPath);
HtmlToPdf.convert(HrmSalaryPayrollConf.TO_PDF_TOOL_PATH, htmlPath, pdfPath);
}
/**
* 合并pdff
*
* @param pdfPath 最终合并的pdf
* @param filesToMerge 待合并的pdf
*/
public static void mergePdf(String pdfPath, List<String> filesToMerge) {
// 创建PDF合并工具实例
PDFMergerUtility merger = new PDFMergerUtility();
// 遍历要合并的PDF文件列表
for (String file : filesToMerge) {
if (FileUtil.isFile(file)) {
try {
merger.addSource(file); // 将每个文件添加到合并工具
} catch (FileNotFoundException e) {
log.error("PDF合并失败1", e);
}
}
}
// 设置合并后的目标文件
merger.setDestinationFileName(pdfPath);
try {
// 执行合并操作
FileUtil.del(pdfPath);
merger.mergeDocuments();
} catch (IOException e) {
log.error("PDF合并失败2", e);
}
}
@NotNull
private static String genPayrollHtmlContent(Map<String, Object> e, SalaryBillSendDTO salaryBillSendParam) {
StringBuilder emailContent = new StringBuilder();
emailContent.append("<meta charset=\"UTF-8\">");
emailContent.append("<div class='container' style='position: relative; overflow: auto;'>");
SalaryBillBO.buildEmailContent(emailContent, e, salaryBillSendParam);
// 构建水印内容
buildEmailWatermarkContent(emailContent, e, salaryBillSendParam);
emailContent.append("</div>");
return emailContent.toString();
}
public static byte[] readInputStream(InputStream is) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
try {
while ((length = is.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
baos.flush();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
byte[] data = baos.toByteArray();
try {
is.close();
baos.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return data;
}
public static void sendSMS(SalaryBillSendDTO salaryBillSendParam, Long id, Long employeeId) {
@ -432,7 +521,7 @@ public class SalaryBillBO {
* @return
*/
public static void buildEmailContent(StringBuilder emailContent, Map<String, Object> e, SalaryBillSendDTO salaryBillSendParam) {
emailContent.append("<div style='display: flex;flex-direction: column;justify-content: flex-start;align-items: center;padding: 16px;'>");
emailContent.append("<div style='display: flex;flex-direction: column;justify-content: flex-start;align-items: center;padding: 16px;background-color: #f6f6f6;width: 850px;margin: 20px auto;box-shadow: 0 0 14px 0 hsla(0,0%,84%,.5);border: 3px solid #fff;'>");
// 1.标题
emailContent.append("<div style='width: 100%;min-height: 22px; line-height: 30px; font-family: PingFangSC-Medium;font-size: 22px;color: #020202;font-weight: 500;text-align: center;'>");
// emailContent.append(salaryBillSendParam.getTitle());
@ -463,6 +552,20 @@ public class SalaryBillBO {
buildMailMain(emailContent, e, salaryBillSendParam);
}
//签章
if (HAS_SIGN) {
RecordSet rs = new RecordSet();
rs.execute("select * from DocSignature where hrmresid=" + e.getOrDefault("employeeId", "1") + " order by markid");
if (rs.next()) {
int imagefileid = rs.getInt("imagefileid");
InputStream imageInputStream = ImageFileManager.getInputStreamById(imagefileid);
byte[] data = readInputStream(imageInputStream);
String imageBase64 = "data:image/jpeg;base64," + Base64Utils.encodeToString(data);
emailContent.append("<div style='width: 100%;margin-top: 16px;text-align: right;line-height: 1.5715;font-family: PingFangSC-Regular;font-size: 12px;color: #111111;'>\n" +
" <img style='width: 140px;height: 70px;' src='" + imageBase64 + "'>" +
" </div>");
}
}
emailContent.append("</div>");
}
@ -489,56 +592,53 @@ public class SalaryBillBO {
if (CollectionUtils.isEmpty(salaryItemSet.getItems())) {
continue;
}
emailContent.append("<div style='margin-bottom: 16px;'>");
emailContent.append(" <div style='margin-bottom: 16px;background-color: #fff;padding: 10px;'>");
if (!salaryItemSet.getGroupId().equals(SalaryTemplateSalaryItemSetGroupConstant.NO_TYPE_GROUP_ID)) {
emailContent.append("<div style='padding: 0 16px;width: 100%;font-family: PingFangSC-Regular;font-size: 18px;box-sizing: border-box;'>");
emailContent.append("<div style='line-height: 2.5715;width: 100%;font-family: PingFangSC-Regular;font-size: 14px;box-sizing: border-box;color: #000;font-weight: bold;'>");
// 4.1.薪资项目组名
emailContent.append(salaryItemSet.getGroupName());
emailContent.append("</div>");
}
emailContent.append("<div style='margin-top: 16px;width: 100%;display: flex;flex-direction: column;align-items: flex-start;box-sizing: border-box;'>");
for (int i = 0; i < salaryItemSet.getItems().size(); i++) {
SalaryTemplateSalaryItemListDTO salaryItem = salaryItemSet.getItems().get(i);
// 员工基本信息
if (salaryItemSet.getGroupId().equals(SalaryTemplateSalaryItemSetGroupConstant.EMPLOYEE_INFO_GROUP_ID) && salaryBillSendParam.getEmployeeInformation() != null) {
Optional<SalaryTemplateSalaryItemListDTO> optionalEmpItem = salaryBillSendParam.getEmployeeInformation().getItems().stream().filter(f -> f.getId().equals(salaryItem.getId())).findFirst();
// 4.2.员工信息
emailContent.append("<div style='border: 1px solid #e5e5e5;border-bottom: none;width: 100%;display: flex;justify-content: flex-start;align-items: center;box-sizing: border-box;");
emailContent.append(i == (salaryItemSet.getItems().size() - 1) ? "border-bottom: 1px solid #e5e5e5;" : "");
emailContent.append("'>");
emailContent.append("<div style='min-height: 60px;padding: 0 16px;flex: 2;background: #fbfbfb;border-right: 1px solid #e5e5e5;display: flex;align-items: center;justify-content: left;' title={name}>");
emailContent.append("<span class='text'>");
emailContent.append((optionalEmpItem.isPresent() ? optionalEmpItem.get().getName() : ""));
emailContent.append("</span>");
emailContent.append("</div>");
emailContent.append("<div style='min-height: 60px;flex: 8;padding-left: 16px;background: #ffffff;display: flex;align-items: center;justify-content: left;'>");
emailContent.append((optionalEmpItem.isPresent() ? optionalEmpItem.get().getSalaryItemValue() : ""));
emailContent.append("</div>");
emailContent.append("</div>");
} else {
for (Object keyName : e.keySet()) {
if ((salaryItem.getId() + SalaryArchiveConstant.DYNAMIC_SUFFIX).equals(keyName.toString())) {
boolean isHide = (isHideNull && e.get(keyName.toString()) != null) || (isHideZero && "0.00".equals(e.get(keyName.toString())));
// 4.2.薪资项目
emailContent.append("<div style='border: 1px solid #e5e5e5;border-bottom: none;width: 100%;display: flex;justify-content: flex-start;align-items: center;box-sizing: border-box;");
emailContent.append(i == (salaryItemSet.getItems().size() - 1) ? "border-bottom: 1px solid #e5e5e5;" : "");
emailContent.append((isHide ? "display: none" : ""));
emailContent.append("'>");
emailContent.append("<div style='min-height: 60px;padding: 0 16px;flex: 2;background: #fbfbfb;border-right: 1px solid #e5e5e5;display: flex;align-items: center;justify-content: left;' title={name}>");
emailContent.append("<span class='text'>");
emailContent.append(salaryItem.getName());
emailContent.append("</span>");
emailContent.append("</div>");
emailContent.append("<div style='min-height: 60px;flex: 8;padding-left: 16px;background: #ffffff;display: flex;align-items: center;justify-content: left;'>");
emailContent.append(e.get(keyName.toString()));
emailContent.append("</div>");
emailContent.append("</div>");
break;
emailContent.append("<table style='table-layout:fixed;border-collapse:collapse;width:100%;border:1px solid rgba(0,0,0,.06);background-color: #fff;'>");
emailContent.append("<tbody>");
List<SalaryTemplateSalaryItemListDTO> items = salaryItemSet.getItems();
List<List<SalaryTemplateSalaryItemListDTO>> itemsPartition = Lists.partition(items, 3);
itemsPartition.forEach(itemPartition -> {
emailContent.append("<tr>");
for (int i = 0; i < itemPartition.size(); i++) {
SalaryTemplateSalaryItemListDTO salaryItem = itemPartition.get(i);
// 员工基本信息
if (salaryItemSet.getGroupId().equals(SalaryTemplateSalaryItemSetGroupConstant.EMPLOYEE_INFO_GROUP_ID) && salaryBillSendParam.getEmployeeInformation() != null) {
Optional<SalaryTemplateSalaryItemListDTO> optionalEmpItem = salaryBillSendParam.getEmployeeInformation().getItems().stream().filter(f -> f.getId().equals(salaryItem.getId())).findFirst();
// 4.2.员工信息
emailContent.append("<th style=\"background-color:#fafafa;padding:16px 24px;color:#000000d9;font-weight:400;font-size:12px;line-height:1.5715;text-align:start;border:1px solid rgba(0,0,0,.06);min-width:100px;max-width:100px\">");
emailContent.append((optionalEmpItem.isPresent() ? optionalEmpItem.get().getName() : ""));
emailContent.append("</th>");
emailContent.append("<td style=\"padding: 16px 24px;display: table-cell;flex: 1;color: #000000d9;font-size: 12px;line-height: 1.5715;word-break: break-word;overflow-wrap: break-word;border-collapse: collapse;border: 1px solid rgba(0,0,0,.06);\">");
emailContent.append((optionalEmpItem.isPresent() ? optionalEmpItem.get().getSalaryItemValue() : ""));
emailContent.append("</td>");
} else {
for (Object keyName : e.keySet()) {
if ((salaryItem.getId() + SalaryArchiveConstant.DYNAMIC_SUFFIX).equals(keyName.toString())) {
boolean isHide = (isHideNull && e.get(keyName.toString()) != null) || (isHideZero && "0.00".equals(e.get(keyName.toString())));
// 4.2.薪资项目
emailContent.append("<th style=\"background-color:#fafafa;padding:16px 24px;color:#000000d9;font-weight:400;font-size:12px;line-height:1.5715;text-align:start;border:1px solid rgba(0,0,0,.06);min-width:100px;max-width:100px\">");
emailContent.append(salaryItem.getName());
emailContent.append("</th>");
emailContent.append("<td style=\"padding: 16px 24px;display: table-cell;flex: 1;color: #000000d9;font-size: 12px;line-height: 1.5715;word-break: break-word;overflow-wrap: break-word;border-collapse: collapse;border: 1px solid rgba(0,0,0,.06);\">");
emailContent.append(e.get(keyName.toString()));
emailContent.append("</td>");
break;
}
}
}
}
}
emailContent.append("</div>");
emailContent.append("</tr>");
});
emailContent.append("</tbody>");
emailContent.append("</table>");
emailContent.append("</div>");
}
emailContent.append("</div>");
@ -586,29 +686,36 @@ public class SalaryBillBO {
emailContent.append("</div>");
emailContent.append("</div>");
for (int i = 0; i < salaryItemSet.getItems().size(); i++) {
SalaryTemplateSalaryItemListDTO salaryItem = salaryItemSet.getItems().get(i);
for (Object keyName : e.keySet()) {
if ((salaryItem.getId() + SalaryArchiveConstant.DYNAMIC_SUFFIX).equals(keyName.toString())) {
boolean isHide = (isHideNull && e.get(keyName.toString()) != null) || (isHideZero && "0.00".equals(e.get(keyName.toString())));
// 4.2.薪资项目
emailContent.append("<div style='border: 1px solid #e5e5e5;border-bottom: none;width: 100%;display: flex;justify-content: flex-start;align-items: center;box-sizing: border-box;");
emailContent.append(i == (salaryItemSet.getItems().size() - 1) && salaryBillSendParam.getSalaryTemplate().getTextContentPosition() == SalaryTemplateTextContentPositionEnum.BEFORE.getValue() ? "border-bottom: 1px solid #e5e5e5;" : "");
emailContent.append((isHide ? "display: none" : ""));
emailContent.append("'>");
emailContent.append("<div style='min-height: 60px;padding: 0 16px;flex: 2;background: #fbfbfb;border-right: 1px solid #e5e5e5;display: flex;align-items: center;justify-content: left;' title={name}>");
emailContent.append("<span class='text'>");
emailContent.append(salaryItem.getName());
emailContent.append("</span>");
emailContent.append("</div>");
emailContent.append("<div style='min-height: 60px;flex: 8;padding-left: 16px;background: #ffffff;display: flex;align-items: center;justify-content: left;'>");
emailContent.append(e.get(keyName.toString()));
emailContent.append("</div>");
emailContent.append("</div>");
break;
emailContent.append("<table style='table-layout:fixed;border-collapse:collapse;width:100%;border:1px solid rgba(0,0,0,.06);background-color: #fff;'>");
emailContent.append("<tbody>");
List<SalaryTemplateSalaryItemListDTO> items = salaryItemSet.getItems();
List<List<SalaryTemplateSalaryItemListDTO>> itemsPartition = Lists.partition(items, 3);
itemsPartition.forEach(itemPartition -> {
emailContent.append("<tr>");
for (int i = 0; i < itemPartition.size(); i++) {
SalaryTemplateSalaryItemListDTO salaryItem = itemPartition.get(i);
for (Object keyName : e.keySet()) {
if ((salaryItem.getId() + SalaryArchiveConstant.DYNAMIC_SUFFIX).equals(keyName.toString())) {
boolean isHide = (isHideNull && e.get(keyName.toString()) != null) || (isHideZero && "0.00".equals(e.get(keyName.toString())));
// 4.2.薪资项目
emailContent.append("<th style=\"background-color:#fafafa;padding:16px 24px;color:#000000d9;font-weight:400;font-size:12px;line-height:1.5715;text-align:start;border:1px solid rgba(0,0,0,.06);min-width:100px;max-width:100px\">");
emailContent.append(salaryItem.getName());
emailContent.append("</th>");
emailContent.append("<td style=\"padding: 16px 24px;display: table-cell;flex: 1;color: #000000d9;font-size: 12px;line-height: 1.5715;word-break: break-word;overflow-wrap: break-word;border-collapse: collapse;border: 1px solid rgba(0,0,0,.06);\">");
emailContent.append(e.get(keyName.toString()));
emailContent.append("</td>");
break;
}
}
}
}
emailContent.append("</tr>");
});
emailContent.append("</tbody>");
emailContent.append("</table>");
// 5.文本内容-如果在薪资项目后
emailContent.append("<div style='border: 1px solid #e5e5e5;border-bottom: none;width: 100%;display: flex;justify-content: flex-start;align-items: center;box-sizing: border-box;");
emailContent.append(salaryBillSendParam.getSalaryTemplate().getTextContentPosition() == SalaryTemplateTextContentPositionEnum.AFTER.getValue() ? "border-bottom: 1px solid #e5e5e5;" : "display: none");
@ -644,7 +751,7 @@ public class SalaryBillBO {
/**
* 构建水印
*
* <p>
* 当前所拥有的变量
* "HRM_Name",当前操作者姓名
* "HRM_Num",当前操作者编号
@ -715,7 +822,7 @@ public class SalaryBillBO {
List<String> wmTextFieldIds = Collections.emptyList();
// 没有水印关闭水印或者系统水印则不拼接
// if (Objects.isNull(salaryBillWatermark) || !salaryBillWatermark.getWatermarkStatus() || !SalaryTemplateWatermarkTypeEnum.CUSTOM.getValue().equals(salaryBillWatermark.getWatermarkType())) {
if (Objects.isNull(salaryBillWatermark) || !salaryBillWatermark.getWatermarkStatus() ) {
if (Objects.isNull(salaryBillWatermark) || !salaryBillWatermark.getWatermarkStatus()) {
return wmTextFieldIds;
}
String wmClassify = salaryBillWatermark.getWmSetting().getOrDefault("wmClassify", StringUtils.EMPTY).toString();
@ -762,7 +869,7 @@ public class SalaryBillBO {
public static String buildEmailWmContentTemplate(boolean isEnableEmail, SalaryBillWatermarkDTO watermarkSetting) {
String emailWmContentTemplate = StringUtils.EMPTY;
// 没有水印关闭水印则不拼接
if (!isEnableEmail || Objects.isNull(watermarkSetting) || !watermarkSetting.getWatermarkStatus() ) {
if (!isEnableEmail || Objects.isNull(watermarkSetting) || !watermarkSetting.getWatermarkStatus()) {
return emailWmContentTemplate;
}
String wmClassify = watermarkSetting.getWmSetting().getOrDefault("wmClassify", StringUtils.EMPTY).toString();
@ -789,7 +896,7 @@ public class SalaryBillBO {
for (int i = 0; i < 20; i++) {
emailWmContentTemp.append("<div class='row' style='width: 100%; min-height: 150px ; overflow: hidden; display: flex;justify-content: center;justify-content: center;'>");
for (int j = 0; j < 8; j++) {
emailWmContentTemp.append("<div class='item' style='display: flex;flex-direction: column; align-items: center;justify-content: center; min-width: 200px; height: 150px;opacity: "+noTransparent+"; transform: rotate(-"+wmRotate+"deg);'>");
emailWmContentTemp.append("<div class='item' style='display: flex;flex-direction: column; align-items: center;justify-content: center; min-width: 200px; height: 150px;opacity: " + noTransparent + "; transform: rotate(-" + wmRotate + "deg);'>");
// 赋值
emailWmContentTemp.append(variable);
emailWmContentTemp.append("</div>");
@ -801,7 +908,7 @@ public class SalaryBillBO {
return emailWmContentTemp.toString();
}
public static String handleWmText(String wmText, List<String> wmTextFieldIds, DataCollectionEmployee simpleEmployee){
public static String handleWmText(String wmText, List<String> wmTextFieldIds, DataCollectionEmployee simpleEmployee) {
for (String wmTextFieldId : wmTextFieldIds) {
// 当前操作者姓名
if (SalaryBillConstant.HRM_Name.equals(wmTextFieldId)) {
@ -820,10 +927,10 @@ public class SalaryBillBO {
wmText = wmText.replace(SalaryBillConstant.HRM_prefix + SalaryBillConstant.HRM_CurrentOperatorId, Objects.isNull(simpleEmployee) ? StringUtils.EMPTY : simpleEmployee.getEmployeeId().toString());
// 当前操作者部门
} else if (SalaryBillConstant.HRM_Department.equals(wmTextFieldId)) {
wmText = wmText.replace(SalaryBillConstant.HRM_prefix + SalaryBillConstant.HRM_Department, Objects.isNull(simpleEmployee) || Objects.isNull(simpleEmployee.getDepartmentName()) ? StringUtils.EMPTY : simpleEmployee.getDepartmentName());
wmText = wmText.replace(SalaryBillConstant.HRM_prefix + SalaryBillConstant.HRM_Department, Objects.isNull(simpleEmployee) || Objects.isNull(simpleEmployee.getDepartmentName()) ? StringUtils.EMPTY : simpleEmployee.getDepartmentName());
// 当前操作者分部
} else if (SalaryBillConstant.HRM_SecondDepartment.equals(wmTextFieldId)) {
wmText = wmText.replace(SalaryBillConstant.HRM_prefix + SalaryBillConstant.HRM_SecondDepartment, Objects.isNull(simpleEmployee) || Objects.isNull(simpleEmployee.getSubcompanyName()) ? StringUtils.EMPTY : simpleEmployee.getSubcompanyName());
wmText = wmText.replace(SalaryBillConstant.HRM_prefix + SalaryBillConstant.HRM_SecondDepartment, Objects.isNull(simpleEmployee) || Objects.isNull(simpleEmployee.getSubcompanyName()) ? StringUtils.EMPTY : simpleEmployee.getSubcompanyName());
// 当前日期
} else if (SalaryBillConstant.HRM_CurrentDate.equals(wmTextFieldId)) {
wmText = wmText.replace(SalaryBillConstant.HRM_prefix + SalaryBillConstant.HRM_CurrentDate, SalaryDateUtil.getFormatLocalDate(LocalDate.now()));

View File

@ -0,0 +1,26 @@
package com.engine.salary.entity.salaryBill.param;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 工资单导出pdf参数
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SalaryExportPdfParam {
/**
* 工资单发放Id
*/
private Long salarySendId;
/**
* 主键id
*/
private Long id;
}

View File

@ -1,5 +1,6 @@
package com.engine.salary.service;
import com.engine.salary.entity.salaryBill.param.SalaryExportPdfParam;
import com.engine.salary.entity.salaryBill.param.SalarySendGrantParam;
import java.util.List;
@ -42,6 +43,9 @@ public interface SalaryBillService {
*/
void feedBackSalaryBill(Long salaryInfoId);
String exportPdf(SalaryExportPdfParam param);
/**
* 工资单撤回
*

View File

@ -7,6 +7,7 @@ import com.engine.core.impl.Service;
import com.engine.salary.biz.SalarySendBiz;
import com.engine.salary.biz.SalarySendInfoBiz;
import com.engine.salary.cache.SalaryCacheKey;
import com.engine.salary.constant.HrmSalaryPayrollConf;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.progress.ProgressDTO;
import com.engine.salary.entity.salaryBill.bo.SalaryBillBO;
@ -15,6 +16,7 @@ import com.engine.salary.entity.salaryBill.dto.SalaryBillSendDTO;
import com.engine.salary.entity.salaryBill.dto.SalaryBillWatermarkDTO;
import com.engine.salary.entity.salaryBill.dto.SalarySendInfoListDTO;
import com.engine.salary.entity.salaryBill.dto.SalaryTemplateSalaryItemSetListDTO;
import com.engine.salary.entity.salaryBill.param.SalaryExportPdfParam;
import com.engine.salary.entity.salaryBill.param.SalarySendGrantParam;
import com.engine.salary.entity.salaryBill.param.SalarySendInfoQueryParam;
import com.engine.salary.entity.salaryBill.po.SalarySendInfoPO;
@ -31,6 +33,7 @@ import com.engine.salary.mapper.salarybill.SalarySendInfoMapper;
import com.engine.salary.mapper.salarybill.SalarySendMapper;
import com.engine.salary.service.*;
import com.engine.salary.util.JsonUtil;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.google.common.collect.Lists;
@ -45,12 +48,15 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import weaver.hrm.User;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static com.engine.salary.constant.HrmSalaryPayrollConf.GEN_PDF;
/**
* 工资单发放
* <p>Copyright: Copyright (c) 2022</p>
@ -129,7 +135,7 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
* 工资单发放 start
**********************************************************************/
@Override
public Map<String, Object> grant(SalarySendGrantParam param) {
public Map<String, Object> grant(SalarySendGrantParam param) {
// 1.检查和获取工资单发放
SalarySendPO salarySend = checkAndGetSalarySend(param.getSalarySendId());
// // 已经冻结不能操作
@ -219,6 +225,9 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
// 3.发送消息先修改数据再发消息避免出错后无法撤回
List<Long> successIds = sendMessage(enableSendList, salaryBillSendParam);
//生成pdf
genPdf(salaryBillSendParam, enableSendList);
// 4.发放
grantSendInfo(successIds, salarySend, salaryTemplate, salaryBillSendParam);
@ -232,8 +241,6 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
+ SalaryI18nUtil.getI18nLabel(134808, "失败条数") + "[" + (total - successCount) + "]";
// 发送进度完成
getProgressService(user).finish(SalaryCacheKey.SALARY_GRANT_PROGRESS + "_" + salarySend.getId(), true, messsage);
// log.info("工资单发送组装耗时:{}毫秒;工资单发送消息中心耗时:{}毫秒;工资单数据更改总耗时:{}毫秒;工资单发送总耗时:{}毫秒;工资单云桥图片地址:{}", l3 - l2, l4 - l3, l5 - l4, System.currentTimeMillis() - l,
// salaryBillSendParam == null ? "" : salaryBillSendParam.getPicUrl());
} catch (Exception e) {
log.info("发送出错:{}", e.getMessage(), e);
// 发送进度失败
@ -242,6 +249,31 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
}
}
private void genPdf(SalaryBillSendDTO salaryBillSendParam, List<Map<String, Object>> enableSendList) {
if (GEN_PDF) {
LocalRunnable localRunnable = new LocalRunnable() {
@Override
public void execute() {
//生成工资单pdf
enableSendList.forEach(e -> {
SalaryBillBO.genPdf(e, salaryBillSendParam);
});
//合并工资单pdf
//1先获取所有工资单
Long id = salaryBillSendParam.getSalarySend().getId();
List<SalarySendInfoPO> salarySendInfos = getSalarySendInfoMapper().listSome(SalarySendInfoPO.builder().salarySendId(id).sendStatus(1).build());
//2工资单pdf转为路径
String yyyyMM = SalaryDateUtil.getFormatYearMonth(salaryBillSendParam.getSalaryDate());
List<String> filesToMerge = salarySendInfos.stream().map(po -> HrmSalaryPayrollConf.GEN_PATH + yyyyMM + File.separator + id + File.separator + po.getId() + ".pdf").collect(Collectors.toList());
String pdfPath = HrmSalaryPayrollConf.GEN_PATH + yyyyMM + File.separator + id + File.separator + id + ".pdf";
SalaryBillBO.mergePdf(pdfPath, filesToMerge);
}
};
ThreadPoolUtil.fixedPoolExecute(ModulePoolEnum.OTHER, "salaryBillGenPdf", localRunnable);
}
}
/**
* 构建发送参数
*
@ -301,7 +333,7 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
// 工资单水印文本动态变量
List<String> wmTextFieldIds = SalaryBillBO.getWmTextFieldIds(domain, salaryBillWatermark);
// 邮件水印模板
boolean isEnableEmail = salaryTemplate.getEmailStatus().equals(SalaryTemplateWhetherEnum.TRUE.getValue());
boolean isEnableEmail = salaryTemplate.getEmailStatus().equals(SalaryTemplateWhetherEnum.TRUE.getValue());
String emailWmContentTemplate = SalaryBillBO.buildEmailWmContentTemplate(isEnableEmail, salaryBillWatermark);
return SalaryBillSendDTO.builder()
@ -406,7 +438,7 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
// 更新进度
getProgressService(user).getAndAddCalculatedQty(SalaryCacheKey.SALARY_GRANT_PROGRESS + "_" + salarySend.getId(), part.size());
/** 注意只有邮件才需要加密的核算数据 */
if (isEnableEmail) {
if (isEnableEmail || GEN_PDF) {
List<SalaryAcctEmployeePO> acctEmployees = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordIdsAndEmployeeIds(Collections.singletonList(salarySend.getSalaryAccountingId()), part);
salaryAcctEmployees.addAll(acctEmployees);
salaryAcctResultValues.addAll(getSalaryAcctResultService(user).listBySalaryAcctEmployeeIds(SalaryEntityUtil.properties(acctEmployees, SalaryAcctEmployeePO::getId, Collectors.toList())));
@ -414,7 +446,7 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
}
} else {
/** 注意只有邮件才需要加密的核算数据 */
if (isEnableEmail) {
if (isEnableEmail || GEN_PDF) {
salaryAcctEmployees = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordId(salarySend.getSalaryAccountingId());
salaryAcctResultValues = getSalaryAcctResultService(user).listBySalaryAcctRecordIds(Collections.singletonList(salarySend.getSalaryAccountingId()));
}
@ -428,7 +460,7 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
@Override
public void confirmSalaryBill(Long salaryInfoId) {
SalarySendInfoPO sendInfoPO = getSalarySendInfoMapper().getById(salaryInfoId);
if(ObjectUtils.isEmpty(sendInfoPO)){
if (ObjectUtils.isEmpty(sendInfoPO)) {
throw new SalaryRunTimeException("工资单不存在或已被删除!");
}
sendInfoPO.setBillConfirmStatus(BillConfimStatusEnum.CONFIRMED.getValue());
@ -439,7 +471,7 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
@Override
public void feedBackSalaryBill(Long salaryInfoId) {
SalarySendInfoPO sendInfoPO = getSalarySendInfoMapper().getById(salaryInfoId);
if(ObjectUtils.isEmpty(sendInfoPO)){
if (ObjectUtils.isEmpty(sendInfoPO)) {
throw new SalaryRunTimeException("工资单不存在或已被删除!");
}
sendInfoPO.setBillConfirmStatus(BillConfimStatusEnum.FEEDBACK.getValue());
@ -447,6 +479,20 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
getSalarySendInfoMapper().updateIgnoreNull(sendInfoPO);
}
@Override
public String exportPdf(SalaryExportPdfParam param) {
SalarySendPO salarySend = checkAndGetSalarySend(param.getSalarySendId());
String yearMonth = SalaryDateUtil.getFormatYearMonth(salarySend.getSalaryMonth());
String path = HrmSalaryPayrollConf.GEN_PATH + File.separator + yearMonth + File.separator + salarySend.getId() + File.separator + "%s" + ".pdf";
Long id = param.getId();
if (id == null) {
path = String.format(path, param.getSalarySendId());
} else {
path = String.format(path, id);
}
return path;
}
public List<Map<String, Object>> getSendInfoList(Long sendId, List<Long> ids) {
SalarySendPO salarySend = getSalarySendMapper().getById(sendId);
@ -525,10 +571,10 @@ public class SalaryBillServiceImpl extends Service implements SalaryBillService
SalaryBillBO.sendMsg(salaryBillSendParam, Long.valueOf(e.get("id").toString()), Long.valueOf(e.get("employeeId").toString()));
}
if (sendChannels.contains(MessageChannelEnum.EMAIL)) {
if (sendChannels.contains(MessageChannelEnum.EMAIL) || GEN_PDF) {
// 构建发送消息
SalaryBillBO.buildEmployeeInfo(salaryBillSendParam, allEmployeeMap.get(e.get("employeeId").toString()));
SalaryBillBO.sendEmail(e, allEmployeeMap, salaryBillSendParam);
SalaryBillBO.sendEmail(e, salaryBillSendParam);
}

View File

@ -0,0 +1,57 @@
package com.engine.salary.util.pdf;
import com.engine.workflow.biz.requestForm.HtmlToPdfInterceptor;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
@Slf4j
public class HtmlToPdf {
/**
* html转pdf
*
* @param toPdfTool 工具路径
* @param srcPath html路径可以是硬盘上的路径也可以是网络路径
* @param destPath pdf保存路径
* @return 转换成功返回true
*/
public static boolean convert(String toPdfTool, String srcPath, String destPath) {
File file = new File(destPath);
File parent = file.getParentFile();
//如果pdf保存路径不存在则创建路径
if (!parent.exists()) {
parent.mkdirs();
}
StringBuilder cmd = new StringBuilder();
cmd.append(toPdfTool);
cmd.append(" ");
cmd.append(" --header-line");//页眉下面的线
cmd.append(" --margin-top 3cm ");//设置页面上边距 (default 10mm)
// cmd.append(" --header-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\head.html"));// (添加一个HTML页眉,后面是网址)
cmd.append(" --header-spacing 5 --orientation Portrait ");// (设置页眉和内容的距离,默认0)
//cmd.append(" --footer-center (设置在中心位置的页脚内容)");//设置在中心位置的页脚内容
//cmd.append(" --footer-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\foter.html"));// (添加一个HTML页脚,后面是网址)
cmd.append(" --footer-line");//* 显示一条线在页脚内容上)
cmd.append(" --footer-spacing 5 ");// (设置页脚和内容的距离)
//--orientation Portrait --footer-center [page]/[topage] --footer-line
cmd.append(srcPath);
cmd.append(" ");
cmd.append(destPath);
boolean result = true;
try {
Process proc = Runtime.getRuntime().exec(cmd.toString());
HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream());
HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream());
error.start();
output.start();
proc.waitFor();
} catch (Exception e) {
result = false;
e.printStackTrace();
}
return result;
}
}

View File

@ -29,8 +29,11 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
@ -521,6 +524,53 @@ public class SalaryBillController {
return Response.ok(output).header("Content-disposition", "attachment;filename=" + fileName).header("Cache-Control", "no-cache").build();
}
// @GET
// @Path("/exportPdf")
// @Produces({"application/pdf"})
// public Response getPDF(@Context HttpServletRequest request, @Context HttpServletResponse response) {
//
// File f = new File("D:\\gzd\\2023-02\\1695104948592.pdf");
// return Response.ok(f, "application/pdf").build();
//
// }
@GET
@Path("/exportPdf")
public Response downloadPdfFile(@Context HttpServletRequest request, @Context HttpServletResponse response) {
User user = HrmUserVarify.getUser(request, response);
SalaryExportPdfParam salaryExportPdfParam = new SalaryExportPdfParam();
String id = request.getParameter("id");
if (StringUtils.isNotBlank(id)) {
salaryExportPdfParam.setId(Long.valueOf(id));
}
String salarySendId = request.getParameter("salarySendId");
if (StringUtils.isNotBlank(salarySendId)) {
salaryExportPdfParam.setSalarySendId(Long.valueOf(salarySendId));
}
StreamingOutput fileStream = new StreamingOutput() {
@Override
public void write(java.io.OutputStream output) throws IOException, WebApplicationException {
String pdfPath = getSalarySendWrapper(user).exportPdf(salaryExportPdfParam);
java.nio.file.Path path = Paths.get(pdfPath);
byte[] data = Files.readAllBytes(path);
output.write(data);
output.flush();
}
};
String fileName = "工资单";
try {
fileName = URLEncoder.encode(fileName + ".pdf", "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return Response
.ok(fileStream, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition", "attachment; filename =" + fileName)
.build();
}
/**
* 我的工资单列表
*
@ -637,6 +687,7 @@ public class SalaryBillController {
/**
* 校验验证码
*
* @param request
* @param response
* @param param
@ -660,7 +711,8 @@ public class SalaryBillController {
@GET
@Path("/baseSet/getForm")
@Produces(MediaType.APPLICATION_JSON)
public String getBaseSetForm( @Context HttpServletRequest request, @Context HttpServletResponse response ) {
// @ApiOperation("获取工资单基础设置表单")
public String getBaseSetForm(@Context HttpServletRequest request, @Context HttpServletResponse response) {
User user = HrmUserVarify.getUser(request, response);
return new ResponseResult<String, SalaryBaseSetFormDTO>(user).run(getSalaryBillBaseSetWrapper(user)::getBaseSetForm);
}
@ -673,7 +725,7 @@ public class SalaryBillController {
@POST
@Path("/baseSet/previewWaterMark")
@Produces(MediaType.APPLICATION_JSON)
public String getBaseSetForm( @Context HttpServletRequest request, @Context HttpServletResponse response, @RequestBody SalaryBaseSetSaveParam saveParam ) {
public String getBaseSetForm(@Context HttpServletRequest request, @Context HttpServletResponse response, @RequestBody SalaryBaseSetSaveParam saveParam) {
User user = HrmUserVarify.getUser(request, response);
return new ResponseResult<SalaryBaseSetSaveParam, String>(user).run(getSalaryBillBaseSetWrapper(user)::previewWaterMark, saveParam);
}
@ -687,7 +739,8 @@ public class SalaryBillController {
@POST
@Path("/baseSet/save")
@Produces(MediaType.APPLICATION_JSON)
public String saveBaseSet( @Context HttpServletRequest request, @Context HttpServletResponse response, @RequestBody SalaryBaseSetSaveParam saveParam) {
// @ApiOperation("保存工资单基础设置")
public String saveBaseSet(@Context HttpServletRequest request, @Context HttpServletResponse response, @RequestBody SalaryBaseSetSaveParam saveParam) {
User user = HrmUserVarify.getUser(request, response);
return new ResponseResult<SalaryBaseSetSaveParam, String>(user).run(getSalaryBillBaseSetWrapper(user)::saveBaseSet, saveParam);
}

View File

@ -14,7 +14,6 @@ import com.cloudstore.eccom.result.WeaResultMsg;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.constant.SalaryItemConstant;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.salaryBill.dto.*;
import com.engine.salary.entity.salaryBill.param.*;
import com.engine.salary.entity.salaryBill.po.SalarySendPO;
@ -698,4 +697,8 @@ public class SalarySendWrapper extends Service implements SalarySendWrapperProxy
}
getSalaryBillService(user).feedBackSalaryBill(salaryInfoId);
}
public String exportPdf(SalaryExportPdfParam param) {
return getSalaryBillService(user).exportPdf(param);
}
}