package cn.iocoder.yudao.module.hrm.service.employee.contract;
|
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeContractDO;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeContractMapper;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper;
|
import com.deepoove.poi.XWPFTemplate;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
|
import java.io.ByteArrayOutputStream;
|
import java.io.InputStream;
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.time.format.DateTimeFormatter;
|
import java.time.temporal.ChronoUnit;
|
import java.util.HashMap;
|
import java.util.Map;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.CONTRACT_TEMPLATE_LOAD_FAILED;
|
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.CONTRACT_TEMPLATE_TYPE_NOT_SUPPORT;
|
|
/**
|
* 员工合同文档生成 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Slf4j
|
@Service
|
public class ContractDocumentServiceImpl implements ContractDocumentService {
|
|
/** 劳动合同(简版·社保折现) */
|
public static final int TYPE_LABOR_SIMPLE = 1;
|
/** 劳动合同(标准·官方制式) */
|
public static final int TYPE_LABOR_STANDARD = 2;
|
/** 劳务合同 */
|
public static final int TYPE_SERVICE = 3;
|
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
|
|
private static final Map<Integer, String> TEMPLATE_PATHS = Map.of(
|
TYPE_LABOR_SIMPLE, "template/contract/contract_labor_simple.docx",
|
TYPE_LABOR_STANDARD, "template/contract/contract_labor_standard.docx",
|
TYPE_SERVICE, "template/contract/contract_service.docx");
|
|
private static final Map<Integer, String> TEMPLATE_NAMES = Map.of(
|
TYPE_LABOR_SIMPLE, "劳动合同(简版)",
|
TYPE_LABOR_STANDARD, "劳动合同(标准)",
|
TYPE_SERVICE, "劳务合同");
|
|
@Resource
|
private HrmEmployeeContractMapper contractMapper;
|
@Resource
|
private HrmEmployeeMapper employeeMapper;
|
|
@Override
|
public byte[] generateDocx(Long contractId, Integer templateType) {
|
HrmEmployeeContractDO contract = contractMapper.selectById(contractId);
|
if (contract == null) {
|
throw exception(CONTRACT_TEMPLATE_LOAD_FAILED);
|
}
|
HrmEmployeeDO employee = contract.getEmployeeId() == null ? null
|
: employeeMapper.selectById(contract.getEmployeeId());
|
Map<String, Object> vars = buildVariables(contract, employee);
|
|
String path = TEMPLATE_PATHS.get(templateType);
|
if (path == null) {
|
throw exception(CONTRACT_TEMPLATE_TYPE_NOT_SUPPORT);
|
}
|
try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) {
|
if (in == null) {
|
throw exception(CONTRACT_TEMPLATE_LOAD_FAILED);
|
}
|
XWPFTemplate template = XWPFTemplate.compile(in).render(vars);
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
template.write(out);
|
template.close();
|
return out.toByteArray();
|
} catch (Exception e) {
|
log.error("[generateDocx][合同({}) 模板({}) 生成失败]", contractId, templateType, e);
|
throw exception(CONTRACT_TEMPLATE_LOAD_FAILED);
|
}
|
}
|
|
@Override
|
public String buildFileName(Long contractId, Integer templateType) {
|
HrmEmployeeContractDO contract = contractMapper.selectById(contractId);
|
String no = contract != null && contract.getContractNo() != null ? contract.getContractNo() : String.valueOf(contractId);
|
String name = TEMPLATE_NAMES.getOrDefault(templateType, "合同");
|
return no + "-" + name + ".docx";
|
}
|
|
private Map<String, Object> buildVariables(HrmEmployeeContractDO contract, HrmEmployeeDO employee) {
|
Map<String, Object> vars = new HashMap<>();
|
// 甲方(公司)—— 目前取签约主体,其余留空后续可接入公司配置
|
vars.put("companyName", nvl(contract.getSignCompany()));
|
vars.put("companyAddress", "");
|
vars.put("legalPerson", "");
|
vars.put("companyPhone", "");
|
vars.put("creditCode", "");
|
// 乙方(员工)
|
vars.put("employeeName", employee != null ? nvl(employee.getName()) : "");
|
vars.put("gender", employee != null ? genderText(employee.getGender()) : "");
|
vars.put("age", employee != null ? nvl(employee.getAge()) : "");
|
vars.put("idCard", employee != null ? nvl(employee.getIdCard()) : "");
|
vars.put("householdAddress", employee != null ? nvl(employee.getAddress()) : "");
|
vars.put("address", employee != null ? nvl(employee.getAddress()) : "");
|
vars.put("phone", employee != null ? nvl(employee.getPhone()) : "");
|
vars.put("emergencyPhone", "");
|
// 合同
|
vars.put("contractNo", nvl(contract.getContractNo()));
|
vars.put("postName", "");
|
vars.put("postDuty", "");
|
vars.put("workPlace", "");
|
vars.put("contractStart", formatDate(contract.getStartDate()));
|
vars.put("contractEnd", formatDate(contract.getEndDate()));
|
vars.put("contractMonths", monthsBetween(contract.getStartDate(), contract.getEndDate()));
|
vars.put("probationStart", formatDate(contract.getProbationStartDate()));
|
vars.put("probationEnd", formatDate(contract.getProbationEndDate()));
|
vars.put("probationMonths", monthsBetween(contract.getProbationStartDate(), contract.getProbationEndDate()));
|
vars.put("probationDays", daysBetween(contract.getProbationStartDate(), contract.getProbationEndDate()));
|
vars.put("birthday", employee != null ? formatDate(employee.getBirthday()) : "");
|
// 年月日(供「___年___月___日」逐格填写)
|
vars.put("contractStartY", y(contract.getStartDate()));
|
vars.put("contractStartM", mo(contract.getStartDate()));
|
vars.put("contractStartD", d(contract.getStartDate()));
|
vars.put("contractEndY", y(contract.getEndDate()));
|
vars.put("contractEndM", mo(contract.getEndDate()));
|
vars.put("contractEndD", d(contract.getEndDate()));
|
vars.put("probationStartY", y(contract.getProbationStartDate()));
|
vars.put("probationStartM", mo(contract.getProbationStartDate()));
|
vars.put("probationStartD", d(contract.getProbationStartDate()));
|
vars.put("probationEndY", y(contract.getProbationEndDate()));
|
vars.put("probationEndM", mo(contract.getProbationEndDate()));
|
vars.put("probationEndD", d(contract.getProbationEndDate()));
|
vars.put("probationSalary", nvl(contract.getProbationSalary()));
|
vars.put("regularSalary", nvl(contract.getRegularSalary()));
|
vars.put("baseSalary", nvl(contract.getRegularSalary()));
|
vars.put("performanceSalary", "");
|
vars.put("socialSecuritySalary", "");
|
vars.put("signDate", formatDate(contract.getSignDate()));
|
// 劳务合同里的可选项
|
vars.put("payType", "一");
|
vars.put("payDay", "");
|
vars.put("workVolume", "");
|
vars.put("workAmount", "");
|
vars.put("piecePrice", "");
|
vars.put("noticeCompensation", "");
|
vars.put("copies", "两");
|
vars.put("eachCopies", "一");
|
return vars;
|
}
|
|
private static String nvl(Object o) {
|
return o == null ? "" : o.toString();
|
}
|
|
private static String formatDate(LocalDate d) {
|
return d == null ? "" : d.format(DATE_FMT);
|
}
|
|
private static String monthsBetween(LocalDate start, LocalDate end) {
|
if (start == null || end == null) {
|
return "";
|
}
|
long months = ChronoUnit.MONTHS.between(start, end);
|
return String.valueOf(Math.max(months, 0));
|
}
|
|
private static String daysBetween(LocalDate start, LocalDate end) {
|
if (start == null || end == null) {
|
return "";
|
}
|
long days = ChronoUnit.DAYS.between(start, end);
|
return String.valueOf(Math.max(days, 0));
|
}
|
|
private static String y(LocalDate date) {
|
return date == null ? "" : String.format("%04d", date.getYear());
|
}
|
|
private static String mo(LocalDate date) {
|
return date == null ? "" : String.format("%02d", date.getMonthValue());
|
}
|
|
private static String d(LocalDate date) {
|
return date == null ? "" : String.format("%02d", date.getDayOfMonth());
|
}
|
|
private static String genderText(Integer sex) {
|
if (sex == null) {
|
return "";
|
}
|
return sex == 1 ? "男" : sex == 2 ? "女" : "";
|
}
|
|
// 兼容 BigDecimal 直接 toString
|
private static String nvl(BigDecimal v) {
|
return v == null ? "" : v.toPlainString();
|
}
|
|
}
|