调整项目
This commit is contained in:
+17
@@ -48,6 +48,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目立项 控制器
|
||||
@@ -70,6 +71,22 @@ public class ProjectApplyController extends BladeController {
|
||||
return R.data(projectApplyService.detail(id));
|
||||
}
|
||||
|
||||
@GetMapping("/fund-risk-stats")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "资金使用风险统计", description = "传入项目筛选条件")
|
||||
public R<Map<String, Integer>> fundRiskStats(ProjectApplyVO projectApply) {
|
||||
return R.data(projectApplyService.fundRiskStats(projectApply));
|
||||
}
|
||||
|
||||
@GetMapping("/change-record/detail")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "变更记录详情", description = "传入项目ID和变更记录序号")
|
||||
public R<Map<String, Object>> changeRecordDetail(
|
||||
@Parameter(description = "项目ID", required = true) @RequestParam Long id,
|
||||
@Parameter(description = "变更记录序号,从0开始", required = true) @RequestParam Integer recordIndex) {
|
||||
return R.data(projectApplyService.changeRecordDetail(id, recordIndex));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "分页", description = "传入projectApply")
|
||||
|
||||
+3
@@ -29,6 +29,7 @@ import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.vo.ProjectApplyVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目立项 服务类
|
||||
@@ -39,6 +40,8 @@ public interface IProjectApplyService extends BaseService<ProjectApply> {
|
||||
|
||||
IPage<ProjectApplyVO> selectProjectApplyPage(IPage<ProjectApply> page, ProjectApplyVO projectApply);
|
||||
ProjectApplyVO detail(Long id);
|
||||
Map<String, Integer> fundRiskStats(ProjectApplyVO projectApply);
|
||||
Map<String, Object> changeRecordDetail(Long id, Integer recordIndex);
|
||||
boolean saveDraft(ProjectApply projectApply);
|
||||
boolean submit(ProjectApply projectApply);
|
||||
boolean submitApproval(Long id);
|
||||
|
||||
+307
-4
@@ -25,16 +25,24 @@ package org.springblade.transport.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.transport.excel.ProjectApplyExcel;
|
||||
import org.springblade.transport.mapper.ProjectApplyMapper;
|
||||
import org.springblade.transport.mapper.FormalSettlementMapper;
|
||||
import org.springblade.transport.mapper.ReceiptClaimSettlementMapper;
|
||||
import org.springblade.transport.mapper.TemporaryCreditLimitMapper;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.FormalSettlement;
|
||||
import org.springblade.transport.pojo.entity.ReceiptClaimSettlement;
|
||||
import org.springblade.transport.pojo.entity.TemporaryCreditLimit;
|
||||
import org.springblade.transport.pojo.vo.ProjectApplyVO;
|
||||
import org.springblade.transport.service.IProjectApplyService;
|
||||
import org.springblade.transport.support.TransportBusinessSupport;
|
||||
@@ -47,9 +55,14 @@ import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 项目立项 服务实现类
|
||||
@@ -57,6 +70,7 @@ import java.util.Objects;
|
||||
* @author Chill
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper, ProjectApply> implements IProjectApplyService {
|
||||
|
||||
private static final String STATUS_DRAFT = "draft";
|
||||
@@ -68,15 +82,21 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
private static final String STATUS_VOIDED = "voided";
|
||||
private static final String EFFECTIVE_TEMPORARY = "temporary";
|
||||
private static final String EFFECTIVE_FORMAL = "formal";
|
||||
private static final String STATUS_SETTLEMENT_APPROVED = "approved";
|
||||
private static final String CHANGE_TYPE_PROJECT = "项目变更";
|
||||
private static final String CHANGE_TYPE_RECORD = "项目备案调整";
|
||||
private static final String CHANGE_TYPE_DRAFT = "草稿编辑";
|
||||
private static final DateTimeFormatter CODE_DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
private static final DateTimeFormatter PROJECT_YEAR = DateTimeFormatter.ofPattern("yyyy");
|
||||
private final TemporaryCreditLimitMapper temporaryCreditLimitMapper;
|
||||
private final FormalSettlementMapper formalSettlementMapper;
|
||||
private final ReceiptClaimSettlementMapper receiptClaimSettlementMapper;
|
||||
|
||||
@Override
|
||||
public IPage<ProjectApplyVO> selectProjectApplyPage(IPage<ProjectApply> page, ProjectApplyVO projectApply) {
|
||||
IPage<ProjectApply> entityPage = page(page, buildQuery(projectApply));
|
||||
IPage<ProjectApplyVO> voPage = ProjectApplyWrapper.build().pageVO(entityPage);
|
||||
fillFundUseRisk(voPage.getRecords());
|
||||
voPage.getRecords().forEach(this::fillReadonly);
|
||||
return voPage;
|
||||
}
|
||||
@@ -89,25 +109,64 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
return projectApplyVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> fundRiskStats(ProjectApplyVO projectApply) {
|
||||
projectApply.setFundUseRisk(null);
|
||||
List<ProjectApplyVO> records = list(buildQuery(projectApply)).stream()
|
||||
.map(record -> ProjectApplyWrapper.build().entityVO(record))
|
||||
.toList();
|
||||
fillFundUseRisk(records);
|
||||
Map<String, Integer> stats = new LinkedHashMap<>();
|
||||
stats.put("high", (int) records.stream().filter(item -> "high".equals(item.getFundUseRisk())).count());
|
||||
stats.put("medium", (int) records.stream().filter(item -> "medium".equals(item.getFundUseRisk())).count());
|
||||
stats.put("none", (int) records.stream().filter(item -> "none".equals(item.getFundUseRisk())).count());
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> changeRecordDetail(Long id, Integer recordIndex) {
|
||||
if (recordIndex == null || recordIndex < 0) {
|
||||
throw new ServiceException("变更记录序号不能为空");
|
||||
}
|
||||
ProjectApply projectApply = loadExists(id);
|
||||
List<Map<String, Object>> records = parseChangeRecords(projectApply.getChangeRecordJson());
|
||||
if (recordIndex >= records.size()) {
|
||||
throw new ServiceException("变更记录不存在");
|
||||
}
|
||||
return records.get(recordIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveDraft(ProjectApply projectApply) {
|
||||
prepare(projectApply);
|
||||
ProjectApply beforeRecord = null;
|
||||
if (Func.isNotEmpty(projectApply.getId())) {
|
||||
loadEditable(projectApply.getId(), true);
|
||||
beforeRecord = loadEditable(projectApply.getId(), true);
|
||||
}
|
||||
prepare(projectApply);
|
||||
prepareCreateOrUpdate(projectApply);
|
||||
projectApply.setApprovalStatus(resolveEditableStatus(projectApply));
|
||||
if (beforeRecord != null) {
|
||||
projectApply.setChangeRecordJson(beforeRecord.getChangeRecordJson());
|
||||
}
|
||||
if (beforeRecord != null && Objects.equals(beforeRecord.getApprovalStatus(), STATUS_DRAFT)) {
|
||||
appendProjectChangeRecord(projectApply, CHANGE_TYPE_DRAFT, null, STATUS_DRAFT, "草稿",
|
||||
buildProjectChangeSnapshot(beforeRecord), buildProjectChangeSnapshot(projectApply));
|
||||
}
|
||||
return saveOrUpdate(projectApply);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(ProjectApply projectApply) {
|
||||
ProjectApply beforeRecord = null;
|
||||
if (Func.isNotEmpty(projectApply.getId())) {
|
||||
beforeRecord = loadEditable(projectApply.getId(), true);
|
||||
}
|
||||
prepare(projectApply);
|
||||
validateSubmit(projectApply);
|
||||
if (Func.isNotEmpty(projectApply.getId())) {
|
||||
loadEditable(projectApply.getId(), true);
|
||||
if (beforeRecord != null) {
|
||||
projectApply.setChangeRecordJson(beforeRecord.getChangeRecordJson());
|
||||
}
|
||||
prepareCreateOrUpdate(projectApply);
|
||||
projectApply.setApprovalStatus(STATUS_REVIEWING);
|
||||
@@ -186,10 +245,13 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveChange(ProjectApplyVO projectApply) {
|
||||
ProjectApply oldRecord = loadChangeable(projectApply.getId());
|
||||
Map<String, Object> beforeData = buildProjectChangeSnapshot(oldRecord);
|
||||
prepare(projectApply);
|
||||
copyChangeFields(oldRecord, projectApply);
|
||||
validateSubmit(oldRecord);
|
||||
validateChangeLength(oldRecord);
|
||||
Map<String, Object> afterData = buildProjectChangeSnapshot(oldRecord);
|
||||
appendProjectChangeRecord(oldRecord, CHANGE_TYPE_PROJECT, oldRecord.getChangeReason(), "saved", "已保存", beforeData, afterData);
|
||||
return updateById(oldRecord);
|
||||
}
|
||||
|
||||
@@ -197,10 +259,14 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submitChange(ProjectApplyVO projectApply) {
|
||||
ProjectApply oldRecord = loadChangeable(projectApply.getId());
|
||||
Map<String, Object> beforeData = buildProjectChangeSnapshot(oldRecord);
|
||||
prepare(projectApply);
|
||||
copyChangeFields(oldRecord, projectApply);
|
||||
validateSubmit(oldRecord);
|
||||
validateChange(oldRecord, projectApply.getChangeType());
|
||||
Map<String, Object> afterData = buildProjectChangeSnapshot(oldRecord);
|
||||
appendProjectChangeRecord(oldRecord, projectApply.getChangeType(), oldRecord.getChangeReason(),
|
||||
STATUS_CHANGE_REVIEWING, "变更审批中", beforeData, afterData);
|
||||
oldRecord.setApprovalStatus(STATUS_CHANGE_REVIEWING);
|
||||
oldRecord.setCurrentNode(projectApply.getChangeType() + "审批");
|
||||
oldRecord.setCurrentProcessor("待处理");
|
||||
@@ -271,7 +337,10 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
queryWrapper.eq(ProjectApply::getUndertakeDeptId, projectApply.getUndertakeDeptId());
|
||||
}
|
||||
queryWrapper.like(Func.isNotEmpty(projectApply.getApplyNo()), ProjectApply::getApplyNo, projectApply.getApplyNo())
|
||||
.like(Func.isNotEmpty(projectApply.getProjectCode()), ProjectApply::getProjectCode, projectApply.getProjectCode())
|
||||
.like(Func.isNotEmpty(projectApply.getProjectName()), ProjectApply::getProjectName, projectApply.getProjectName())
|
||||
.like(Func.isNotEmpty(projectApply.getCustomerNames()), ProjectApply::getCustomerNames, projectApply.getCustomerNames())
|
||||
.like(Func.isNotEmpty(projectApply.getCarrierNames()), ProjectApply::getCarrierNames, projectApply.getCarrierNames())
|
||||
.like(Func.isNotEmpty(projectApply.getUndertakeDeptName()), ProjectApply::getUndertakeDeptName, projectApply.getUndertakeDeptName())
|
||||
.like(Func.isNotEmpty(projectApply.getPrincipalUserName()), ProjectApply::getPrincipalUserName, projectApply.getPrincipalUserName())
|
||||
.eq(Func.isNotEmpty(projectApply.getProjectType()), ProjectApply::getProjectType, projectApply.getProjectType())
|
||||
@@ -292,9 +361,84 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
queryWrapper.eq(ProjectApply::getEffectiveType, EFFECTIVE_FORMAL)
|
||||
.in(ProjectApply::getApprovalStatus, STATUS_APPROVED, "change_approved");
|
||||
}
|
||||
if (Func.isNotEmpty(projectApply.getFundUseRisk())) {
|
||||
List<ProjectApplyVO> riskRecords = baseMapper.selectList(queryWrapper).stream()
|
||||
.map(record -> ProjectApplyWrapper.build().entityVO(record))
|
||||
.toList();
|
||||
fillFundUseRisk(riskRecords);
|
||||
List<Long> matchedIds = riskRecords.stream()
|
||||
.filter(record -> Objects.equals(record.getFundUseRisk(), projectApply.getFundUseRisk()))
|
||||
.map(ProjectApply::getId)
|
||||
.toList();
|
||||
queryWrapper.in(ProjectApply::getId, matchedIds.isEmpty() ? List.of(-1L) : matchedIds);
|
||||
}
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
private void fillFundUseRisk(List<ProjectApplyVO> records) {
|
||||
if (records == null || records.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<Long> projectIds = records.stream()
|
||||
.map(ProjectApply::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (projectIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Long, BigDecimal> approvedTemporaryLimits = new HashMap<>();
|
||||
temporaryCreditLimitMapper.selectList(Wrappers.<TemporaryCreditLimit>lambdaQuery()
|
||||
.in(TemporaryCreditLimit::getProjectId, projectIds)
|
||||
.eq(TemporaryCreditLimit::getApprovalStatus, STATUS_SETTLEMENT_APPROVED)
|
||||
.eq(TemporaryCreditLimit::getIsDeleted, 0))
|
||||
.forEach(item -> approvedTemporaryLimits.merge(item.getProjectId(), nonNegative(item.getApplyLimit()), BigDecimal::add));
|
||||
|
||||
List<FormalSettlement> approvedReceivables = formalSettlementMapper.selectList(Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.in(FormalSettlement::getProjectId, projectIds)
|
||||
.eq(FormalSettlement::getSettlementType, "receivable")
|
||||
.eq(FormalSettlement::getApprovalStatus, STATUS_SETTLEMENT_APPROVED)
|
||||
.eq(FormalSettlement::getIsDeleted, 0));
|
||||
Map<Long, Long> settlementProjects = approvedReceivables.stream()
|
||||
.filter(item -> item.getId() != null && item.getProjectId() != null)
|
||||
.collect(Collectors.toMap(FormalSettlement::getId, FormalSettlement::getProjectId, (left, right) -> left));
|
||||
Map<Long, BigDecimal> usedFundLimits = new HashMap<>();
|
||||
if (!settlementProjects.isEmpty()) {
|
||||
receiptClaimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.in(ReceiptClaimSettlement::getFormalSettlementId, settlementProjects.keySet())
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.eq(ReceiptClaimSettlement::getIsDeleted, 0))
|
||||
.forEach(item -> {
|
||||
Long projectId = settlementProjects.get(item.getFormalSettlementId());
|
||||
if (projectId != null) {
|
||||
usedFundLimits.merge(projectId, nonNegative(item.getAllocatedReceiptAmount()), BigDecimal::add);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
records.forEach(project -> {
|
||||
BigDecimal projectLimit = nonNegative(project.getFundLimit());
|
||||
BigDecimal temporaryLimit = approvedTemporaryLimits.getOrDefault(project.getId(), BigDecimal.ZERO);
|
||||
BigDecimal maxFundLimit = projectLimit.max(temporaryLimit);
|
||||
BigDecimal usedFundLimit = usedFundLimits.getOrDefault(project.getId(), BigDecimal.ZERO);
|
||||
BigDecimal fundUseRate = maxFundLimit.signum() == 0
|
||||
? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
|
||||
: usedFundLimit.multiply(BigDecimal.valueOf(100)).divide(maxFundLimit, 2, RoundingMode.HALF_UP);
|
||||
String risk = fundUseRate.compareTo(new BigDecimal("90")) >= 0 ? "high"
|
||||
: fundUseRate.compareTo(new BigDecimal("80")) >= 0 ? "medium" : "none";
|
||||
project.setUsedFundLimit(usedFundLimit);
|
||||
project.setMaxFundLimit(maxFundLimit);
|
||||
project.setFundUseRate(fundUseRate);
|
||||
project.setFundUseRisk(risk);
|
||||
project.setFundUseRiskName("high".equals(risk) ? "高风险" : "medium".equals(risk) ? "中风险" : "无风险");
|
||||
});
|
||||
}
|
||||
|
||||
private BigDecimal nonNegative(BigDecimal value) {
|
||||
return value == null || value.signum() < 0 ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
private void prepareCreateOrUpdate(ProjectApply projectApply) {
|
||||
if (Func.isEmpty(projectApply.getId())) {
|
||||
// 新增项目不产生变更记录,避免客户端误传变更字段导致记录回显。
|
||||
@@ -414,6 +558,146 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
target.setChangeReason(TransportBusinessSupport.trimToNull(source.getChangeReason()));
|
||||
}
|
||||
|
||||
private Map<String, Object> buildProjectChangeSnapshot(ProjectApply projectApply) {
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("projectType", projectApply.getProjectType());
|
||||
data.put("projectName", projectApply.getProjectName());
|
||||
data.put("projectShortName", projectApply.getProjectShortName());
|
||||
data.put("businessDeptName", projectApply.getBusinessDeptName());
|
||||
data.put("undertakeDeptName", projectApply.getUndertakeDeptName());
|
||||
data.put("projectSource", projectApply.getProjectSource());
|
||||
data.put("sourceRemark", projectApply.getSourceRemark());
|
||||
data.put("fundLimit", projectApply.getFundLimit());
|
||||
data.put("receivableLimit", projectApply.getReceivableLimit());
|
||||
data.put("receivableDays", projectApply.getReceivableDays());
|
||||
data.put("paymentDays", projectApply.getPaymentDays());
|
||||
data.put("cargoType", projectApply.getCargoType());
|
||||
data.put("cargoQuantity", projectApply.getCargoQuantity());
|
||||
data.put("businessStartDate", projectApply.getBusinessStartDate());
|
||||
data.put("businessEndDate", projectApply.getBusinessEndDate());
|
||||
data.put("transportRoute", projectApply.getTransportRoute());
|
||||
data.put("transportType", projectApply.getTransportType());
|
||||
data.put("businessType", projectApply.getBusinessType());
|
||||
data.put("businessMode", projectApply.getBusinessMode());
|
||||
data.put("projectScale", projectApply.getProjectScale());
|
||||
data.put("settlementMode", projectApply.getSettlementMode());
|
||||
data.put("estimatedProfit", projectApply.getEstimatedProfit());
|
||||
data.put("profitRate", projectApply.getProfitRate());
|
||||
data.put("fundDemand", projectApply.getFundDemand());
|
||||
data.put("handlerUserName", projectApply.getHandlerUserName());
|
||||
data.put("principalUserName", projectApply.getPrincipalUserName());
|
||||
data.put("customerNames", projectApply.getCustomerNames());
|
||||
data.put("carrierNames", projectApply.getCarrierNames());
|
||||
data.put("situationRemark", normalizeProjectSnapshotJson(projectApply.getSituationRemark()));
|
||||
data.put("attachmentsJson", normalizeProjectSnapshotJson(projectApply.getAttachmentsJson()));
|
||||
return data;
|
||||
}
|
||||
|
||||
private String normalizeProjectSnapshotJson(String value) {
|
||||
if (Func.isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
String text = value.trim();
|
||||
try {
|
||||
Object parsed = JsonUtil.parse(text, Object.class);
|
||||
if (parsed instanceof List<?> list && list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (parsed instanceof Map<?, ?> map && map.values().stream().allMatch(this::isEmptyProjectSnapshotValue)) {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 非JSON文本按原值参与差异比较。
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private boolean isEmptyProjectSnapshotValue(Object value) {
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
if (value instanceof String text) {
|
||||
return text.isBlank();
|
||||
}
|
||||
if (value instanceof List<?> list) {
|
||||
return list.isEmpty();
|
||||
}
|
||||
if (value instanceof Map<?, ?> map) {
|
||||
return map.isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void appendProjectChangeRecord(ProjectApply projectApply, String changeType, String changeReason,
|
||||
String status, String statusName, Map<String, Object> beforeData,
|
||||
Map<String, Object> afterData) {
|
||||
retainChangedSnapshotFields(beforeData, afterData);
|
||||
if (beforeData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<Map<String, Object>> records = parseChangeRecords(projectApply.getChangeRecordJson());
|
||||
Map<String, Object> record = new LinkedHashMap<>();
|
||||
record.put("changeDate", LocalDate.now().toString());
|
||||
record.put("handlerUserId", AuthUtil.getUserId());
|
||||
record.put("handlerUserName", AuthUtil.getUserName());
|
||||
record.put("changeType", changeType);
|
||||
record.put("changeContent", buildProjectChangeContent(beforeData, afterData));
|
||||
record.put("changeReason", TransportBusinessSupport.trimToNull(changeReason));
|
||||
record.put("status", status);
|
||||
record.put("statusName", statusName);
|
||||
record.put("changedFields", new ArrayList<>(beforeData.keySet()));
|
||||
record.put("beforeData", beforeData);
|
||||
record.put("afterData", afterData);
|
||||
records.add(record);
|
||||
projectApply.setChangeRecordJson(JsonUtil.toJson(records));
|
||||
}
|
||||
|
||||
private void retainChangedSnapshotFields(Map<String, Object> beforeData, Map<String, Object> afterData) {
|
||||
List<String> unchangedFields = beforeData.entrySet().stream()
|
||||
.filter(entry -> Objects.equals(entry.getValue(), afterData.get(entry.getKey())))
|
||||
.map(Map.Entry::getKey)
|
||||
.toList();
|
||||
unchangedFields.forEach(field -> {
|
||||
beforeData.remove(field);
|
||||
afterData.remove(field);
|
||||
});
|
||||
}
|
||||
|
||||
private String buildProjectChangeContent(Map<String, Object> beforeData, Map<String, Object> afterData) {
|
||||
return beforeData.keySet().stream()
|
||||
.map(field -> "【" + projectChangeFieldLabel(field) + "】从【"
|
||||
+ formatProjectChangeValue(beforeData.get(field)) + "】调整为【"
|
||||
+ formatProjectChangeValue(afterData.get(field)) + "】")
|
||||
.collect(Collectors.joining(";"));
|
||||
}
|
||||
|
||||
private String projectChangeFieldLabel(String field) {
|
||||
Map<String, String> labels = Map.ofEntries(
|
||||
Map.entry("projectType", "项目类型"), Map.entry("projectName", "项目名称"),
|
||||
Map.entry("projectShortName", "项目简称"), Map.entry("businessDeptName", "业务部门"),
|
||||
Map.entry("undertakeDeptName", "承办部门"), Map.entry("projectSource", "项目由来"),
|
||||
Map.entry("sourceRemark", "项目由来说明"), Map.entry("fundLimit", "项目资金使用额度"),
|
||||
Map.entry("receivableLimit", "项目应收账款额度"), Map.entry("receivableDays", "应收账款回款期限"),
|
||||
Map.entry("paymentDays", "回款账期"), Map.entry("cargoType", "货物类型"),
|
||||
Map.entry("cargoQuantity", "预估货物数量"), Map.entry("businessStartDate", "业务周期起"),
|
||||
Map.entry("businessEndDate", "业务周期止"), Map.entry("transportRoute", "运输线路"),
|
||||
Map.entry("transportType", "运输类型"), Map.entry("businessType", "业务类型"),
|
||||
Map.entry("businessMode", "业务模式"), Map.entry("projectScale", "项目规模"),
|
||||
Map.entry("settlementMode", "结算方式"), Map.entry("estimatedProfit", "预计利润"),
|
||||
Map.entry("profitRate", "利润率"), Map.entry("fundDemand", "履约保证金"),
|
||||
Map.entry("handlerUserName", "项目经办人"), Map.entry("principalUserName", "项目负责人"),
|
||||
Map.entry("customerNames", "客户名称"), Map.entry("carrierNames", "下游承运商"),
|
||||
Map.entry("situationRemark", "项目情况说明"), Map.entry("attachmentsJson", "项目材料"));
|
||||
return labels.getOrDefault(field, field);
|
||||
}
|
||||
|
||||
private String formatProjectChangeValue(Object value) {
|
||||
if (value == null || (value instanceof String text && text.isBlank())) {
|
||||
return "空";
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
private void validateDraft(ProjectApply projectApply) {
|
||||
TransportBusinessSupport.validateRequired(projectApply.getProjectType(), "请选择项目类型");
|
||||
TransportBusinessSupport.validateRequired(projectApply.getProjectName(), "请输入项目名称");
|
||||
@@ -563,6 +847,25 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
return value == null ? null : value.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Map<String, Object>> parseChangeRecords(String value) {
|
||||
if (Func.isEmpty(value)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
Object records = JsonUtil.parse(value, Object.class);
|
||||
if (records instanceof List<?> list) {
|
||||
return (List<Map<String, Object>>) (List<?>) list;
|
||||
}
|
||||
if (records instanceof Map<?, ?> map) {
|
||||
return new ArrayList<>(List.of((Map<String, Object>) map));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 历史记录JSON损坏时从空列表继续,避免影响项目保存。
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private void fillReadonly(ProjectApplyVO projectApplyVO) {
|
||||
projectApplyVO.setReadonly(!canCurrentUserOperate(projectApplyVO));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user