diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java index d0fb928..2e075ec 100644 --- a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java @@ -179,4 +179,10 @@ public class Dept extends TenantEntity { @Schema(description = "是否是oa的部门") private Integer isOa; + /** + * 是否平台公司:0否,1是 + */ + @Schema(description = "是否平台公司:0否,1是") + private Integer isPlatformCompany; + } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java index 1a2f0eb..bd1befa 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java @@ -30,6 +30,7 @@ import org.springblade.transport.pojo.entity.CustomerArchive; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serial; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -85,4 +86,20 @@ public class CustomerArchiveVO extends CustomerArchive { @Schema(description = "客商类型:external外部客商,internal内部组织") private String customerKind; + @TableField(exist = false) + @Schema(description = "资金使用风险等级:high、medium、none") + private String fundUseRisk; + + @TableField(exist = false) + @Schema(description = "资金使用风险名称") + private String fundUseRiskName; + + @TableField(exist = false) + @Schema(description = "资金使用率(百分比)") + private BigDecimal fundUseRate; + + @TableField(exist = false) + @Schema(description = "已付金额合计(元)") + private BigDecimal usedFundLimit; + } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/VehicleDispatchVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/VehicleDispatchVO.java index 05ed2f9..91544a8 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/VehicleDispatchVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/VehicleDispatchVO.java @@ -50,6 +50,9 @@ public class VehicleDispatchVO extends VehicleDispatch { @Schema(description = "审批状态名称") private String approvalStatusName; @TableField(exist = false) + @Schema(description = "车辆类型") + private String vehicleType; + @TableField(exist = false) @Schema(description = "创建人姓名") private String createUserName; @TableField(exist = false) diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/DeptController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/DeptController.java index 06d1eb9..11c4081 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/DeptController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/DeptController.java @@ -189,12 +189,23 @@ public class DeptController extends BladeController { return R.data(deptService.selectDept(deptId)); } + /** + * 平台公司下拉(是否平台公司=是) + */ + @PreAuth(AuthConstant.PERMIT_ALL) + @GetMapping("/platform-company-select") + @ApiOperationSupport(order = 9) + @Operation(summary = "平台公司下拉", description = "返回是否平台公司=是的部门列表") + public R> platformCompanySelect() { + return R.data(deptService.listPlatformCompany()); + } + /** * 获取部门的主管信息 */ @IsAdmin @GetMapping("/dept-leader-info") - @ApiOperationSupport(order = 9) + @ApiOperationSupport(order = 10) @Operation(summary = "获取部门的主管信息", description = "传入deptId") public R> deptLeaderInfo(@Parameter(description = "部门id", required = true) @RequestParam Long deptId) { List list = deptService.deptLeaderInfo(deptId); diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DeptMapper.xml b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DeptMapper.xml index 4b40dea..baef84d 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DeptMapper.xml +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DeptMapper.xml @@ -16,6 +16,7 @@ + @@ -35,6 +36,7 @@ + diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java index 22b05cd..48e5102 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java @@ -97,6 +97,13 @@ public interface IDeptService extends IService { */ List selectDept(String deptId); + /** + * 平台公司下拉(是否平台公司=是) + * + * @return 平台公司部门列表 + */ + List listPlatformCompany(); + /** * 根据部门名称精确匹配获取部门ID集合 * diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java index 53b4b7b..ff25a47 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java @@ -151,6 +151,18 @@ public class DeptServiceImpl extends ServiceImpl implements ID return baseMapper.selectList(queryWrapper); } + @Override + public List listPlatformCompany() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() + .eq(Dept::getIsPlatformCompany, 1) + .orderByAsc(Dept::getSort) + .orderByAsc(Dept::getId); + if (!AuthUtil.isAdministrator()) { + queryWrapper.eq(Dept::getTenantId, AuthUtil.getTenantId()); + } + return list(queryWrapper); + } + @Override public String getDeptIds(String tenantId, String deptNames) { List deptList = baseMapper.selectList(Wrappers.query().lambda().eq(Dept::getTenantId, tenantId).in(Dept::getDeptName, Func.toStrList(deptNames))); @@ -232,6 +244,9 @@ public class DeptServiceImpl extends ServiceImpl implements ID dept.setAncestors(parent.getAncestors() + StringPool.COMMA + dept.getParentId()); } dept.setIsDeleted(BladeConstant.DB_NOT_DELETED); + if (dept.getIsPlatformCompany() == null) { + dept.setIsPlatformCompany(0); + } if (Func.isEmpty(dept.getTenantId())) { throw new ServiceException("租户ID不能为空"); } @@ -264,8 +279,10 @@ public class DeptServiceImpl extends ServiceImpl implements ID private void validateDeptCode(Dept dept, Dept parent) { String deptCode = dept.getDeptCode(); + // 组织编码非必填,为空时存 null,避免唯一索引冲突 if (StringUtil.isBlank(deptCode)) { - throw new ServiceException("组织编码不能为空"); + dept.setDeptCode(null); + return; } deptCode = deptCode.trim(); if (deptCode.length() > 30) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java index 92a11df..b6d2710 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java @@ -132,7 +132,7 @@ public class ContractManageController extends BladeController { @Operation(summary = "发起变更", description = "传入id、changeContent和changeReason") public R startChange(@Parameter(description = "主键", required = true) @RequestParam Long id, @RequestParam(required = false) String changeContent, - @RequestParam String changeReason) { + @RequestParam(required = false) String changeReason) { return R.status(contractManageService.startChange(id, changeContent, changeReason)); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java index a18a875..cc15e8b 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java @@ -54,36 +54,24 @@ public class CustomerArchiveExcel implements Serializable { @ExcelProperty("客商简称") private String shortName; - @ExcelProperty("*客商名称") + @ExcelProperty("客商名称") private String fullName; - @ExcelProperty("*客商类型") + @ExcelProperty("客商类型") private String customerType; - @ExcelProperty("*客商性质") + @ExcelProperty("客商性质") private String customerNature; - @ExcelProperty("*统一信用代码") + @ExcelProperty("统一信用代码") private String unifiedCreditCode; - @ExcelProperty("*所属组织") + @ExcelProperty("所属组织") private String deptName; @ExcelProperty("准入类型") private String accessTypeName; - @ExcelProperty("审批状态") - private String approvalStatusName; - - @ExcelProperty("当前节点") - private String currentNode; - - @ExcelProperty("当前处理人") - private String currentProcessor; - - @ExcelProperty("审核通过时间") - private LocalDateTime approvedTime; - @ExcelProperty("状态") private String statusName; @@ -96,13 +84,25 @@ public class CustomerArchiveExcel implements Serializable { @ExcelProperty("申请总资金使用额度(万元)") private BigDecimal applyCreditLimit; - @ExcelProperty("*联系电话") + @ExcelProperty("联系电话") private String contactPhone; - @ExcelProperty("*法人/负责人") + @ExcelProperty("法人/负责人") private String legalPerson; @ExcelProperty("创建时间") private Date createTime; + @ExcelProperty("审批状态") + private String approvalStatusName; + + @ExcelProperty("当前节点") + private String currentNode; + + @ExcelProperty("当前处理人") + private String currentProcessor; + + @ExcelProperty("审核通过时间") + private LocalDateTime approvedTime; + } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java index cf8bdaa..8bd25d4 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/ProjectApplyExcel.java @@ -60,7 +60,7 @@ public class ProjectApplyExcel implements Serializable { private String projectType; @ExcelProperty("业务部门") private String businessDeptName; - @ExcelProperty("承办部门") + @ExcelProperty("平台公司") private String undertakeDeptName; @ExcelProperty("项目由来") private String projectSource; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/VehicleDispatchExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/VehicleDispatchExcel.java index 32f4cf6..c24e9ee 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/VehicleDispatchExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/VehicleDispatchExcel.java @@ -30,6 +30,7 @@ public class VehicleDispatchExcel implements Serializable { @ExcelProperty("车牌号") private String plateNo; @ExcelProperty("所属组织") private String organizationName; @ExcelProperty("使用部门") private String useDepartment; + @ExcelProperty("车辆类型") private String vehicleType; @ExcelProperty("审批状态") private String approvalStatusName; @ExcelProperty("当前节点") private String currentNode; @ExcelProperty("当前处理人") private String currentProcessor; @@ -42,6 +43,7 @@ public class VehicleDispatchExcel implements Serializable { target.plateNo = source.getPlateNo(); target.organizationName = source.getOrganizationName(); target.useDepartment = source.getUseDepartment(); + target.vehicleType = source.getVehicleType(); target.approvalStatusName = source.getApprovalStatusName(); target.currentNode = source.getCurrentNode(); target.currentProcessor = source.getCurrentProcessor(); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/VehicleDispatchMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/VehicleDispatchMapper.xml index e372641..72a9bfb 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/VehicleDispatchMapper.xml +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/VehicleDispatchMapper.xml @@ -8,6 +8,7 @@ + @@ -21,26 +22,31 @@ diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java index 663521e..aea3954 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java @@ -251,7 +251,6 @@ public class ContractManageServiceImpl extends BaseServiceImpl CHANGE_SNAPSHOT_IGNORE_FIELDS = Set.of( + "id", "customerId", "invoiceId", "scoreId", "tenantId", + "createUser", "createDept", "createTime", "updateUser", "updateTime", + "isDeleted", "status", "regionPath", "deptIdList", "registeredRegionPath", + "fundUseAmount", "fundUseRate", "fundUseRisk", "standards", "attachments" + ); private final CustomerContactMapper contactMapper; private final CustomerReceiptAccountMapper receiptAccountMapper; @@ -121,10 +136,13 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl selectCustomerArchivePage(IPage page, CustomerArchiveVO customer) { - return page.setRecords(baseMapper.selectCustomerArchivePage(page, customer, AuthUtil.getUserId())); + List records = baseMapper.selectCustomerArchivePage(page, customer, AuthUtil.getUserId()); + fillFundUseRisk(records); + return page.setRecords(records); } @Override @@ -156,6 +174,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl 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())); + snapshot.put("联系人信息", toBusinessSnapshotJson(detail.getContacts())); + snapshot.put("收款信息", toBusinessSnapshotJson(detail.getReceiptAccounts())); + snapshot.put("发票信息", toBusinessSnapshotJson(detail.getInvoices())); + snapshot.put("评分信息", toBusinessSnapshotJson(detail.getScores())); + } + + /** + * 明细快照只保留业务字段,并忽略主键/审计字段,避免“无修改提交”因重建 ID 误记变更。 + */ + private String toBusinessSnapshotJson(Object value) { + Object parsed = JsonUtil.parse(JsonUtil.toJson(value == null ? List.of() : value), Object.class); + return JsonUtil.toJson(normalizeSnapshotNode(parsed)); + } + + @SuppressWarnings("unchecked") + private Object normalizeSnapshotNode(Object node) { + if (node == null) { + return null; + } + if (node instanceof Map map) { + Map result = new TreeMap<>(); + map.forEach((key, value) -> { + String field = String.valueOf(key); + if (CHANGE_SNAPSHOT_IGNORE_FIELDS.contains(field)) { + return; + } + Object normalized = normalizeSnapshotNode(value); + if (normalized == null || "".equals(normalized)) { + return; + } + if (normalized instanceof Map nestedMap && nestedMap.isEmpty()) { + return; + } + if (normalized instanceof List nestedList && nestedList.isEmpty()) { + return; + } + result.put(field, normalized); + }); + return result; + } + if (node instanceof List list) { + List result = list.stream() + .map(this::normalizeSnapshotNode) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(ArrayList::new)); + result.sort(Comparator.comparing(JsonUtil::toJson)); + return result; + } + if (node instanceof BigDecimal decimal) { + return decimal.stripTrailingZeros().toPlainString(); + } + if (node instanceof Number number) { + return new BigDecimal(number.toString()).stripTrailingZeros().toPlainString(); + } + if (node instanceof Boolean || node instanceof LocalDate || node instanceof LocalDateTime) { + return node; + } + String text = String.valueOf(node).trim(); + return Func.isEmpty(text) || "null".equalsIgnoreCase(text) ? null : text; } private Map customerSnapshot(CustomerArchive customer) { @@ -1085,6 +1186,65 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl records) { + if (records == null || records.isEmpty()) { + return; + } + Set payerNames = new HashSet<>(); + records.forEach(customer -> { + if (Func.isNotEmpty(customer.getFullName())) { + payerNames.add(customer.getFullName().trim()); + } + if (Func.isNotEmpty(customer.getShortName())) { + payerNames.add(customer.getShortName().trim()); + } + }); + Map paidAmountByPayer = new HashMap<>(); + if (!payerNames.isEmpty()) { + paymentApplicationMapper.selectList(Wrappers.lambdaQuery() + .in(PaymentApplication::getPayerName, payerNames) + .eq(PaymentApplication::getApprovalStatus, APPROVAL_APPROVED) + .eq(PaymentApplication::getIsDeleted, 0)) + .forEach(item -> { + String payerName = Func.toStr(item.getPayerName()).trim(); + if (Func.isEmpty(payerName)) { + return; + } + paidAmountByPayer.merge(payerName, nonNegative(item.getPaidAmount()), BigDecimal::add); + }); + } + records.forEach(customer -> { + BigDecimal usedFundLimit = BigDecimal.ZERO; + if (Func.isNotEmpty(customer.getFullName())) { + usedFundLimit = usedFundLimit.add(paidAmountByPayer.getOrDefault(customer.getFullName().trim(), BigDecimal.ZERO)); + } + if (Func.isNotEmpty(customer.getShortName())) { + String shortName = customer.getShortName().trim(); + if (!shortName.equals(Func.toStr(customer.getFullName()).trim())) { + usedFundLimit = usedFundLimit.add(paidAmountByPayer.getOrDefault(shortName, BigDecimal.ZERO)); + } + } + BigDecimal maxCreditLimitYuan = nonNegative(customer.getMaxCreditLimit()).multiply(BigDecimal.valueOf(10000)); + BigDecimal fundUseRate = maxCreditLimitYuan.signum() == 0 + ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) + : usedFundLimit.multiply(BigDecimal.valueOf(100)).divide(maxCreditLimitYuan, 2, RoundingMode.HALF_UP); + String risk = fundUseRate.compareTo(new BigDecimal("90")) >= 0 ? "high" + : fundUseRate.compareTo(new BigDecimal("80")) >= 0 ? "medium" : "none"; + customer.setUsedFundLimit(usedFundLimit); + customer.setFundUseRate(fundUseRate); + customer.setFundUseRisk(risk); + customer.setFundUseRiskName("high".equals(risk) ? "高风险" : "medium".equals(risk) ? "中风险" : "无风险"); + }); + } + + private BigDecimal nonNegative(BigDecimal value) { + return value == null || value.signum() < 0 ? BigDecimal.ZERO : value; + } + private CustomerArchiveExcel buildExcel(CustomerArchive customer) { CustomerArchiveExcel excel = new CustomerArchiveExcel(); excel.setCustomerCode(customer.getCustomerCode()); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java index d30273d..0a58318 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java @@ -683,7 +683,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl labels = Map.ofEntries( Map.entry("projectType", "项目类型"), Map.entry("projectName", "项目名称"), Map.entry("projectShortName", "项目简称"), Map.entry("businessDeptName", "业务部门"), - Map.entry("undertakeDeptName", "承办部门"), Map.entry("projectSource", "项目由来"), + Map.entry("undertakeDeptName", "平台公司"), Map.entry("projectSource", "项目由来"), Map.entry("sourceRemark", "项目由来说明"), Map.entry("fundLimit", "项目资金使用额度"), Map.entry("receivableLimit", "项目应收账款额度"), Map.entry("receivableDays", "应收账款回款期限"), Map.entry("paymentDays", "回款账期"), Map.entry("cargoType", "货物类型"), @@ -717,7 +717,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl