mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-11-04 04:02:30 +08:00
feat: introduce TenantOwned interface for type-safe tenant filtering
Replace reflective getTenantId() in BaseController with a typed TenantOwned interface. All tenant-scoped BO classes now implement TenantOwned, enabling compile-time safety instead of runtime reflection. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
b0094f69b7
commit
a08f15904d
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.auth.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.RoleExt;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -38,7 +39,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class RoleBO extends BaseBO {
|
||||
public class RoleBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* ID
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.dal.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import io.github.pnoker.common.enums.GroupTypeFlagEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -38,7 +39,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class GroupBO extends BaseBO {
|
||||
public class GroupBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Parent group ID.
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.dal.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import io.github.pnoker.common.enums.EntityTypeFlagEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -38,7 +39,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class LabelBO extends BaseBO {
|
||||
public class LabelBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Label name.
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.DeviceExt;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -40,7 +41,7 @@ import java.util.List;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class DeviceBO extends BaseBO {
|
||||
public class DeviceBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Device Name
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.DriverAttributeExt;
|
||||
import io.github.pnoker.common.enums.AttributeTypeFlagEnum;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
@@ -39,7 +40,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class DriverAttributeBO extends BaseBO {
|
||||
public class DriverAttributeBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Name
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.JsonExt;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -38,7 +39,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class DriverAttributeConfigBO extends BaseBO {
|
||||
public class DriverAttributeConfigBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* ID
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.DriverExt;
|
||||
import io.github.pnoker.common.enums.DriverTypeFlagEnum;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
@@ -39,7 +40,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class DriverBO extends BaseBO {
|
||||
public class DriverBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Name
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.PointAttributeExt;
|
||||
import io.github.pnoker.common.enums.AttributeTypeFlagEnum;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
@@ -39,7 +40,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class PointAttributeBO extends BaseBO {
|
||||
public class PointAttributeBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Name
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.JsonExt;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -38,7 +39,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class PointAttributeConfigBO extends BaseBO {
|
||||
public class PointAttributeConfigBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* ID
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.PointExt;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import io.github.pnoker.common.enums.PointTypeFlagEnum;
|
||||
@@ -42,7 +43,7 @@ import java.math.BigDecimal;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class PointBO extends BaseBO {
|
||||
public class PointBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Name
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
package io.github.pnoker.common.manager.entity.bo;
|
||||
|
||||
import io.github.pnoker.common.entity.base.BaseBO;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.entity.ext.ProfileExt;
|
||||
import io.github.pnoker.common.enums.EnableFlagEnum;
|
||||
import io.github.pnoker.common.enums.ProfileShareFlagEnum;
|
||||
@@ -40,7 +41,7 @@ import lombok.ToString;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = true)
|
||||
public class ProfileBO extends BaseBO {
|
||||
public class ProfileBO extends BaseBO implements TenantOwned {
|
||||
|
||||
/**
|
||||
* Name
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2016-present the IoT DC3 original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.pnoker.common.entity.common;
|
||||
|
||||
/**
|
||||
* Marker contract for tenant-scoped objects.
|
||||
*
|
||||
* @author pnoker
|
||||
* @version 2026.5.10
|
||||
* @since 2026.5.10
|
||||
*/
|
||||
public interface TenantOwned {
|
||||
|
||||
Long getTenantId();
|
||||
|
||||
}
|
||||
+5
-15
@@ -19,12 +19,12 @@ package io.github.pnoker.common.base;
|
||||
|
||||
import io.github.pnoker.common.entity.R;
|
||||
import io.github.pnoker.common.entity.common.RequestHeader;
|
||||
import io.github.pnoker.common.entity.common.TenantOwned;
|
||||
import io.github.pnoker.common.exception.NotFoundException;
|
||||
import io.github.pnoker.common.utils.UserHeaderUtil;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -65,8 +65,8 @@ public interface BaseController {
|
||||
* Fail closed when an ID-based lookup returns an entity outside the caller's tenant.
|
||||
* Returning 404 avoids exposing whether a cross-tenant resource exists.
|
||||
*/
|
||||
default <T> T requireTenant(Long tenantId, T entity) {
|
||||
if (Objects.isNull(entity) || !Objects.equals(tenantId, resolveTenantId(entity))) {
|
||||
default <T extends TenantOwned> T requireTenant(Long tenantId, T entity) {
|
||||
if (Objects.isNull(entity) || !Objects.equals(tenantId, entity.getTenantId())) {
|
||||
throw new NotFoundException("Resource does not exist");
|
||||
}
|
||||
return entity;
|
||||
@@ -75,26 +75,16 @@ public interface BaseController {
|
||||
/**
|
||||
* Keep only entities that belong to the caller's tenant for bulk lookup endpoints.
|
||||
*/
|
||||
default <T> List<T> filterTenant(Long tenantId, Collection<T> entities) {
|
||||
default <T extends TenantOwned> List<T> filterTenant(Long tenantId, Collection<T> entities) {
|
||||
if (Objects.isNull(entities) || entities.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return entities.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(entity -> Objects.equals(tenantId, resolveTenantId(entity)))
|
||||
.filter(entity -> Objects.equals(tenantId, entity.getTenantId()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Long resolveTenantId(Object entity) {
|
||||
try {
|
||||
Method method = entity.getClass().getMethod("getTenantId");
|
||||
Object tenantId = method.invoke(entity);
|
||||
return tenantId instanceof Long value ? value : null;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new NotFoundException("Resource does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user ID from user header
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user