This commit is contained in:
2026-08-11 08:43:22 +08:00
parent 0ec904f68d
commit 1029bcd209
10 changed files with 258 additions and 72 deletions
@@ -57,6 +57,15 @@ public class CustomerChangeRecord extends TenantEntity {
@Schema(description = "变更内容") @Schema(description = "变更内容")
private String changeContent; private String changeContent;
@Schema(description = "变更字段,多个字段以逗号分隔")
private String changedFields;
@Schema(description = "变更前数据JSON")
private String beforeData;
@Schema(description = "变更后数据JSON")
private String afterData;
@Schema(description = "变更账号") @Schema(description = "变更账号")
private String changeUserName; private String changeUserName;
@@ -43,6 +43,7 @@ import org.springblade.core.tool.utils.Func;
import org.springblade.transport.excel.CustomerArchiveExcel; import org.springblade.transport.excel.CustomerArchiveExcel;
import org.springblade.transport.pojo.entity.CustomerArchive; import org.springblade.transport.pojo.entity.CustomerArchive;
import org.springblade.transport.pojo.vo.CustomerArchiveVO; import org.springblade.transport.pojo.vo.CustomerArchiveVO;
import org.springblade.transport.pojo.vo.CustomerChangeRecordVO;
import org.springblade.transport.pojo.vo.CustomerCreditScoreVO; import org.springblade.transport.pojo.vo.CustomerCreditScoreVO;
import org.springblade.transport.service.ICustomerArchiveService; import org.springblade.transport.service.ICustomerArchiveService;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -89,6 +90,17 @@ public class CustomerArchiveController extends BladeController {
return R.data(pages); return R.data(pages);
} }
/**
* 变更记录分页
*/
@GetMapping("/change-record/list")
@ApiOperationSupport(order = 3)
@Operation(summary = "客商变更记录分页", description = "传入客商ID")
public R<IPage<CustomerChangeRecordVO>> changeRecordList(
@Parameter(description = "客商ID", required = true) @RequestParam Long customerId, Query query) {
return R.data(customerArchiveService.selectChangeRecordPage(Condition.getPage(query), customerId));
}
/** /**
* 新增或修改 * 新增或修改
*/ */
@@ -144,8 +144,8 @@ public class ProjectApplyController extends BladeController {
@ApiOperationSupport(order = 12) @ApiOperationSupport(order = 12)
@Operation(summary = "发起变更", description = "传入id、changeContent和changeReason") @Operation(summary = "发起变更", description = "传入id、changeContent和changeReason")
public R startChange(@Parameter(description = "主键", required = true) @RequestParam Long id, public R startChange(@Parameter(description = "主键", required = true) @RequestParam Long id,
@RequestParam String changeContent, @RequestParam(required = false) String changeContent,
@RequestParam String changeReason) { @RequestParam(required = false) String changeReason) {
return R.status(projectApplyService.startChange(id, changeContent, changeReason)); return R.status(projectApplyService.startChange(id, changeContent, changeReason));
} }
@@ -28,6 +28,7 @@ import org.springblade.core.mp.base.BaseService;
import org.springblade.transport.excel.CustomerArchiveExcel; import org.springblade.transport.excel.CustomerArchiveExcel;
import org.springblade.transport.pojo.entity.CustomerArchive; import org.springblade.transport.pojo.entity.CustomerArchive;
import org.springblade.transport.pojo.vo.CustomerArchiveVO; import org.springblade.transport.pojo.vo.CustomerArchiveVO;
import org.springblade.transport.pojo.vo.CustomerChangeRecordVO;
import org.springblade.transport.pojo.vo.CustomerCreditScoreVO; import org.springblade.transport.pojo.vo.CustomerCreditScoreVO;
import java.util.List; import java.util.List;
@@ -48,6 +49,15 @@ public interface ICustomerArchiveService extends BaseService<CustomerArchive> {
*/ */
IPage<CustomerArchiveVO> selectCustomerArchivePage(IPage<CustomerArchiveVO> page, CustomerArchiveVO customer); IPage<CustomerArchiveVO> selectCustomerArchivePage(IPage<CustomerArchiveVO> page, CustomerArchiveVO customer);
/**
* 客商变更记录分页
*
* @param page 分页参数
* @param customerId 客商ID
* @return 变更记录分页
*/
IPage<CustomerChangeRecordVO> selectChangeRecordPage(IPage<CustomerChangeRecordVO> page, Long customerId);
/** /**
* 聚合详情 * 聚合详情
* *
@@ -27,6 +27,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springblade.core.log.exception.ServiceException; import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.core.mp.base.BaseServiceImpl;
@@ -122,33 +123,46 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
return page.setRecords(baseMapper.selectCustomerArchivePage(page, customer, AuthUtil.getUserId())); return page.setRecords(baseMapper.selectCustomerArchivePage(page, customer, AuthUtil.getUserId()));
} }
@Override
public IPage<CustomerChangeRecordVO> selectChangeRecordPage(IPage<CustomerChangeRecordVO> page, Long customerId) {
if (Func.isEmpty(customerId)) {
throw new ServiceException("客商ID不能为空");
}
ensureCustomerAccessible(customerId);
IPage<CustomerChangeRecord> recordPage = changeRecordMapper.selectPage(
new Page<>(page.getCurrent(), page.getSize()),
Wrappers.<CustomerChangeRecord>lambdaQuery()
.eq(CustomerChangeRecord::getCustomerId, customerId)
.eq(CustomerChangeRecord::getIsDeleted, 0)
.orderByDesc(CustomerChangeRecord::getChangeTime));
page.setTotal(recordPage.getTotal());
return page.setRecords(recordPage.getRecords().stream()
.map(record -> Objects.requireNonNull(BeanUtil.copyProperties(record, CustomerChangeRecordVO.class)))
.toList());
}
@Override @Override
public CustomerArchiveVO detail(Long id) { public CustomerArchiveVO detail(Long id) {
if (Func.isEmpty(id)) { if (Func.isEmpty(id)) {
throw new ServiceException("主键不能为空"); throw new ServiceException("主键不能为空");
} }
CustomerArchive customer = getById(id); CustomerArchive customer = ensureCustomerAccessible(id);
if (Func.isEmpty(customer) || Objects.equals(customer.getIsDeleted(), 1)) {
throw new ServiceException("客商档案不存在");
}
if (baseMapper.selectCustomerPermissionCount(id, AuthUtil.getUserId()) == 0) {
throw new ServiceException("无权访问该客商档案");
}
CustomerArchiveVO detail = Objects.requireNonNull(BeanUtil.copyProperties(customer, CustomerArchiveVO.class)); CustomerArchiveVO detail = Objects.requireNonNull(BeanUtil.copyProperties(customer, CustomerArchiveVO.class));
detail.setContacts(loadContacts(id)); detail.setContacts(loadContacts(id));
detail.setReceiptAccounts(loadReceiptAccounts(id)); detail.setReceiptAccounts(loadReceiptAccounts(id));
detail.setInvoices(loadInvoices(id)); detail.setInvoices(loadInvoices(id));
detail.setScores(loadScores(id)); detail.setScores(loadScores(id));
detail.setChangeRecords(loadChangeRecords(id));
return detail; return detail;
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean submit(CustomerArchiveVO customer) { public boolean submit(CustomerArchiveVO customer) {
CustomerArchiveVO beforeDetail = Func.isEmpty(customer.getId()) ? null : detail(customer.getId());
prepare(customer); prepare(customer);
validateBase(customer); validateBase(customer);
boolean created = Func.isEmpty(customer.getId()); boolean created = Func.isEmpty(customer.getId());
CustomerArchive before = created ? null : ensureCustomerAccessible(customer.getId());
if (created) { if (created) {
customer.setCustomerCode(nextCustomerCode()); customer.setCustomerCode(nextCustomerCode());
} }
@@ -159,7 +173,9 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
if (created && result) { if (created && result) {
grantNewCustomerToIncludedUsers(entity); grantNewCustomerToIncludedUsers(entity);
} }
addChangeRecord(entity.getId(), created ? "新增客商档案" : "修改客商档案"); if (result) {
addChangeRecord(entity.getId(), created ? "新增客商档案" : "修改客商档案", before, entity, beforeDetail, customer);
}
return result; return result;
} }
@@ -170,32 +186,28 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
prepare(detail); prepare(detail);
validateBase(detail); validateBase(detail);
validateApproval(detail); validateApproval(detail);
CustomerArchive update = new CustomerArchive(); CustomerArchive before = Objects.requireNonNull(BeanUtil.copyProperties(detail, CustomerArchive.class));
update.setId(id); CustomerArchive after = copyCustomer(before);
update.setApprovalStatus(APPROVAL_REVIEWING); after.setApprovalStatus(APPROVAL_REVIEWING);
update.setCurrentNode("客商准入审批"); after.setCurrentNode("客商准入审批");
update.setCurrentProcessor("待处理"); after.setCurrentProcessor("待处理");
addChangeRecord(id, "提交客商准入审批"); addChangeRecord(id, "提交客商准入审批", before, after);
return updateById(update); return updateById(after);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean withdrawApproval(Long id) { public boolean withdrawApproval(Long id) {
CustomerArchive customer = getById(id); CustomerArchive customer = ensureCustomerAccessible(id);
if (Func.isEmpty(customer) || Objects.equals(customer.getIsDeleted(), 1)) {
throw new ServiceException("客商档案不存在");
}
if (!List.of(APPROVAL_DRAFT, APPROVAL_REVIEWING).contains(customer.getApprovalStatus())) { if (!List.of(APPROVAL_DRAFT, APPROVAL_REVIEWING).contains(customer.getApprovalStatus())) {
throw new ServiceException("仅未审核或审核中状态客商可撤回"); throw new ServiceException("仅未审核或审核中状态客商可撤回");
} }
CustomerArchive update = new CustomerArchive(); CustomerArchive after = copyCustomer(customer);
update.setId(id); after.setApprovalStatus(APPROVAL_DRAFT);
update.setApprovalStatus(APPROVAL_DRAFT); after.setCurrentNode("草稿");
update.setCurrentNode("草稿"); after.setCurrentProcessor(AuthUtil.getUserName());
update.setCurrentProcessor(AuthUtil.getUserName()); addChangeRecord(id, "撤回客商准入审批", customer, after);
addChangeRecord(id, "撤回客商准入审批"); return updateById(after);
return updateById(update);
} }
@Override @Override
@@ -205,27 +217,27 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
prepare(detail); prepare(detail);
validateBase(detail); validateBase(detail);
validateApproval(detail); validateApproval(detail);
CustomerArchive update = new CustomerArchive(); CustomerArchive before = Objects.requireNonNull(BeanUtil.copyProperties(detail, CustomerArchive.class));
update.setId(id); CustomerArchive after = copyCustomer(before);
update.setAccessType(ACCESS_FORMAL); after.setAccessType(ACCESS_FORMAL);
update.setApprovalStatus(APPROVAL_APPROVED); after.setApprovalStatus(APPROVAL_APPROVED);
update.setCurrentNode("审核通过"); after.setCurrentNode("审核通过");
update.setCurrentProcessor(AuthUtil.getUserName()); after.setCurrentProcessor(AuthUtil.getUserName());
update.setApprovedTime(LocalDateTime.now()); after.setApprovedTime(LocalDateTime.now());
addChangeRecord(id, "客商准入审核通过"); addChangeRecord(id, "客商准入审核通过", before, after);
return updateById(update); return updateById(after);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean reject(Long id) { public boolean reject(Long id) {
CustomerArchive update = new CustomerArchive(); CustomerArchive before = ensureCustomerAccessible(id);
update.setId(id); CustomerArchive after = copyCustomer(before);
update.setApprovalStatus(APPROVAL_REJECTED); after.setApprovalStatus(APPROVAL_REJECTED);
update.setCurrentNode("审核不通过"); after.setCurrentNode("审核不通过");
update.setCurrentProcessor(AuthUtil.getUserName()); after.setCurrentProcessor(AuthUtil.getUserName());
addChangeRecord(id, "客商准入审核不通过"); addChangeRecord(id, "客商准入审核不通过", before, after);
return updateById(update); return updateById(after);
} }
@Override @Override
@@ -234,15 +246,11 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
if (!Objects.equals(status, STATUS_ENABLED) && !Objects.equals(status, STATUS_DISABLED)) { if (!Objects.equals(status, STATUS_ENABLED) && !Objects.equals(status, STATUS_DISABLED)) {
throw new ServiceException("启停状态不正确"); throw new ServiceException("启停状态不正确");
} }
CustomerArchive customer = getById(id); CustomerArchive customer = ensureCustomerAccessible(id);
if (Func.isEmpty(customer) || Objects.equals(customer.getIsDeleted(), 1)) { CustomerArchive after = copyCustomer(customer);
throw new ServiceException("客商档案不存在"); after.setStatus(status);
} addChangeRecord(id, Objects.equals(status, STATUS_ENABLED) ? "启用客商档案" : "停用客商档案", customer, after);
CustomerArchive update = new CustomerArchive(); return updateById(after);
update.setId(id);
update.setStatus(status);
addChangeRecord(id, Objects.equals(status, STATUS_ENABLED) ? "启用客商档案" : "停用客商档案");
return updateById(update);
} }
@Override @Override
@@ -477,6 +485,9 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
score.setIsDeleted(0); score.setIsDeleted(0);
creditScoreMapper.insert(score); creditScoreMapper.insert(score);
List<CustomerCreditScoreDetailVO> details = scoreVO.getDetails() == null ? new ArrayList<>() : scoreVO.getDetails(); List<CustomerCreditScoreDetailVO> details = scoreVO.getDetails() == null ? new ArrayList<>() : scoreVO.getDetails();
if (Func.isEmpty(details)) {
throw new ServiceException("评分明细不能为空,请重新读取评分量化表后再保存");
}
for (int detailIndex = 0; detailIndex < details.size(); detailIndex++) { for (int detailIndex = 0; detailIndex < details.size(); detailIndex++) {
CustomerCreditScoreDetailVO detailVO = details.get(detailIndex); CustomerCreditScoreDetailVO detailVO = details.get(detailIndex);
if (Func.isEmpty(detailVO.getScore())) { if (Func.isEmpty(detailVO.getScore())) {
@@ -484,6 +495,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
} }
CustomerCreditScoreDetail detail = Objects.requireNonNull(BeanUtil.copyProperties(detailVO, CustomerCreditScoreDetail.class)); CustomerCreditScoreDetail detail = Objects.requireNonNull(BeanUtil.copyProperties(detailVO, CustomerCreditScoreDetail.class));
detail.setId(IdWorker.getId()); detail.setId(IdWorker.getId());
detail.setTenantId(score.getTenantId());
detail.setScoreId(scoreId); detail.setScoreId(scoreId);
detail.setQuantificationId(score.getQuantificationId()); detail.setQuantificationId(score.getQuantificationId());
detail.setSort(detailIndex + 1); detail.setSort(detailIndex + 1);
@@ -604,14 +616,6 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
} }
} }
private List<CustomerChangeRecordVO> loadChangeRecords(Long customerId) {
return changeRecordMapper.selectList(Wrappers.<CustomerChangeRecord>lambdaQuery()
.eq(CustomerChangeRecord::getCustomerId, customerId)
.eq(CustomerChangeRecord::getIsDeleted, 0)
.orderByDesc(CustomerChangeRecord::getChangeTime))
.stream().map(record -> Objects.requireNonNull(BeanUtil.copyProperties(record, CustomerChangeRecordVO.class))).toList();
}
private CreditScoreQuantification resolveQuantification(Long quantificationId) { private CreditScoreQuantification resolveQuantification(Long quantificationId) {
CreditScoreQuantification quantification; CreditScoreQuantification quantification;
if (Func.isNotEmpty(quantificationId)) { if (Func.isNotEmpty(quantificationId)) {
@@ -763,17 +767,125 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
return CUSTOMER_CODE_PREFIX + String.format("%06d", nextNumber); return CUSTOMER_CODE_PREFIX + String.format("%06d", nextNumber);
} }
private void addChangeRecord(Long customerId, String content) { private CustomerArchive ensureCustomerAccessible(Long id) {
CustomerArchive customer = getById(id);
if (Func.isEmpty(customer) || Objects.equals(customer.getIsDeleted(), 1)) {
throw new ServiceException("客商档案不存在");
}
if (baseMapper.selectCustomerPermissionCount(id, AuthUtil.getUserId()) == 0) {
throw new ServiceException("无权访问该客商档案");
}
return customer;
}
private CustomerArchive copyCustomer(CustomerArchive customer) {
return Objects.requireNonNull(BeanUtil.copyProperties(customer, CustomerArchive.class));
}
private void addChangeRecord(Long customerId, String content, CustomerArchive before, CustomerArchive after) {
Map<String, Object> beforeSnapshot = customerSnapshot(before);
Map<String, Object> afterSnapshot = customerSnapshot(after);
addDetailSnapshot(beforeSnapshot, null);
addDetailSnapshot(afterSnapshot, null);
writeChangeRecord(customerId, content, beforeSnapshot, afterSnapshot);
}
private void addChangeRecord(Long customerId, String content, CustomerArchive before, CustomerArchive after,
CustomerArchiveVO beforeDetail, CustomerArchiveVO afterDetail) {
Map<String, Object> beforeSnapshot = customerSnapshot(before);
Map<String, Object> afterSnapshot = customerSnapshot(after);
addDetailSnapshot(beforeSnapshot, beforeDetail);
addDetailSnapshot(afterSnapshot, afterDetail);
writeChangeRecord(customerId, content, beforeSnapshot, afterSnapshot);
}
private void writeChangeRecord(Long customerId, String content, Map<String, Object> beforeSnapshot,
Map<String, Object> afterSnapshot) {
for (String field : afterSnapshot.keySet()) {
beforeSnapshot.putIfAbsent(field, null);
}
Map<String, Object> beforeData = new LinkedHashMap<>();
Map<String, Object> afterData = new LinkedHashMap<>();
List<String> changedFields = new ArrayList<>();
for (String field : beforeSnapshot.keySet()) {
if (!afterSnapshot.containsKey(field)) {
afterSnapshot.put(field, null);
}
Object beforeValue = beforeSnapshot.get(field);
Object afterValue = afterSnapshot.get(field);
if (!Objects.equals(beforeValue, afterValue)) {
changedFields.add(field);
beforeData.put(field, beforeValue);
afterData.put(field, afterValue);
}
}
if (changedFields.isEmpty()) {
return;
}
CustomerChangeRecord record = new CustomerChangeRecord(); CustomerChangeRecord record = new CustomerChangeRecord();
record.setId(IdWorker.getId()); record.setId(IdWorker.getId());
record.setCustomerId(customerId); record.setCustomerId(customerId);
record.setChangeTime(LocalDateTime.now()); record.setChangeTime(LocalDateTime.now());
record.setChangeContent(content); record.setChangeContent(content);
record.setChangedFields(String.join("", changedFields));
record.setBeforeData(JsonUtil.toJson(beforeData));
record.setAfterData(JsonUtil.toJson(afterData));
record.setChangeUserName(AuthUtil.getUserName()); record.setChangeUserName(AuthUtil.getUserName());
record.setStatus(STATUS_ENABLED); record.setStatus(STATUS_ENABLED);
changeRecordMapper.insert(record); changeRecordMapper.insert(record);
} }
private void addDetailSnapshot(Map<String, Object> snapshot, CustomerArchiveVO detail) {
if (detail == null) {
return;
}
snapshot.put("联系人信息", JsonUtil.toJson(detail.getContacts()));
snapshot.put("收款信息", JsonUtil.toJson(detail.getReceiptAccounts()));
snapshot.put("发票信息", JsonUtil.toJson(detail.getInvoices()));
snapshot.put("评分信息", JsonUtil.toJson(detail.getScores()));
}
private Map<String, Object> customerSnapshot(CustomerArchive customer) {
Map<String, Object> snapshot = new LinkedHashMap<>();
if (customer == null) {
return snapshot;
}
snapshot.put("客商编号", customer.getCustomerCode());
snapshot.put("客商简称", customer.getShortName());
snapshot.put("客商全称", customer.getFullName());
snapshot.put("客商性质", customer.getCustomerNature());
snapshot.put("统一社会信用代码", customer.getUnifiedCreditCode());
snapshot.put("客商类型", customer.getCustomerType());
snapshot.put("所属项目", customer.getProjectName());
snapshot.put("注册/实际经营地址", customer.getRegisteredAddress());
snapshot.put("注册地址行政区划", customer.getRegisteredRegionName());
snapshot.put("注册地址详细地址", customer.getRegisteredDetailAddress());
snapshot.put("法人/负责人", customer.getLegalPerson());
snapshot.put("联系电话", customer.getContactPhone());
snapshot.put("所属组织ID", customer.getDeptId());
snapshot.put("所属组织ID集合", customer.getDeptIds());
snapshot.put("所属组织", customer.getDeptName());
snapshot.put("开票税点", customer.getInvoiceTaxRate());
snapshot.put("经营范围", customer.getBusinessScope());
snapshot.put("营业期限类型", customer.getBusinessTermType());
snapshot.put("营业期限截止日", customer.getBusinessEndDate());
snapshot.put("注册资金(万元)", customer.getRegisteredCapital());
snapshot.put("客商负责人", customer.getPrincipal());
snapshot.put("助记码", customer.getMnemonicCode());
snapshot.put("客户等级", customer.getCustomerLevel());
snapshot.put("最大资金使用额度(万元)", customer.getMaxCreditLimit());
snapshot.put("申请总资金使用额度(万元)", customer.getApplyCreditLimit());
snapshot.put("备注", customer.getRemark());
snapshot.put("客商材料", customer.getQualificationAttachments());
snapshot.put("准入类型", customer.getAccessType());
snapshot.put("审批状态", customer.getApprovalStatus());
snapshot.put("当前节点", customer.getCurrentNode());
snapshot.put("当前处理人", customer.getCurrentProcessor());
snapshot.put("审核通过时间", customer.getApprovedTime());
snapshot.put("状态", customer.getStatus());
return snapshot;
}
private CustomerArchiveExcel buildExcel(CustomerArchive customer) { private CustomerArchiveExcel buildExcel(CustomerArchive customer) {
CustomerArchiveExcel excel = new CustomerArchiveExcel(); CustomerArchiveExcel excel = new CustomerArchiveExcel();
excel.setCustomerCode(customer.getCustomerCode()); excel.setCustomerCode(customer.getCustomerCode());
@@ -214,8 +214,6 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
if (!canCurrentUserOperate(projectApply)) { if (!canCurrentUserOperate(projectApply)) {
throw new ServiceException("无权操作其他组织项目立项"); throw new ServiceException("无权操作其他组织项目立项");
} }
TransportBusinessSupport.validateRequired(changeContent, "请输入变更内容");
TransportBusinessSupport.validateRequired(changeReason, "请输入变更原因");
projectApply.setApprovalStatus(STATUS_CHANGE_REVIEWING); projectApply.setApprovalStatus(STATUS_CHANGE_REVIEWING);
projectApply.setChangeContent(TransportBusinessSupport.trimToNull(changeContent)); projectApply.setChangeContent(TransportBusinessSupport.trimToNull(changeContent));
projectApply.setChangeReason(TransportBusinessSupport.trimToNull(changeReason)); projectApply.setChangeReason(TransportBusinessSupport.trimToNull(changeReason));
@@ -439,8 +437,6 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
if (!List.of(CHANGE_TYPE_PROJECT, CHANGE_TYPE_RECORD).contains(changeType)) { if (!List.of(CHANGE_TYPE_PROJECT, CHANGE_TYPE_RECORD).contains(changeType)) {
throw new ServiceException("请选择变更类型"); throw new ServiceException("请选择变更类型");
} }
TransportBusinessSupport.validateRequired(projectApply.getChangeContent(), "请输入变更内容");
TransportBusinessSupport.validateRequired(projectApply.getChangeReason(), "请输入变更原因");
validateChangeLength(projectApply); validateChangeLength(projectApply);
} }
@@ -45,6 +45,7 @@ import org.springblade.transport.wrapper.WaybillWrapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@@ -427,6 +428,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
} else { } else {
TransportBusinessSupport.validateRequired(waybill.getDriverName(), "司机不能为空"); TransportBusinessSupport.validateRequired(waybill.getDriverName(), "司机不能为空");
TransportBusinessSupport.validateRequired(waybill.getDriverPhone(), "司机手机号不能为空"); TransportBusinessSupport.validateRequired(waybill.getDriverPhone(), "司机手机号不能为空");
if (!isRoadTransport(waybill.getTransportType())) {
TransportBusinessSupport.validateRequired(waybill.getTrailerVehicleNo(), "挂车车牌号不能为空"); TransportBusinessSupport.validateRequired(waybill.getTrailerVehicleNo(), "挂车车牌号不能为空");
TransportBusinessSupport.validateRequired(waybill.getEscortName(), "押运人不能为空"); TransportBusinessSupport.validateRequired(waybill.getEscortName(), "押运人不能为空");
TransportBusinessSupport.validateRequired(waybill.getEscortPhone(), "押运人手机号不能为空"); TransportBusinessSupport.validateRequired(waybill.getEscortPhone(), "押运人手机号不能为空");
@@ -434,6 +436,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
throw new ServiceException("里程不能为空"); throw new ServiceException("里程不能为空");
} }
} }
}
TransportBusinessSupport.validateRequired(waybill.getCarrierJson(), "承运信息不能为空"); TransportBusinessSupport.validateRequired(waybill.getCarrierJson(), "承运信息不能为空");
if (Func.isEmpty(waybill.getQuantity())) { if (Func.isEmpty(waybill.getQuantity())) {
throw new ServiceException("数量不能为空"); throw new ServiceException("数量不能为空");
@@ -492,6 +495,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
TransportBusinessSupport.validateNonNegative(waybill.getMileage(), "里程"); TransportBusinessSupport.validateNonNegative(waybill.getMileage(), "里程");
TransportBusinessSupport.validateNonNegative(waybill.getUnitPrice(), "单价"); TransportBusinessSupport.validateNonNegative(waybill.getUnitPrice(), "单价");
TransportBusinessSupport.validateNonNegative(waybill.getOtherFeeTotal(), "其他费用合计"); TransportBusinessSupport.validateNonNegative(waybill.getOtherFeeTotal(), "其他费用合计");
validateRoadTaskInfo(waybill);
TransportBusinessSupport.validateDateRange(waybill.getStartDate(), waybill.getEndDate(), "开始日期不能晚于结束日期"); TransportBusinessSupport.validateDateRange(waybill.getStartDate(), waybill.getEndDate(), "开始日期不能晚于结束日期");
if (waybill.getEstimatedStartTime() != null if (waybill.getEstimatedStartTime() != null
&& waybill.getEstimatedEndTime() != null && waybill.getEstimatedEndTime() != null
@@ -518,6 +522,31 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
return false; return false;
} }
private void validateRoadTaskInfo(Waybill waybill) {
if (!isRoadTransport(waybill.getTransportType())) {
return;
}
TransportBusinessSupport.validateLength(waybill.getDriverName(), 20, "司机姓名不能超过20字");
TransportBusinessSupport.validateLength(waybill.getDriverPhone(), 20, "司机手机号不能超过20字");
TransportBusinessSupport.validateLength(waybill.getVehicleNo(), 30, "车牌号不能超过30字");
TransportBusinessSupport.validateLength(waybill.getTrailerVehicleNo(), 30, "挂车车牌号不能超过30字");
TransportBusinessSupport.validateLength(waybill.getEscortName(), 20, "押运人不能超过20字");
TransportBusinessSupport.validateLength(waybill.getEscortPhone(), 20, "押运人手机号不能超过20字");
BigDecimal mileage = waybill.getMileage();
if (mileage == null) {
return;
}
BigDecimal normalizedMileage = mileage.stripTrailingZeros();
if (normalizedMileage.signum() <= 0 || normalizedMileage.scale() > 0 || normalizedMileage.precision() > 10) {
throw new ServiceException("里程必须为不超过10位的正整数");
}
}
private boolean isRoadTransport(String transportType) {
String value = transportType == null ? "" : transportType.toLowerCase();
return value.contains("公路") || value.contains("道路") || value.contains("road") || "gl".equals(value);
}
private void fillJsonFromFlatFields(Waybill waybill) { private void fillJsonFromFlatFields(Waybill waybill) {
if (Func.isEmpty(waybill.getTaskEntryMode())) { if (Func.isEmpty(waybill.getTaskEntryMode())) {
waybill.setTaskEntryMode("simple"); waybill.setTaskEntryMode("simple");
@@ -221,6 +221,9 @@ CREATE TABLE `blade_customer_change_record` (
`customer_id` bigint NOT NULL COMMENT '客商ID', `customer_id` bigint NOT NULL COMMENT '客商ID',
`change_time` datetime NULL DEFAULT NULL COMMENT '变更日期', `change_time` datetime NULL DEFAULT NULL COMMENT '变更日期',
`change_content` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '变更内容', `change_content` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '变更内容',
`changed_fields` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '变更字段',
`before_data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '变更前数据JSON',
`after_data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '变更后数据JSON',
`change_user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '变更账号', `change_user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '变更账号',
`create_user` bigint NULL DEFAULT NULL COMMENT '创建人', `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
@@ -0,0 +1,6 @@
-- 客商变更记录:增加字段变更审计明细
ALTER TABLE `blade_customer_change_record`
ADD COLUMN `changed_fields` varchar(1000) DEFAULT NULL COMMENT '变更字段' AFTER `change_content`,
ADD COLUMN `before_data` text COMMENT '变更前数据JSON' AFTER `changed_fields`,
ADD COLUMN `after_data` text COMMENT '变更后数据JSON' AFTER `before_data`;
@@ -0,0 +1,9 @@
-- 修复评分明细未写入 tenant_id 导致被多租户拦截器过滤的问题。
-- 明细租户与所属评分主记录保持一致;仅更新 tenant_id 为空的历史数据。
UPDATE `blade_customer_credit_score_detail` detail
INNER JOIN `blade_customer_credit_score` score ON score.`id` = detail.`score_id`
SET detail.`tenant_id` = score.`tenant_id`
WHERE (detail.`tenant_id` IS NULL OR TRIM(detail.`tenant_id`) = '')
AND score.`tenant_id` IS NOT NULL
AND TRIM(score.`tenant_id`) <> '';