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 0446ece9..a024a6f8 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
@@ -203,14 +203,18 @@ public class Dept extends TenantEntity {
/**
* 是否平台公司:0否,1是
+ * ALWAYS:保证「否(0)」也能更新落库,避免被字段策略跳过
*/
@Schema(description = "是否平台公司:0否,1是")
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
private Integer isPlatformCompany;
/**
* 是否业务部门:0否,1是
+ * ALWAYS:保证「否(0)」也能更新落库,避免被字段策略跳过
*/
@Schema(description = "是否业务部门:0否,1是")
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
private Integer isBusinessDept;
/**
diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleDataScopeVO.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleDataScopeVO.java
index 1656f7ae..78eb5307 100644
--- a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleDataScopeVO.java
+++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleDataScopeVO.java
@@ -67,6 +67,18 @@ public class RoleDataScopeVO implements Serializable {
return scope;
}
+ /**
+ * 无组织可见(未配置数据权限时的默认结果)
+ */
+ public static RoleDataScopeVO none() {
+ RoleDataScopeVO scope = new RoleDataScopeVO();
+ scope.setScopeType(RoleDataScopeType.CUSTOM.getCode());
+ scope.setAllDept(false);
+ scope.setOwnCreate(false);
+ scope.setDeptIds(Collections.emptyList());
+ return scope;
+ }
+
public static RoleDataScopeVO ownCreate() {
RoleDataScopeVO scope = new RoleDataScopeVO();
scope.setScopeType(RoleDataScopeType.OWN_CREATE.getCode());
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ContractManageVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ContractManageVO.java
index 5f76a439..c26bef6f 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ContractManageVO.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ContractManageVO.java
@@ -64,9 +64,13 @@ public class ContractManageVO extends ContractManage {
private Boolean hcsSynced;
@TableField(exist = false)
- @Schema(description = "生效类型筛选")
+ @Schema(description = "合同状态筛选")
private String effectiveType;
+ @TableField(exist = false)
+ @Schema(description = "客商(匹配甲/乙/丙方)")
+ private String partyName;
+
@TableField(exist = false)
@Schema(description = "创建人姓名")
private String createUserName;
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 c5f370ac..0c4a3256 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
@@ -126,8 +126,11 @@ public class DeptController extends BladeController {
/**
* 获取部门树形结构
+ *
+ * 登录即可调用;数据范围由角色「数据权限」中 {@code dept_tree}(树形结构)单独配置,
+ * 未配置时返回空树(默认不展示)。
*/
- @PreAuth(menu = "dept")
+ @PreAuth(AuthConstant.PERMIT_ALL)
@GetMapping("/tree")
@ApiOperationSupport(order = 4)
@Operation(summary = "树形结构", description = "树形结构")
@@ -139,8 +142,9 @@ public class DeptController extends BladeController {
* 懒加载获取部门树形结构
*
* tenantId 由 Service 层完整决策:超管入参为空表示查全部,非超管强制覆盖为会话租户。
+ * 数据范围与 {@link #tree} 相同,由 {@code dept_tree} 单独配置,未配置时返回空树。
*/
- @PreAuth(menu = "dept")
+ @PreAuth(AuthConstant.PERMIT_ALL)
@GetMapping("/lazy-tree")
@ApiOperationSupport(order = 5)
@Operation(summary = "懒加载树形结构", description = "树形结构")
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IRoleMenuDataScopeService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IRoleMenuDataScopeService.java
index 93cb33d2..17349a22 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/IRoleMenuDataScopeService.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IRoleMenuDataScopeService.java
@@ -66,6 +66,16 @@ public interface IRoleMenuDataScopeService extends BaseService roleIds, String menuCode, List userDeptIds);
+ /**
+ * 按菜单编码解析角色数据权限;菜单不存在或未配置时返回空权限(不回落为全部组织)
+ *
+ * @param roleIds 角色ID集合
+ * @param menuCode 菜单编码
+ * @param userDeptIds 用户所属组织
+ * @return 解析结果
+ */
+ RoleDataScopeVO resolveByMenuCodeOrEmpty(List roleIds, String menuCode, List userDeptIds);
+
/**
* 合并用户全部角色菜单配置为最宽数据权限
*
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 a211a169..2b2f485e 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
@@ -224,13 +224,18 @@ public class DeptServiceImpl extends ServiceImpl implements ID
.toList();
}
+ /**
+ * 组织树专用数据权限菜单编码(角色「数据权限」中单独配置;未配置时组织树默认不展示)。
+ */
+ public static final String MENU_DEPT_TREE = "dept_tree";
+
/**
* 解析当前登录用户的组织树数据权限。
*
- * 超管或角色菜单数据权限最宽合并为「全部组织」时不过滤;
- * 其余按角色配置的组织并集过滤。仅「本人创建」时按用户所属组织及下级展示组织树。
+ * 仅读取 {@link #MENU_DEPT_TREE}「树形结构」菜单的数据权限配置,不再合并其他业务菜单;
+ * 超管不过滤;未配置该菜单数据权限时返回空树(默认不显示)。
*
- * @return 过滤上下文;无法识别用户时按空权限处理
+ * @return 过滤上下文;无法识别用户或未配置时按空权限处理
*/
private DeptDataScopeFilter resolveDeptDataScopeFilter() {
if (AuthUtil.isAdministrator()) {
@@ -246,8 +251,11 @@ public class DeptServiceImpl extends ServiceImpl implements ID
if (Func.isNotEmpty(user) && Func.isNotEmpty(user.getDeptId())) {
userDeptIds = Func.toLongList(user.getDeptId());
}
- RoleDataScopeVO scope = roleMenuDataScopeService.resolveWidest(roleIds, userDeptIds);
- if (scope == null || scope.isAllDept()) {
+ RoleDataScopeVO scope = roleMenuDataScopeService.resolveByMenuCodeOrEmpty(roleIds, MENU_DEPT_TREE, userDeptIds);
+ if (scope == null) {
+ return DeptDataScopeFilter.empty();
+ }
+ if (scope.isAllDept()) {
return DeptDataScopeFilter.all();
}
LinkedHashSet allowedDeptIds = new LinkedHashSet<>();
@@ -665,12 +673,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 (dept.getIsBusinessDept() == null) {
- dept.setIsBusinessDept(0);
- }
+ // 统一规范为 0/1,避免前端传空串、布尔或其它类型导致更新未生效
+ dept.setIsPlatformCompany(normalizeYesNoFlag(dept.getIsPlatformCompany()));
+ dept.setIsBusinessDept(normalizeYesNoFlag(dept.getIsBusinessDept()));
// 排序未填时默认 1
if (dept.getSort() == null) {
dept.setSort(1);
@@ -679,9 +684,24 @@ public class DeptServiceImpl extends ServiceImpl implements ID
throw new ServiceException("租户ID不能为空");
}
validateDeptCategory(dept);
- validateDeptCode(dept, parent);
+ validateDeptCode(dept);
validateContractInitialSerial(dept);
- return saveOrUpdate(dept);
+ boolean saved = saveOrUpdate(dept);
+ if (saved && Func.isNotEmpty(dept.getId())) {
+ // 显式回写两个业务标志,避免部分更新路径漏改
+ this.update(Wrappers.lambdaUpdate()
+ .set(Dept::getIsPlatformCompany, dept.getIsPlatformCompany())
+ .set(Dept::getIsBusinessDept, dept.getIsBusinessDept())
+ .eq(Dept::getId, dept.getId()));
+ }
+ return saved;
+ }
+
+ /**
+ * 将是否类标志规范为 0/1;空值按 0 处理。
+ */
+ private Integer normalizeYesNoFlag(Integer flag) {
+ return Integer.valueOf(1).equals(flag) ? 1 : 0;
}
@Override
@@ -881,9 +901,9 @@ public class DeptServiceImpl extends ServiceImpl implements ID
}
}
- private void validateDeptCode(Dept dept, Dept parent) {
+ private void validateDeptCode(Dept dept) {
String deptCode = dept.getDeptCode();
- // 组织编码非必填,为空时存 null,避免唯一索引冲突
+ // 组织编码非必填,为空时存 null,避免唯一索引冲突;不再校验 GWWL/上级编码+三位数字格式
if (StringUtil.isBlank(deptCode)) {
dept.setDeptCode(null);
return;
@@ -892,26 +912,6 @@ public class DeptServiceImpl extends ServiceImpl implements ID
if (deptCode.length() > 30) {
throw new ServiceException("组织编码不能超过30个字符");
}
- // 顶级组织不依赖上级编码
- if (parent == null) {
- this.ensureDeptCodeUnique(dept, deptCode);
- dept.setDeptCode(deptCode);
- return;
- }
- if (StringUtil.isBlank(parent.getDeptCode())) {
- throw new ServiceException("请选择已配置组织编码的上级组织");
- }
- // 公司:GWWL+三位数字;部门:所属公司编码+三位数字;其他:上级编码+三位数字
- String expectedParentCode = parent.getDeptCode();
- if (DeptCategory.COMPANY.getCode().equals(dept.getDeptCategory())) {
- expectedParentCode = OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE;
- } else if (DeptCategory.DEPT.getCode().equals(dept.getDeptCategory())
- && StringUtil.isNotBlank(dept.getBelongCompanyCode())) {
- expectedParentCode = dept.getBelongCompanyCode();
- }
- if (!org.springblade.system.util.DeptCodeUtil.matchesChildCode(expectedParentCode, deptCode)) {
- throw new ServiceException("组织编码格式应为:" + expectedParentCode + "三位数字(不足补零,中间不加-)");
- }
this.ensureDeptCodeUnique(dept, deptCode);
dept.setDeptCode(deptCode);
}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RoleMenuDataScopeServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RoleMenuDataScopeServiceImpl.java
index 0314afc9..04d62d5b 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RoleMenuDataScopeServiceImpl.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RoleMenuDataScopeServiceImpl.java
@@ -184,20 +184,44 @@ public class RoleMenuDataScopeServiceImpl extends BaseServiceImpllambdaQuery()
- .eq(Menu::getCode, menuCode)
- .eq(Menu::getIsDeleted, 0)
- .last("limit 1"));
+ Menu menu = findMenuByCode(menuCode);
if (Func.isEmpty(menu)) {
return RoleDataScopeVO.all();
}
- List scopeList = list(Wrappers.lambdaQuery()
- .in(RoleMenuDataScope::getRoleId, roleIds)
- .eq(RoleMenuDataScope::getMenuId, menu.getId())
- .eq(RoleMenuDataScope::getIsDeleted, 0));
+ List scopeList = listScopes(roleIds, menu.getId());
return mergeScopes(scopeList, userDeptIds);
}
+ @Override
+ public RoleDataScopeVO resolveByMenuCodeOrEmpty(List roleIds, String menuCode, List userDeptIds) {
+ if (Func.isEmpty(roleIds) || Func.isBlank(menuCode)) {
+ return RoleDataScopeVO.none();
+ }
+ Menu menu = findMenuByCode(menuCode);
+ if (Func.isEmpty(menu)) {
+ return RoleDataScopeVO.none();
+ }
+ List scopeList = listScopes(roleIds, menu.getId());
+ if (Func.isEmpty(scopeList)) {
+ return RoleDataScopeVO.none();
+ }
+ return mergeScopes(scopeList, userDeptIds);
+ }
+
+ private Menu findMenuByCode(String menuCode) {
+ return menuMapper.selectOne(Wrappers.