1.1.46 导出出差审批单 PDF接口

dev
yinq 1 month ago
parent d7a536d6b9
commit 6de7c0d7ba

@ -1,6 +1,7 @@
package org.dromara.oa.crm.controller;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
@ -13,10 +14,14 @@ import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.word.util.WordTemplateUtil;
import com.deepoove.poi.config.Configure;
import org.dromara.oa.crm.word.CrmBusinessTripItineraryLoopRowPolicy;
import org.dromara.oa.crm.domain.vo.CrmBusinessTripApplyVo;
import org.dromara.oa.crm.domain.bo.CrmBusinessTripApplyBo;
import org.dromara.oa.crm.service.ICrmBusinessTripApplyService;
@ -57,6 +62,26 @@ public class CrmBusinessTripApplyController extends BaseController {
ExcelUtil.exportExcel(list, "出差申请", CrmBusinessTripApplyVo.class, response);
}
/**
* PDF Word
*
* @param tripId ID
*/
@SaCheckPermission("oa/crm:businessTripApply:export")
@Log(title = "出差申请单PDF导出", businessType = BusinessType.EXPORT)
@GetMapping("/exportTripApplyPdf/{tripId}")
public void exportTripApplyPdf(@NotNull(message = "出差申请ID不能为空") @PathVariable("tripId") Long tripId,
HttpServletResponse response) {
Map<String, Object> data = crmBusinessTripApplyService.buildTripApplyPdfExportData(tripId);
CrmBusinessTripApplyVo vo = crmBusinessTripApplyService.queryById(tripId);
String fileName = "出差申请单_" + (vo != null && StringUtils.isNotBlank(vo.getApplyCode())
? vo.getApplyCode() : tripId);
Configure config = Configure.builder()
.bind("行程明细", new CrmBusinessTripItineraryLoopRowPolicy())
.build();
WordTemplateUtil.renderToPdfResponse("出差申请单模板.docx", fileName, data, config, response);
}
/**
*
*

@ -8,6 +8,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Service
@ -74,4 +75,12 @@ public interface ICrmBusinessTripApplyService {
* @return
*/
CrmBusinessTripApplyVo submitAndFlowStart(CrmBusinessTripApplyBo bo);
/**
* PDF
*
* @param tripId ID
* @return Word
*/
Map<String, Object> buildTripApplyPdfExportData(Long tripId);
}

@ -1,7 +1,9 @@
package org.dromara.oa.crm.service.impl;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -23,9 +25,17 @@ import org.dromara.oa.crm.domain.CrmCustomerInfo;
import org.dromara.oa.erp.mapper.ErpProjectInfoMapper;
import org.dromara.oa.crm.mapper.CrmBusinessTripDetailsMapper;
import cn.hutool.core.bean.BeanUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
@ -33,6 +43,7 @@ import org.apache.seata.spring.annotation.GlobalTransactional;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.system.api.RemoteCodeRuleService;
import org.dromara.system.api.RemoteUserService;
import org.dromara.workflow.api.RemoteWorkflowService;
import org.dromara.workflow.api.domain.RemoteStartProcess;
import org.dromara.workflow.api.domain.RemoteFlowInstanceBizExt;
@ -53,6 +64,19 @@ import cn.hutool.core.convert.Convert;
@Slf4j
public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplyService {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String PRINT_DATETIME_FORMAT = "yyyy-MM-dd HH:mm";
private static final Map<String, String> TRIP_TYPE_LABELS = Map.of(
"1", "安装调试",
"2", "市场交流",
"3", "展会/会议",
"4", "其他"
);
private final CrmBusinessTripApplyMapper baseMapper;
private final CrmBusinessTripDetailsMapper detailsMapper;
private final ErpProjectInfoMapper erpProjectInfoMapper;
@ -63,6 +87,9 @@ public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplySer
@DubboReference()
private RemoteCodeRuleService remoteCodeRuleService;
@DubboReference
private RemoteUserService remoteUserService;
/**
*
*
@ -381,4 +408,229 @@ public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplySer
baseMapper.updateById(tripApply);
});
}
@Override
public Map<String, Object> buildTripApplyPdfExportData(Long tripId) {
CrmBusinessTripApplyVo apply = queryById(tripId);
if (apply == null) {
throw new ServiceException("出差申请不存在ID" + tripId);
}
if (!"3".equals(apply.getTripStatus())) {
throw new ServiceException("仅申请状态为“已审批”时允许导出出差申请单");
}
CrmBusinessTripApply entity = baseMapper.selectById(tripId);
Map<String, Object> data = new HashMap<>(24);
data.put("审批编号", strVal(apply.getApplyCode()));
data.put("创建人", strVal(apply.getApplicantName()));
data.put("创建人部门", strVal(apply.getDeptName()));
data.put("创建时间", formatDate(entity == null ? null : entity.getCreateTime()));
data.put("审批状态", "已通过");
data.put("行程明细", buildItineraryLoopData(apply));
data.put("审批流程", buildApprovalFlowText(tripId));
data.put("打印时间", DateUtils.parseDateToStr(PRINT_DATETIME_FORMAT, new Date()));
data.put("打印人", resolvePrintUserName());
return data;
}
private List<Map<String, Object>> buildItineraryLoopData(CrmBusinessTripApplyVo apply) {
String tripTypeLabel = resolveTripTypeLabel(apply.getTripType());
List<CrmBusinessTripDetailsVo> details = apply.getCrmBusinessTripDetailsList();
if (details != null && !details.isEmpty()) {
List<Map<String, Object>> rows = new ArrayList<>();
for (CrmBusinessTripDetailsVo detail : details) {
rows.add(toItineraryRow(apply, detail, tripTypeLabel));
}
return rows;
}
return List.of(toItineraryRowFromApply(apply, tripTypeLabel));
}
private Map<String, Object> toItineraryRow(CrmBusinessTripApplyVo apply, CrmBusinessTripDetailsVo detail,
String tripTypeLabel) {
Map<String, Object> row = new HashMap<>(12);
Long order = detail.getItineraryNumber();
row.put("itineraryLabel", order == null ? "行程明细" : "行程明细" + order);
row.put("tripType", tripTypeLabel);
row.put("tripLocation", strVal(detail.getTripLocation()));
row.put("projectName", strVal(detail.getProjectName()));
row.put("projectCode", strVal(detail.getProjectCode()));
row.put("tripReason", firstNonBlank(detail.getTripReason(), apply.getTripReason()));
row.put("startTime", formatDate(detail.getStartTime()));
row.put("endTime", formatDate(detail.getEndTime()));
row.put("durationText", formatDuration(detail.getDurationDays(), apply.getDurationDays()));
return row;
}
private Map<String, Object> toItineraryRowFromApply(CrmBusinessTripApplyVo apply, String tripTypeLabel) {
Map<String, Object> row = new HashMap<>(12);
row.put("itineraryLabel", "行程明细1");
row.put("tripType", tripTypeLabel);
row.put("tripLocation", strVal(apply.getTripLocation()));
row.put("projectName", strVal(apply.getProjectName()));
row.put("projectCode", strVal(apply.getProjectCode()));
row.put("tripReason", strVal(apply.getTripReason()));
row.put("startTime", formatDate(apply.getStartTime()));
row.put("endTime", formatDate(apply.getEndTime()));
row.put("durationText", formatDuration(apply.getDurationDays(), null));
return row;
}
private String buildApprovalFlowText(Long tripId) {
try {
Map<String, Object> flowData = remoteWorkflowService.flowHisTaskList(String.valueOf(tripId));
List<Map<String, Object>> handledTasks = extractHandledTasks(flowData == null ? null : flowData.get("list"));
if (handledTasks.isEmpty()) {
return "";
}
return handledTasks.stream()
.map(this::formatApprovalFlowLine)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining("\n"));
} catch (Exception e) {
log.warn("读取出差申请审批记录失败, tripId={}", tripId, e);
return "";
}
}
private String formatApprovalFlowLine(Map<String, Object> task) {
String message = strVal(task.get("message"));
if (StringUtils.isBlank(message)) {
message = "同意";
}
String approveName = resolveApproveName(task);
String statusLabel = resolveTaskStatusLabel(task);
String time = formatDateTime(parseDate(task.get("updateTime")));
return message + " " + approveName + " " + statusLabel + " " + time;
}
private String resolveTaskStatusLabel(Map<String, Object> task) {
String flowStatusName = strVal(task.get("flowStatusName"));
if (StringUtils.isNotBlank(flowStatusName)) {
return flowStatusName;
}
String nodeName = strVal(task.get("nodeName"));
if (nodeName.contains("抄送")) {
return "已抄送";
}
return "已同意";
}
private String resolveTripTypeLabel(String tripType) {
if (StringUtils.isBlank(tripType)) {
return "";
}
return TRIP_TYPE_LABELS.getOrDefault(tripType, tripType);
}
private String formatDuration(Long detailDays, Long fallbackDays) {
Long days = detailDays != null ? detailDays : fallbackDays;
if (days == null) {
return "";
}
return days + ".0天";
}
private String resolvePrintUserName() {
try {
if (LoginHelper.getLoginUser() != null && StringUtils.isNotBlank(LoginHelper.getLoginUser().getNickname())) {
return LoginHelper.getLoginUser().getNickname();
}
} catch (Exception ignored) {
// ignore
}
return LoginHelper.getUsername();
}
private List<Map<String, Object>> extractHandledTasks(Object listObj) {
if (!(listObj instanceof List<?> taskList) || taskList.isEmpty()) {
return Collections.emptyList();
}
List<Map<String, Object>> handledTasks = new ArrayList<>();
for (Object taskObj : taskList) {
Map<String, Object> taskMap = toMap(taskObj);
if (!taskMap.isEmpty() && taskMap.get("updateTime") != null) {
handledTasks.add(taskMap);
}
}
handledTasks.sort(Comparator.comparing(task -> {
Date updateTime = parseDate(task.get("updateTime"));
return updateTime == null ? new Date(0L) : updateTime;
}));
return handledTasks;
}
private String resolveApproveName(Map<String, Object> task) {
String approveName = strVal(task.get("approveName"));
if (StringUtils.isNotBlank(approveName)) {
return approveName;
}
String approver = strVal(task.get("approver"));
if (StringUtils.isBlank(approver)) {
return "";
}
try {
String nickname = remoteUserService.selectNicknameByIds(approver);
return StringUtils.isNotBlank(nickname) ? nickname : approver;
} catch (Exception e) {
log.warn("审批人昵称解析失败, approver={}", approver, e);
return approver;
}
}
private Map<String, Object> toMap(Object obj) {
if (obj == null) {
return Collections.emptyMap();
}
if (obj instanceof Map<?, ?> rawMap) {
Map<String, Object> result = new HashMap<>(rawMap.size());
for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
result.put(String.valueOf(entry.getKey()), entry.getValue());
}
return result;
}
return BeanUtil.beanToMap(obj);
}
private Date parseDate(Object rawDate) {
if (rawDate == null) {
return null;
}
if (rawDate instanceof Date date) {
return date;
}
if (rawDate instanceof Number number) {
long millis = number.longValue();
if (String.valueOf(millis).length() == 10) {
millis = millis * 1000;
}
return new Date(millis);
}
return DateUtils.parseDate(rawDate);
}
private String formatDate(Date date) {
if (date == null) {
return "";
}
return DateUtils.parseDateToStr(DATE_FORMAT, date);
}
private String formatDateTime(Date date) {
if (date == null) {
return "";
}
return DateUtils.parseDateToStr(DATETIME_FORMAT, date);
}
private String firstNonBlank(String primary, String fallback) {
if (StringUtils.isNotBlank(primary)) {
return primary;
}
return strVal(fallback);
}
private String strVal(Object value) {
return value == null ? "" : String.valueOf(value);
}
}

Loading…
Cancel
Save