diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/FormalSettlementSaveRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/FormalSettlementSaveRequest.java index 706c321d..adf6dd14 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/FormalSettlementSaveRequest.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/FormalSettlementSaveRequest.java @@ -30,6 +30,8 @@ public class FormalSettlementSaveRequest implements Serializable { private String businessModel; /** 运输方式(业务字典 transport_type) */ private String transportType; + /** 用印类型(多选,顿号拼接:公章、业务章) */ + private String sealTypes; private LocalDate exchangeRateDate; private BigDecimal exchangeRate; private String attachmentsJson; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PreSettlementSaveRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PreSettlementSaveRequest.java index d30e8955..8a1323d2 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PreSettlementSaveRequest.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PreSettlementSaveRequest.java @@ -61,6 +61,9 @@ public class PreSettlementSaveRequest implements Serializable { @Schema(description = "运输方式(业务字典 transport_type)") private String transportType; + @Schema(description = "用印类型(多选,顿号拼接:公章、业务章)") + private String sealTypes; + @Schema(description = "汇率日期") private LocalDate exchangeRateDate; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java index 2a2b36ca..30d025d3 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java @@ -96,6 +96,10 @@ public class FormalSettlement extends TenantEntity { * 运输方式(业务字典 transport_type) */ private String transportType; + /** + * 用印类型(多选,顿号拼接:公章、业务章) + */ + private String sealTypes; /** * 本位币,默认 RMB */ diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PreSettlement.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PreSettlement.java index a333caf0..7b201b08 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PreSettlement.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PreSettlement.java @@ -98,6 +98,9 @@ public class PreSettlement extends TenantEntity { @Schema(description = "运输方式(业务字典 transport_type)") private String transportType; + @Schema(description = "用印类型(多选,顿号拼接:公章、业务章)") + private String sealTypes; + @Schema(description = "本位币") private String localCurrency; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ProjectChange.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ProjectChange.java index ad3776a1..a090793d 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ProjectChange.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ProjectChange.java @@ -188,6 +188,9 @@ public class ProjectChange extends TenantEntity { @Schema(description = "项目附件JSON") private String attachmentsJson; + @Schema(description = "调整材料JSON(含原附件快照与调整原因)") + private String adjustAttachmentsJson; + @Schema(description = "审批状态") private String approvalStatus; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java index 53be18ea..054adf22 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java @@ -101,6 +101,7 @@ public class FormalSettlementController extends BladeController { excel.setSettlementAmount(formatMoney(vo.getSettlementAmount(), vo.getCurrency())); excel.setBusinessModelName(vo.getBusinessModelName()); excel.setTransportTypeName(vo.getTransportTypeName()); + excel.setSealTypes(vo.getSealTypes()); excel.setLocalSettlementAmount(formatMoney(vo.getLocalSettlementAmount(), vo.getLocalCurrency())); excel.setExchangeRate(vo.getExchangeRate() == null ? "" : vo.getExchangeRate().stripTrailingZeros().toPlainString()); excel.setInvoiceStatusName(invoiceStatusName(vo.getInvoiceStatus(), vo.getSettlementType())); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PreSettlementController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PreSettlementController.java index 385e8ded..f27765dd 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PreSettlementController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PreSettlementController.java @@ -252,6 +252,7 @@ public class PreSettlementController extends BladeController { excel.setSettlementAmount(formatMoney(vo.getSettlementAmount(), vo.getCurrency())); excel.setBusinessModelName(vo.getBusinessModelName()); excel.setTransportTypeName(vo.getTransportTypeName()); + excel.setSealTypes(vo.getSealTypes()); excel.setLocalSettlementAmount(formatMoney(vo.getLocalSettlementAmount(), vo.getLocalCurrency())); excel.setExchangeRate(vo.getExchangeRate() == null ? "" : vo.getExchangeRate().stripTrailingZeros().toPlainString()); excel.setAdvanceAppliedAmount(formatMoney(vo.getAdvanceAppliedAmount(), vo.getCurrency())); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/FormalSettlementExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/FormalSettlementExcel.java index 99b72f1e..f512f86b 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/FormalSettlementExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/FormalSettlementExcel.java @@ -43,6 +43,8 @@ public class FormalSettlementExcel implements Serializable { private String businessModelName; @ExcelProperty("运输方式") private String transportTypeName; + @ExcelProperty("用印类型") + private String sealTypes; @ExcelProperty("本位币结算金额") private String localSettlementAmount; @ExcelProperty("结算汇率") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/PreSettlementExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/PreSettlementExcel.java index 29d3a936..2d754d52 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/PreSettlementExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/PreSettlementExcel.java @@ -54,6 +54,8 @@ public class PreSettlementExcel implements Serializable { private String businessModelName; @ExcelProperty("运输方式") private String transportTypeName; + @ExcelProperty("用印类型") + private String sealTypes; @ExcelProperty("本位币结算金额") private String localSettlementAmount; @ExcelProperty("结算汇率") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java index c1ddc927..01dd9d6c 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java @@ -651,6 +651,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl> materials = parseAttachmentMapList(attachmentsJson); + List> adjustRows = parseAttachmentMapList(adjustAttachmentsJson); + if (adjustRows.isEmpty()) { + return TransportBusinessSupport.trimToNull(attachmentsJson); + } + Set replacedIndexes = new HashSet<>(); + for (Map adjustRow : adjustRows) { + if (adjustRow == null || adjustRow.isEmpty()) { + continue; + } + Map nextMaterial = toProjectMaterialRow(adjustRow); + if (nextMaterial.isEmpty()) { + continue; + } + int originIndex = findOriginAttachmentIndex(materials, adjustRow, replacedIndexes); + if (originIndex >= 0) { + materials.set(originIndex, nextMaterial); + replacedIndexes.add(originIndex); + } else { + materials.add(nextMaterial); + } + } + return materials.isEmpty() ? null : JsonUtil.toJson(materials); + } + + @SuppressWarnings("unchecked") + private List> parseAttachmentMapList(String value) { + if (Func.isEmpty(value)) { + return new ArrayList<>(); + } + try { + Object parsed = JsonUtil.parse(value, List.class); + if (!(parsed instanceof List list)) { + return new ArrayList<>(); + } + List> rows = new ArrayList<>(); + for (Object item : list) { + if (item instanceof Map map) { + Map row = new LinkedHashMap<>(); + map.forEach((key, val) -> row.put(String.valueOf(key), val)); + rows.add(row); + } + } + return rows; + } catch (Exception ignored) { + return new ArrayList<>(); + } + } + + private Map toProjectMaterialRow(Map adjustRow) { + Map material = new LinkedHashMap<>(adjustRow); + material.remove("adjustReason"); + material.remove("originAttachment"); + material.remove("originKey"); + return material; + } + + @SuppressWarnings("unchecked") + private int findOriginAttachmentIndex(List> materials, Map adjustRow, + Set replacedIndexes) { + Object originObj = adjustRow.get("originAttachment"); + Map origin = null; + if (originObj instanceof Map map) { + origin = new LinkedHashMap<>(); + Map finalOrigin = origin; + map.forEach((key, val) -> finalOrigin.put(String.valueOf(key), val)); + } + String originKey = String.valueOf(adjustRow.getOrDefault("originKey", "")).trim(); + for (int index = 0; index < materials.size(); index++) { + if (replacedIndexes.contains(index)) { + continue; + } + Map current = materials.get(index); + if (Func.isNotEmpty(originKey) && originKey.equals(attachmentIdentity(current))) { + return index; + } + if (origin != null && attachmentIdentity(current).equals(attachmentIdentity(origin))) { + return index; + } + } + return -1; + } + + private String attachmentIdentity(Map row) { + if (row == null || row.isEmpty()) { + return ""; + } + String uid = String.valueOf(row.getOrDefault("uid", "")).trim(); + if (Func.isNotEmpty(uid) && !"null".equals(uid)) { + return "uid:" + uid; + } + for (String field : List.of("url", "link", "fileUrl", "downloadUrl", "src", "domain")) { + String value = String.valueOf(row.getOrDefault(field, "")).trim(); + if (Func.isNotEmpty(value) && !"null".equals(value)) { + return "url:" + value; + } + } + String fileType = String.valueOf(row.getOrDefault("fileType", "")).trim(); + String name = firstAttachmentName(row); + if (Func.isNotEmpty(fileType) || Func.isNotEmpty(name)) { + return "name:" + fileType + "|" + name; + } + return ""; + } + + private String firstAttachmentName(Map row) { + for (String field : List.of("originalName", "name", "fileName")) { + String value = String.valueOf(row.getOrDefault(field, "")).trim(); + if (Func.isNotEmpty(value) && !"null".equals(value)) { + return value; + } + } + return ""; + } + @Override @Transactional(rollbackFor = Exception.class) public boolean removeDraft(String ids) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectChangeServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectChangeServiceImpl.java index 28b0a18b..427d683a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectChangeServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectChangeServiceImpl.java @@ -126,6 +126,7 @@ public class ProjectChangeServiceImpl extends BaseServiceImpl SETTLEMENT_SEAL_TYPE_OPTIONS = List.of("公章", "业务章"); /** 站点名(省+市+区县)列宽:blade_waybill.departure_name / arrival_name 为 varchar(100) */ public static final int REGION_NAME_MAX_LENGTH = 100; @@ -134,7 +137,8 @@ public final class TransportBusinessSupport { } /** - * 解析指定菜单下的角色数据权限(超管视为全部组织),并与用户「数据管理权限」组织取并集。 + * 解析指定菜单下的角色数据权限(超管视为全部组织)。 + * 全部组织、本人创建以角色权限为准;所在部门及下级按用户数据权限组织。 */ public static RoleDataScopeVO resolveRoleDataScope(String menuCode) { if (isAdministrator()) { @@ -166,7 +170,8 @@ public final class TransportBusinessSupport { } /** - * 解析最宽角色数据权限,并与用户「数据管理权限」组织取并集。 + * 解析最宽角色数据权限。 + * 全部组织、本人创建以角色权限为准;所在部门及下级按用户数据权限组织。 */ public static RoleDataScopeVO resolveWidestDataScope() { if (isAdministrator()) { @@ -177,9 +182,12 @@ public final class TransportBusinessSupport { } /** - * 角色组织权限与用户数据管理组织取并集。 + * 按角色数据权限类型决定是否采用用户数据权限组织。 *

- * 全部组织 / 本人创建 不因用户管理组织改变;其余类型将用户管理组织(含下级)并入 deptIds。 + * 全部组织、本人创建以角色权限为准,不按用户数据权限收窄。 + * 所在部门及下级改为用户「数据管理权限」组织(含下级);同时存在自定义组织时取并集。 + * 自定义组织仍与用户数据权限组织取并集。 + * 所在部门及下级且用户未配置数据权限组织时,返回不可命中的组织,避免回落到所属组织或全部数据。 */ private static RoleDataScopeVO mergeUserManageDepts(RoleDataScopeVO scope) { if (scope == null || scope.isAllDept() || scope.isOwnCreate()) { @@ -187,6 +195,19 @@ public final class TransportBusinessSupport { } Long userId = AuthUtil.getUserId(); List manageDeptIds = UserCache.getManageDeptIds(userId); + if (RoleDataScopeType.DEPT_AND_CHILD.matches(scope.getScopeType())) { + LinkedHashSet deptIds = new LinkedHashSet<>(); + if (Func.isNotEmpty(scope.getDeptIds())) { + deptIds.addAll(scope.getDeptIds()); + } + if (Func.isNotEmpty(manageDeptIds)) { + deptIds.addAll(manageDeptIds); + } + if (deptIds.isEmpty()) { + return RoleDataScopeVO.ofDepts(RoleDataScopeType.DEPT_AND_CHILD.getCode(), List.of(-1L)); + } + return RoleDataScopeVO.ofDepts(RoleDataScopeType.DEPT_AND_CHILD.getCode(), List.copyOf(deptIds)); + } if (Func.isEmpty(manageDeptIds)) { return scope; } @@ -495,6 +516,37 @@ public final class TransportBusinessSupport { return trimValue.isEmpty() ? null : trimValue; } + /** + * 归一化结算单用印类型:仅允许「公章」「业务章」,多选用顿号拼接,并按固定顺序去重。 + * + * @param value 原始用印类型(支持顿号、逗号、空白分隔) + * @return 归一化后的用印类型,未选择时返回 null + */ + public static String normalizeSettlementSealTypes(String value) { + String source = trimToNull(value); + if (Func.isEmpty(source)) { + return null; + } + String[] parts = source.split("[、,,\\s]+"); + LinkedHashSet selected = new LinkedHashSet<>(); + for (String part : parts) { + String item = trimToNull(part); + if (Func.isEmpty(item)) { + continue; + } + if (!SETTLEMENT_SEAL_TYPE_OPTIONS.contains(item)) { + throw new ServiceException("用印类型仅支持:公章、业务章"); + } + selected.add(item); + } + if (selected.isEmpty()) { + return null; + } + return SETTLEMENT_SEAL_TYPE_OPTIONS.stream() + .filter(selected::contains) + .collect(Collectors.joining("、")); + } + /** * 归一化运输计划数据来源,保证列表只出现约定的三种来源。 * diff --git a/doc/sql/transport/blade_project_change_adjust_attachments_20261109.sql b/doc/sql/transport/blade_project_change_adjust_attachments_20261109.sql new file mode 100644 index 00000000..4e83ae1d --- /dev/null +++ b/doc/sql/transport/blade_project_change_adjust_attachments_20261109.sql @@ -0,0 +1,3 @@ +-- 项目变更:调整材料(记录原附件,审批通过后替换并同步到原项目档案项目材料) +ALTER TABLE `blade_project_change` + ADD COLUMN `adjust_attachments_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '调整材料JSON(含原附件快照与调整原因)' AFTER `attachments_json`; diff --git a/doc/sql/transport/blade_settlement_seal_types_20261109.sql b/doc/sql/transport/blade_settlement_seal_types_20261109.sql new file mode 100644 index 00000000..3d100b82 --- /dev/null +++ b/doc/sql/transport/blade_settlement_seal_types_20261109.sql @@ -0,0 +1,6 @@ +-- 预付结算单、正式结算单(付款/收款):用印类型(多选,顿号拼接存储:公章、业务章) +ALTER TABLE `blade_pre_settlement` + ADD COLUMN `seal_types` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用印类型(多选,顿号拼接:公章、业务章)' AFTER `transport_type`; + +ALTER TABLE `blade_formal_settlement` + ADD COLUMN `seal_types` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用印类型(多选,顿号拼接:公章、业务章)' AFTER `transport_type`;