|
|
|
|
@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|