feat(密钥管理): 按数据权限分组隔离与筛选
- 后台密钥列表改为按 biz_shop_manage_group 分组:主管只看自己带的分组成员,超管看全量可按 group_id 筛选 - 行数据补「所属分组」名称(批量查询);列表接口带回分组筛选项 - 修复超管筛选不生效:前端筛选参数统一 snake_case(camel 被后端静默忽略) - 列表列/筛选项文案由「所属管理员」改为「分组」
This commit is contained in:
+33
@@ -102,4 +102,37 @@ public interface ShopManageGroupMapper extends BaseMapper<ShopManageGroupEntity>
|
||||
</script>
|
||||
""")
|
||||
List<Long> selectUserIdsByGroupIds(@Param("groupIds") List<Long> groupIds);
|
||||
|
||||
/** 批量查用户所属分组名(密钥管理列表「分组」列展示用)。 */
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT gm.user_id AS userId, g.group_name AS groupName
|
||||
FROM biz_shop_manage_group_member gm
|
||||
INNER JOIN biz_shop_manage_group g ON g.id = gm.group_id
|
||||
WHERE gm.user_id IN
|
||||
<foreach collection='userIds' item='userId' open='(' separator=',' close=')'>
|
||||
#{userId}
|
||||
</foreach>
|
||||
ORDER BY g.id ASC
|
||||
</script>
|
||||
""")
|
||||
List<com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef> selectGroupNamesByUserIds(
|
||||
@Param("userIds") List<Long> userIds);
|
||||
|
||||
/** 某人作为组长(创建人)的分组(密钥管理等按组隔离场景用)。 */
|
||||
@Select("""
|
||||
SELECT g.id AS id, g.group_name AS groupName
|
||||
FROM biz_shop_manage_group g
|
||||
WHERE g.created_by_id = #{operatorId} OR g.user_id = #{operatorId}
|
||||
ORDER BY g.id ASC
|
||||
""")
|
||||
List<ShopManageGroupEntity> selectLedGroups(@Param("operatorId") Long operatorId);
|
||||
|
||||
/** 全部分组(超管筛选项用)。 */
|
||||
@Select("""
|
||||
SELECT g.id AS id, g.group_name AS groupName
|
||||
FROM biz_shop_manage_group g
|
||||
ORDER BY g.id ASC
|
||||
""")
|
||||
List<ShopManageGroupEntity> selectAllGroups();
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.nanri.aiimage.modules.shopkey.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/** 用户→分组名映射行(密钥管理列表展示用户所属分组用)。 */
|
||||
@Data
|
||||
public class UserGroupRef {
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String groupName;
|
||||
}
|
||||
+3
-3
@@ -39,19 +39,19 @@ public class AdminUserApiSecretController {
|
||||
private final AdminAuthSupport adminAuthSupport;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "分页查询用户密钥", description = "一行一用户;keyword 匹配用户名,checkStatus 按行级状态筛选。主管只能看自己名下子账户,超管看全量并可按创建人筛选。")
|
||||
@Operation(summary = "分页查询用户密钥", description = "一行一用户;keyword 匹配用户名,checkStatus 按行级状态筛选。主管只能看自己带的分组成员,超管看全量并可按分组筛选。")
|
||||
public ApiResponse<AdminUserSecretPageVo> page(
|
||||
HttpServletRequest request,
|
||||
@Parameter(description = "关键字:用户名") @RequestParam(required = false) String keyword,
|
||||
@Parameter(description = "行级状态筛选:passed/failed/incomplete/error/unknown") @RequestParam(required = false) String checkStatus,
|
||||
@Parameter(description = "按创建人筛选(仅超管生效)") @RequestParam(name = "created_by_id", required = false) Long createdById,
|
||||
@Parameter(description = "按数据权限分组筛选(仅超管生效)") @RequestParam(name = "group_id", required = false) Long groupId,
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Long page,
|
||||
@Parameter(description = "每页数量") @RequestParam(defaultValue = "15") Long pageSize) {
|
||||
AdminUserEntity operator = adminAuthSupport.requireAdmin(request);
|
||||
AdminUserSecretQuery query = new AdminUserSecretQuery();
|
||||
query.setKeyword(keyword);
|
||||
query.setCheckStatus(checkStatus);
|
||||
query.setCreatedById(createdById);
|
||||
query.setGroupId(groupId);
|
||||
query.setPage(page);
|
||||
query.setPageSize(pageSize);
|
||||
return ApiResponse.success(userApiSecretService.adminPage(operator, query));
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ public class AdminUserSecretQuery {
|
||||
@Schema(description = "行级状态筛选:passed/failed/incomplete/error/unknown")
|
||||
private String checkStatus;
|
||||
|
||||
@Schema(description = "按创建人筛选(仅超管生效;主管强制为本组)")
|
||||
private Long createdById;
|
||||
@Schema(description = "按数据权限分组筛选(超管可选;主管强制为自己带的分组)")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "页码,从 1 开始")
|
||||
private Long page = 1L;
|
||||
|
||||
+7
@@ -20,4 +20,11 @@ public class AdminUserSecretPageVo {
|
||||
|
||||
@Schema(description = "每页数量")
|
||||
private Long pageSize;
|
||||
|
||||
@Schema(description = "分组筛选项(超管=全部分组,主管=自己带的分组)")
|
||||
private List<GroupOptionVo> groupOptions;
|
||||
|
||||
@Schema(description = "分组筛选项")
|
||||
public record GroupOptionVo(Long id, String groupName) {
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "后台密钥管理-单用户一行(三个字段列 + 行级状态)")
|
||||
@@ -15,11 +16,8 @@ public class AdminUserSecretRowVo {
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "所属管理员(创建人)ID")
|
||||
private Long createdById;
|
||||
|
||||
@Schema(description = "所属管理员用户名")
|
||||
private String createdByUsername;
|
||||
@Schema(description = "所属分组名(数据权限分组,可能多个)")
|
||||
private List<String> groups;
|
||||
|
||||
@Schema(description = "货源查询密钥")
|
||||
private AdminUserSecretModuleVo similarAsin;
|
||||
|
||||
+102
-31
@@ -6,6 +6,9 @@ import com.nanri.aiimage.common.security.ShopCredentialCryptoService;
|
||||
import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
|
||||
import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
|
||||
import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
|
||||
import com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper;
|
||||
import com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef;
|
||||
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
|
||||
import com.nanri.aiimage.modules.usersecret.client.JikipProxyClient;import com.nanri.aiimage.modules.usersecret.mapper.UserApiSecretMapper;
|
||||
import com.nanri.aiimage.modules.usersecret.model.dto.AdminUserSecretQuery;
|
||||
import com.nanri.aiimage.modules.usersecret.model.dto.UserApiSecretMigrateRequest;
|
||||
@@ -69,6 +72,7 @@ public class UserApiSecretService {
|
||||
private final JikipProxyClient jikipProxyClient;
|
||||
private final AdminUserMapper adminUserMapper;
|
||||
private final AdminAuthSupport adminAuthSupport;
|
||||
private final ShopManageGroupMapper adminGroupMapper;
|
||||
|
||||
/** 当前用户密钥包:仅必填模块(代理为选配,不下发)+ 服务端下发的必填清单 + 完整性判定。 */
|
||||
public UserApiSecretBundleVo bundle(Long userId) {
|
||||
@@ -200,43 +204,50 @@ public class UserApiSecretService {
|
||||
* 先按关键字圈定用户,再全量聚合、行级状态筛选、按最近更新时间倒序后内存分页
|
||||
* (当前规模为用户数×3,内存聚合成本可控;量级上来后可改为 GROUP BY 下推)。
|
||||
*/
|
||||
/** 后台分页查询:主管(admin)只能看自己名下子账户(created_by_id=自己),超管看全量可按创建人筛选。 */
|
||||
/** 后台分页查询:主管(admin)只看自己带的「数据权限分组」成员;超管看全量、可按分组筛选。 */
|
||||
public AdminUserSecretPageVo adminPage(AdminUserEntity operator, AdminUserSecretQuery query) {
|
||||
AdminUserSecretQuery safeQuery = query == null ? new AdminUserSecretQuery() : query;
|
||||
long page = safeQuery.getPage() == null || safeQuery.getPage() < 1 ? 1L : safeQuery.getPage();
|
||||
long pageSize = safeQuery.getPageSize() == null || safeQuery.getPageSize() < 1
|
||||
? 15L : Math.min(safeQuery.getPageSize(), 100L);
|
||||
|
||||
// 组隔离:主管强制锁定本组;超管可按 created_by_id 筛选(0 或空 = 全部)。
|
||||
boolean superAdmin = "super_admin".equals(adminAuthSupport.currentRole(operator));
|
||||
Long scopedCreatedById;
|
||||
Long requestedGroupId = safeQuery.getGroupId();
|
||||
List<Long> ledGroupIds = superAdmin ? List.of() : listLedGroupIds(operator.getId());
|
||||
// 组隔离:主管强制锁定自己带的分组;超管按 group_id 筛选(空 = 全部)。
|
||||
List<Long> scopedGroupIds;
|
||||
if (superAdmin) {
|
||||
Long filter = safeQuery.getCreatedById();
|
||||
scopedCreatedById = filter != null && filter > 0 ? filter : null;
|
||||
scopedGroupIds = requestedGroupId != null && requestedGroupId > 0 ? List.of(requestedGroupId) : null;
|
||||
} else {
|
||||
scopedCreatedById = operator.getId();
|
||||
if (ledGroupIds.isEmpty()) {
|
||||
log.info("[user-secret] 后台密钥列表:主管未带任何分组,返回空 operatorId={}", operator.getId());
|
||||
return emptyPageWithGroups(page, pageSize, List.of());
|
||||
}
|
||||
scopedGroupIds = ledGroupIds;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<UserApiSecretEntity> wrapper = new LambdaQueryWrapper<>();
|
||||
String keyword = normalize(safeQuery.getKeyword());
|
||||
List<Long> allowedUserIds = null;
|
||||
// 用户存在性/归属先按 users 表圈定:keyword 匹配 + 组隔离前置过滤(无密钥记录的用户本来就不在聚合表里)。
|
||||
LambdaQueryWrapper<AdminUserEntity> userWrapper = new LambdaQueryWrapper<>();
|
||||
if (!keyword.isEmpty()) {
|
||||
userWrapper.like(AdminUserEntity::getUsername, keyword);
|
||||
}
|
||||
if (scopedCreatedById != null) {
|
||||
userWrapper.eq(AdminUserEntity::getCreatedById, scopedCreatedById);
|
||||
}
|
||||
if (!keyword.isEmpty() || scopedCreatedById != null) {
|
||||
allowedUserIds = adminUserMapper.selectList(userWrapper).stream()
|
||||
.map(AdminUserEntity::getId)
|
||||
.filter(id -> id != null)
|
||||
.toList();
|
||||
if (!keyword.isEmpty() || scopedGroupIds != null) {
|
||||
// 分组圈定成员用户(含组长本人);再叠加用户名关键字。
|
||||
List<Long> allowedUserIds = scopedGroupIds == null
|
||||
? new ArrayList<>()
|
||||
: new ArrayList<>(adminGroupMapper.selectUserIdsByGroupIds(scopedGroupIds));
|
||||
if (!keyword.isEmpty()) {
|
||||
List<Long> matched = adminUserMapper.selectList(new LambdaQueryWrapper<AdminUserEntity>()
|
||||
.like(AdminUserEntity::getUsername, keyword)
|
||||
.last("limit 200"))
|
||||
.stream().map(AdminUserEntity::getId).filter(id -> id != null).toList();
|
||||
if (scopedGroupIds == null) {
|
||||
allowedUserIds = new ArrayList<>(matched);
|
||||
} else {
|
||||
allowedUserIds.retainAll(matched);
|
||||
}
|
||||
}
|
||||
if (allowedUserIds.isEmpty()) {
|
||||
log.info("[user-secret] 后台密钥列表无匹配用户 keyword={} createdById={} operatorId={} role={}",
|
||||
keyword, scopedCreatedById, operator.getId(), superAdmin ? "super_admin" : "admin");
|
||||
return emptyPage(page, pageSize);
|
||||
log.info("[user-secret] 后台密钥列表无匹配用户 keyword={} groupIds={} operatorId={} role={}",
|
||||
keyword, scopedGroupIds, operator.getId(), superAdmin ? "super_admin" : "admin");
|
||||
return emptyPageWithGroups(page, pageSize, groupOptions(operator, superAdmin, ledGroupIds));
|
||||
}
|
||||
wrapper.in(UserApiSecretEntity::getUserId, allowedUserIds);
|
||||
}
|
||||
@@ -265,13 +276,77 @@ public class UserApiSecretService {
|
||||
long total = all.size();
|
||||
int from = (int) Math.min((page - 1) * pageSize, total);
|
||||
int to = (int) Math.min(from + pageSize, total);
|
||||
List<AdminUserSecretRowVo> pageItems = new ArrayList<>(all.subList(from, to));
|
||||
fillRowGroups(pageItems);
|
||||
|
||||
AdminUserSecretPageVo vo = new AdminUserSecretPageVo();
|
||||
vo.setItems(new ArrayList<>(all.subList(from, to)));
|
||||
vo.setItems(pageItems);
|
||||
vo.setTotal(total);
|
||||
vo.setPage(page);
|
||||
vo.setPageSize(pageSize);
|
||||
log.info("[user-secret] 后台密钥列表 keyword={} statusFilter={} createdById={} role={} 聚合用户数={} 本页返回={}",
|
||||
keyword, statusFilter, scopedCreatedById, superAdmin ? "super_admin" : "admin", total, vo.getItems().size());
|
||||
vo.setGroupOptions(groupOptions(operator, superAdmin, ledGroupIds));
|
||||
log.info("[user-secret] 后台密钥列表 keyword={} statusFilter={} groupIds={} role={} 聚合用户数={} 本页返回={}",
|
||||
keyword, statusFilter, scopedGroupIds, superAdmin ? "super_admin" : "admin", total, pageItems.size());
|
||||
return vo;
|
||||
}
|
||||
|
||||
/** 主管带的分组 ID(created_by_id / user_id = 自己)。 */
|
||||
private List<Long> listLedGroupIds(Long operatorId) {
|
||||
if (operatorId == null) {
|
||||
return List.of();
|
||||
}
|
||||
return adminGroupMapper.selectLedGroups(operatorId).stream()
|
||||
.map(ShopManageGroupEntity::getId)
|
||||
.filter(id -> id != null && id > 0)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/** 分组筛选项:超管=全部;主管=自己带的分组(没有则不展示下拉)。 */
|
||||
private List<AdminUserSecretPageVo.GroupOptionVo> groupOptions(
|
||||
AdminUserEntity operator, boolean superAdmin, List<Long> ledGroupIds) {
|
||||
List<ShopManageGroupEntity> groups;
|
||||
if (superAdmin) {
|
||||
groups = adminGroupMapper.selectAllGroups();
|
||||
} else if (ledGroupIds.isEmpty()) {
|
||||
return List.of();
|
||||
} else {
|
||||
groups = adminGroupMapper.selectLedGroups(operator.getId());
|
||||
}
|
||||
return groups.stream()
|
||||
.filter(group -> group.getId() != null)
|
||||
.map(group -> new AdminUserSecretPageVo.GroupOptionVo(group.getId(), group.getGroupName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/** 本页行补「所属分组」:一次批量查询,避免逐行查库。 */
|
||||
private void fillRowGroups(List<AdminUserSecretRowVo> pageItems) {
|
||||
List<Long> userIds = pageItems.stream()
|
||||
.map(AdminUserSecretRowVo::getUserId)
|
||||
.filter(id -> id != null)
|
||||
.toList();
|
||||
if (userIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, List<String>> byUser = new LinkedHashMap<>();
|
||||
for (UserGroupRef ref : adminGroupMapper.selectGroupNamesByUserIds(userIds)) {
|
||||
if (ref.getUserId() == null) {
|
||||
continue;
|
||||
}
|
||||
String name = normalize(ref.getGroupName());
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
byUser.computeIfAbsent(ref.getUserId(), key -> new ArrayList<>()).add(name);
|
||||
}
|
||||
for (AdminUserSecretRowVo item : pageItems) {
|
||||
item.setGroups(byUser.getOrDefault(item.getUserId(), List.of()));
|
||||
}
|
||||
}
|
||||
|
||||
private AdminUserSecretPageVo emptyPageWithGroups(
|
||||
long page, long pageSize, List<AdminUserSecretPageVo.GroupOptionVo> groupOptions) {
|
||||
AdminUserSecretPageVo vo = emptyPage(page, pageSize);
|
||||
vo.setGroupOptions(groupOptions);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@@ -412,11 +487,7 @@ public class UserApiSecretService {
|
||||
vo.setUpdatedAt(latestUpdatedAt(modules));
|
||||
AdminUserEntity user = adminUserMapper.selectById(userId);
|
||||
vo.setUsername(user == null ? "" : user.getUsername());
|
||||
if (user != null && user.getCreatedById() != null) {
|
||||
vo.setCreatedById(user.getCreatedById());
|
||||
AdminUserEntity creator = adminUserMapper.selectById(user.getCreatedById());
|
||||
vo.setCreatedByUsername(creator == null ? "" : creator.getUsername());
|
||||
}
|
||||
vo.setGroups(List.of());
|
||||
return vo;
|
||||
}
|
||||
|
||||
|
||||
+59
-16
@@ -4,6 +4,8 @@ import com.nanri.aiimage.common.security.ShopCredentialCryptoService;
|
||||
import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
|
||||
import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
|
||||
import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
|
||||
import com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef;
|
||||
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
|
||||
import com.nanri.aiimage.modules.usersecret.client.JikipProxyClient;
|
||||
import com.nanri.aiimage.modules.usersecret.mapper.UserApiSecretMapper;
|
||||
import com.nanri.aiimage.modules.usersecret.model.dto.AdminUserSecretQuery;
|
||||
@@ -32,6 +34,8 @@ class UserApiSecretServiceTest {
|
||||
private final JikipProxyClient jikipProxyClient = mock(JikipProxyClient.class);
|
||||
private final AdminUserMapper adminUserMapper = mock(AdminUserMapper.class);
|
||||
private final AdminAuthSupport adminAuthSupport = mock(AdminAuthSupport.class);
|
||||
private final com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper adminGroupMapper =
|
||||
mock(com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper.class);
|
||||
|
||||
private UserApiSecretService newService() {
|
||||
when(crypto.encrypt(anyString())).thenAnswer(inv -> "enc:" + inv.getArgument(0, String.class));
|
||||
@@ -41,7 +45,8 @@ class UserApiSecretServiceTest {
|
||||
});
|
||||
// 默认按主管(admin)判定;超管用例里单独改打桩。
|
||||
when(adminAuthSupport.currentRole(any())).thenReturn("admin");
|
||||
return new UserApiSecretService(mapper, crypto, checkService, jikipProxyClient, adminUserMapper, adminAuthSupport);
|
||||
return new UserApiSecretService(
|
||||
mapper, crypto, checkService, jikipProxyClient, adminUserMapper, adminAuthSupport, adminGroupMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -210,8 +215,6 @@ class UserApiSecretServiceTest {
|
||||
@Test
|
||||
void adminPageAggregatesOneRowPerUserWithProxyMasked() {
|
||||
UserApiSecretService service = newService();
|
||||
// 非限定查询:无 keyword 无组过滤时不触发 users 表圈定查询。
|
||||
when(adminUserMapper.selectList(any())).thenReturn(List.of());
|
||||
when(mapper.selectList(any())).thenReturn(List.of(
|
||||
row(1L, "similar-asin", "enc:sk-sa-1234", "passed"),
|
||||
row(1L, "appearance-patent", "enc:sk-ap-5678", "passed"),
|
||||
@@ -220,11 +223,11 @@ class UserApiSecretServiceTest {
|
||||
user.setId(1L);
|
||||
user.setUsername("张三");
|
||||
when(adminUserMapper.selectById(1L)).thenReturn(user);
|
||||
AdminUserEntity creator = new AdminUserEntity();
|
||||
creator.setId(99L);
|
||||
creator.setUsername("主管甲");
|
||||
when(adminUserMapper.selectById(99L)).thenReturn(creator);
|
||||
user.setCreatedById(99L);
|
||||
// 分组列:用户 1 属于「一组」
|
||||
UserGroupRef ref = new UserGroupRef();
|
||||
ref.setUserId(1L);
|
||||
ref.setGroupName("一组");
|
||||
when(adminGroupMapper.selectGroupNamesByUserIds(any())).thenReturn(List.of(ref));
|
||||
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
@@ -234,8 +237,7 @@ class UserApiSecretServiceTest {
|
||||
assertThat(page.getItems()).hasSize(1);
|
||||
var rowVo = page.getItems().get(0);
|
||||
assertThat(rowVo.getUsername()).isEqualTo("张三");
|
||||
assertThat(rowVo.getCreatedById()).isEqualTo(99L);
|
||||
assertThat(rowVo.getCreatedByUsername()).isEqualTo("主管甲");
|
||||
assertThat(rowVo.getGroups()).containsExactly("一组");
|
||||
assertThat(rowVo.getStatus()).isEqualTo("failed");
|
||||
assertThat(rowVo.getSimilarAsin().getMasked()).isEqualTo("sk-s****1234");
|
||||
assertThat(rowVo.getProxy().getExists()).isTrue();
|
||||
@@ -243,20 +245,61 @@ class UserApiSecretServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminPageLocksAdminToOwnGroupUsers() {
|
||||
void adminPageLocksAdminToOwnLedGroups() {
|
||||
UserApiSecretService service = newService();
|
||||
// 主管无论传什么 createdById,都强制锁定为本组(created_by_id=88L)。
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
when(adminUserMapper.selectList(any())).thenReturn(List.of());
|
||||
// 主管未带任何分组 → 直接空页,不查密钥表。
|
||||
when(adminGroupMapper.selectLedGroups(88L)).thenReturn(List.of());
|
||||
|
||||
service.adminPage(operator, new AdminUserSecretQuery());
|
||||
var page = service.adminPage(operator, new AdminUserSecretQuery());
|
||||
|
||||
// 主管模式:先圈定 users 表(组过滤),密钥表因无匹配行不再查询。
|
||||
verify(adminUserMapper).selectList(any());
|
||||
assertThat(page.getItems()).isEmpty();
|
||||
verify(mapper, never()).selectList(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminPageScopesToLedGroupMembers() {
|
||||
UserApiSecretService service = newService();
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
ShopManageGroupEntity group = new ShopManageGroupEntity();
|
||||
group.setId(5L);
|
||||
group.setGroupName("一组");
|
||||
when(adminGroupMapper.selectLedGroups(88L)).thenReturn(List.of(group));
|
||||
when(adminGroupMapper.selectUserIdsByGroupIds(any())).thenReturn(List.of(1L));
|
||||
when(mapper.selectList(any())).thenReturn(List.of(row(1L, "similar-asin", "enc:sk-1", "passed")));
|
||||
AdminUserEntity user = new AdminUserEntity();
|
||||
user.setId(1L);
|
||||
user.setUsername("张三");
|
||||
when(adminUserMapper.selectById(1L)).thenReturn(user);
|
||||
when(adminGroupMapper.selectGroupNamesByUserIds(any())).thenReturn(List.of());
|
||||
|
||||
var page = service.adminPage(operator, new AdminUserSecretQuery());
|
||||
|
||||
assertThat(page.getItems()).hasSize(1);
|
||||
assertThat(page.getGroupOptions()).extracting(com.nanri.aiimage.modules.usersecret.model.vo.AdminUserSecretPageVo.GroupOptionVo::groupName)
|
||||
.containsExactly("一组");
|
||||
verify(adminGroupMapper).selectUserIdsByGroupIds(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminPageFiltersSuperAdminByGroupId() {
|
||||
UserApiSecretService service = newService();
|
||||
AdminUserEntity operator = new AdminUserEntity();
|
||||
operator.setId(88L);
|
||||
when(adminAuthSupport.currentRole(operator)).thenReturn("super_admin");
|
||||
when(adminGroupMapper.selectUserIdsByGroupIds(List.of(5L))).thenReturn(List.of());
|
||||
when(adminGroupMapper.selectAllGroups()).thenReturn(List.of());
|
||||
|
||||
AdminUserSecretQuery query = new AdminUserSecretQuery();
|
||||
query.setGroupId(5L);
|
||||
var page = service.adminPage(operator, query);
|
||||
|
||||
assertThat(page.getItems()).isEmpty();
|
||||
verify(adminGroupMapper).selectUserIdsByGroupIds(List.of(5L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminClearByUserDeletesAllRowsOfUser() {
|
||||
UserApiSecretService service = newService();
|
||||
|
||||
Reference in New Issue
Block a user