diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/mapper/AdminUserMapper.java b/backend-java/src/main/java/com/nanri/aiimage/common/mapper/AdminUserMapper.java
similarity index 59%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/permission/mapper/AdminUserMapper.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/mapper/AdminUserMapper.java
index 4e0c7efe..90982714 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/mapper/AdminUserMapper.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/mapper/AdminUserMapper.java
@@ -1,7 +1,7 @@
-package com.nanri.aiimage.modules.permission.mapper;
+package com.nanri.aiimage.common.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/mapper/ShopManageGroupMapper.java b/backend-java/src/main/java/com/nanri/aiimage/common/mapper/ShopManageGroupMapper.java
similarity index 98%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/mapper/ShopManageGroupMapper.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/mapper/ShopManageGroupMapper.java
index fa3dd509..8a9b6f03 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/mapper/ShopManageGroupMapper.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/mapper/ShopManageGroupMapper.java
@@ -1,7 +1,7 @@
-package com.nanri.aiimage.modules.shopkey.mapper;
+package com.nanri.aiimage.common.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/model/entity/AdminUserEntity.java b/backend-java/src/main/java/com/nanri/aiimage/common/model/entity/AdminUserEntity.java
similarity index 92%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/permission/model/entity/AdminUserEntity.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/model/entity/AdminUserEntity.java
index 99fed96c..d51497ae 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/model/entity/AdminUserEntity.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/model/entity/AdminUserEntity.java
@@ -1,4 +1,4 @@
-package com.nanri.aiimage.modules.permission.model.entity;
+package com.nanri.aiimage.common.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/model/entity/ShopManageGroupEntity.java b/backend-java/src/main/java/com/nanri/aiimage/common/model/entity/ShopManageGroupEntity.java
similarity index 91%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/model/entity/ShopManageGroupEntity.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/model/entity/ShopManageGroupEntity.java
index 33e4dee2..a69e52cc 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/model/entity/ShopManageGroupEntity.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/model/entity/ShopManageGroupEntity.java
@@ -1,4 +1,4 @@
-package com.nanri.aiimage.modules.shopkey.model.entity;
+package com.nanri.aiimage.common.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/support/AdminAuthSupport.java b/backend-java/src/main/java/com/nanri/aiimage/common/security/AdminAuthSupport.java
similarity index 95%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/admin/support/AdminAuthSupport.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/security/AdminAuthSupport.java
index 6ac2390f..a9e2b688 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/support/AdminAuthSupport.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/security/AdminAuthSupport.java
@@ -1,10 +1,10 @@
-package com.nanri.aiimage.modules.admin.support;
+package com.nanri.aiimage.common.security;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.auth.service.JwtService;
-import com.nanri.aiimage.modules.auth.support.DeviceSessionPolicy;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.JwtService;
+import com.nanri.aiimage.common.security.DeviceSessionPolicy;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
@@ -12,7 +12,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
-import com.nanri.aiimage.modules.auth.config.AuthProperties;
+import com.nanri.aiimage.common.security.AuthProperties;
import org.springframework.beans.factory.annotation.Value;
import java.nio.charset.StandardCharsets;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/config/AuthProperties.java b/backend-java/src/main/java/com/nanri/aiimage/common/security/AuthProperties.java
similarity index 93%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/auth/config/AuthProperties.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/security/AuthProperties.java
index 54a44e18..7fba8829 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/config/AuthProperties.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/security/AuthProperties.java
@@ -1,4 +1,4 @@
-package com.nanri.aiimage.modules.auth.config;
+package com.nanri.aiimage.common.security;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/support/DeviceSessionPolicy.java b/backend-java/src/main/java/com/nanri/aiimage/common/security/DeviceSessionPolicy.java
similarity index 98%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/auth/support/DeviceSessionPolicy.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/security/DeviceSessionPolicy.java
index 7e5ac418..bd0a41a0 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/support/DeviceSessionPolicy.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/security/DeviceSessionPolicy.java
@@ -1,4 +1,4 @@
-package com.nanri.aiimage.modules.auth.support;
+package com.nanri.aiimage.common.security;
import com.nanri.aiimage.common.exception.BusinessException;
import io.jsonwebtoken.Claims;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/service/JwtService.java b/backend-java/src/main/java/com/nanri/aiimage/common/security/JwtService.java
similarity index 97%
rename from backend-java/src/main/java/com/nanri/aiimage/modules/auth/service/JwtService.java
rename to backend-java/src/main/java/com/nanri/aiimage/common/security/JwtService.java
index f93b170d..dbb69dc9 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/service/JwtService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/common/security/JwtService.java
@@ -1,6 +1,6 @@
-package com.nanri.aiimage.modules.auth.service;
+package com.nanri.aiimage.common.security;
-import com.nanri.aiimage.modules.auth.config.AuthProperties;
+import com.nanri.aiimage.common.security.AuthProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/config/AdminApiGuardFilter.java b/backend-java/src/main/java/com/nanri/aiimage/config/AdminApiGuardFilter.java
index 96237322..a20c0630 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/config/AdminApiGuardFilter.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/config/AdminApiGuardFilter.java
@@ -3,7 +3,7 @@ package com.nanri.aiimage.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java
index cb47af6b..f536a59a 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.admin.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.model.vo.PermissionMenuItemVo;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import com.nanri.aiimage.modules.permission.service.support.AdminMenuTreeBuilder;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminUserController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminUserController.java
index 5f81fca8..c0658e2b 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminUserController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminUserController.java
@@ -5,8 +5,8 @@ import com.nanri.aiimage.modules.admin.model.dto.AdminUserCreateRequest;
import com.nanri.aiimage.modules.admin.model.dto.AdminUserUpdateRequest;
import com.nanri.aiimage.modules.admin.model.vo.AdminUserListVo;
import com.nanri.aiimage.modules.admin.service.AdminUserService;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/service/AdminUserService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/service/AdminUserService.java
index 8a103e5d..c8e9f765 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/service/AdminUserService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/service/AdminUserService.java
@@ -8,14 +8,14 @@ import com.nanri.aiimage.modules.admin.model.dto.AdminUserUpdateRequest;
import com.nanri.aiimage.modules.admin.model.vo.AdminBriefVo;
import com.nanri.aiimage.modules.admin.model.vo.AdminUserItemVo;
import com.nanri.aiimage.modules.admin.model.vo.AdminUserListVo;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.admin.util.PinyinAbbrUtil;
import com.nanri.aiimage.modules.auth.util.WerkzeugPasswordEncoder;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
import com.nanri.aiimage.modules.permission.model.dto.UserColumnPermissionUpdateRequest;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
-import com.nanri.aiimage.modules.usersecret.service.UserApiSecretService;
+import com.nanri.aiimage.modules.admin.spi.UserSecretCleanupPort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
@@ -43,7 +43,7 @@ public class AdminUserService {
private final WerkzeugPasswordEncoder passwordEncoder;
private final AdminAuthSupport adminAuthSupport;
private final PermissionMenuService permissionMenuService;
- private final UserApiSecretService userApiSecretService;
+ private final UserSecretCleanupPort userSecretCleanupPort;
public AdminUserListVo listUsers(AdminUserEntity currentUser, Integer page, Integer pageSize,
String username, Long createdById, String roleFilter) {
@@ -303,7 +303,7 @@ public class AdminUserService {
if (affected == 0) {
throw new BusinessException("用户不存在");
}
- int secretRows = userApiSecretService.adminClearByUser(uid);
+ int secretRows = userSecretCleanupPort.adminClearByUser(uid);
log.info("[admin-user] 用户已删除 uid={} 级联清理密钥行={}", uid, secretRows);
}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/spi/UserSecretCleanupPort.java b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/spi/UserSecretCleanupPort.java
new file mode 100644
index 00000000..df86c051
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/spi/UserSecretCleanupPort.java
@@ -0,0 +1,12 @@
+package com.nanri.aiimage.modules.admin.spi;
+
+/**
+ * 删除用户时级联清理其密钥数据(2026-09 边界收敛:admin → usersecret 的类依赖改为端口)。
+ *
+ *
实现方在 usersecret 模块;返回清理的行数用于日志。
+ */
+public interface UserSecretCleanupPort {
+
+ /** 清理该用户的全部密钥相关行,返回受影响行数。 */
+ int adminClearByUser(Long userId);
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/service/AuthService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/auth/service/AuthService.java
index 61f96398..b9bd1f9d 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/auth/service/AuthService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/auth/service/AuthService.java
@@ -3,12 +3,12 @@ package com.nanri.aiimage.modules.auth.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.auth.config.AuthProperties;
+import com.nanri.aiimage.common.security.AuthProperties;
import com.nanri.aiimage.modules.auth.mapper.LoginUserMapper;
import com.nanri.aiimage.modules.auth.model.dto.LoginRequest;
import com.nanri.aiimage.modules.auth.model.entity.LoginUserEntity;
import com.nanri.aiimage.modules.auth.model.vo.LoginResultVo;
-import com.nanri.aiimage.modules.auth.support.DeviceSessionPolicy;
+import com.nanri.aiimage.common.security.DeviceSessionPolicy;
import com.nanri.aiimage.modules.auth.util.WerkzeugPasswordEncoder;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import io.jsonwebtoken.Claims;
@@ -21,6 +21,7 @@ import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import com.nanri.aiimage.common.security.JwtService;
@Service
@RequiredArgsConstructor
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/brand/service/BrandResultFileJobHandler.java b/backend-java/src/main/java/com/nanri/aiimage/modules/brand/service/BrandResultFileJobHandler.java
index 8bc0778e..72c41112 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/brand/service/BrandResultFileJobHandler.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/brand/service/BrandResultFileJobHandler.java
@@ -7,8 +7,8 @@ import com.nanri.aiimage.modules.task.model.entity.TaskFileJobEntity;
/**
* BRAND 结果文件 Job Handler(04 注册表)。
- * 注意:resultFileUrl 解析特例(resolveResultObjectKey,无 resultId 也走)
- * 保留在 Worker 公共路径 resolveResultFileUrl,Handler 不接管 URL 解析;
+ * resultFileUrl 解析特例(resolveResultObjectKey,无 resultId 也走)由本 Handler 的
+ * resolveResultFileUrl 钩子承担(2026-09:原实现在 Worker 内直接 import 本模块,构成 task → 业务依赖);
* cleanup 为空(原 cleanupAfterSuccess 无 BRAND 分支)。
*/
public class BrandResultFileJobHandler implements ResultFileJobHandler {
@@ -32,4 +32,12 @@ public class BrandResultFileJobHandler implements ResultFileJobHandler {
brandTaskService.processResultFileJob(job);
return true;
}
+
+ @Override
+ public String resolveResultFileUrl(TaskFileJobEntity job) {
+ if (job == null || job.getTaskId() == null) {
+ return null;
+ }
+ return brandTaskService.resolveResultObjectKey(job.getTaskId());
+ }
}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataController.java
index 429f72c0..cfc01900 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataController.java
@@ -2,7 +2,7 @@ package com.nanri.aiimage.modules.dedupe.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
import com.nanri.aiimage.modules.dedupe.model.dto.DedupeTotalDataCreateRequest;
import com.nanri.aiimage.modules.dedupe.model.dto.DedupeTotalDataUpdateRequest;
@@ -12,7 +12,7 @@ import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataItemVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataPageVo;
import com.nanri.aiimage.modules.dedupe.service.DedupeTotalDataService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataService.java
index 6e64a1c6..34410309 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataService.java
@@ -15,10 +15,10 @@ import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportStartVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataItemVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataPageVo;
-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.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/GroupDeletionGuard.java b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/GroupDeletionGuard.java
new file mode 100644
index 00000000..e0caf129
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/GroupDeletionGuard.java
@@ -0,0 +1,28 @@
+package com.nanri.aiimage.modules.dedupe.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.nanri.aiimage.common.exception.BusinessException;
+import com.nanri.aiimage.modules.dedupe.mapper.DedupeTotalDataMapper;
+import com.nanri.aiimage.modules.dedupe.model.entity.DedupeTotalDataEntity;
+import com.nanri.aiimage.modules.shopkey.spi.GroupDeletionGuardPort;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * 分组删除守卫:去重总数据(2026-09 从 shopkey 收回本模块,文案保持不变)。
+ */
+@Service
+@RequiredArgsConstructor
+public class GroupDeletionGuard implements GroupDeletionGuardPort {
+
+ private final DedupeTotalDataMapper dedupeTotalDataMapper;
+
+ @Override
+ public void ensureNoGroupData(Long groupId) {
+ Long count = dedupeTotalDataMapper.selectCount(new LambdaQueryWrapper()
+ .eq(DedupeTotalDataEntity::getGroupId, groupId));
+ if (count != null && count > 0) {
+ throw new BusinessException("该分组下存在数据去重总数据,无法删除");
+ }
+ }
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/digitalhuman/controller/DigitalHumanVersionController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/digitalhuman/controller/DigitalHumanVersionController.java
index e60bc09a..d0a5a6e7 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/digitalhuman/controller/DigitalHumanVersionController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/digitalhuman/controller/DigitalHumanVersionController.java
@@ -1,7 +1,7 @@
package com.nanri.aiimage.modules.digitalhuman.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.digitalhuman.model.dto.UploadVersionRequest;
import com.nanri.aiimage.modules.digitalhuman.model.vo.DigitalHumanVersionVo;
import com.nanri.aiimage.modules.digitalhuman.service.DigitalHumanVersionService;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/controller/ImageHistoryController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/controller/ImageHistoryController.java
index c99360f6..ec4cc1b7 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/controller/ImageHistoryController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/controller/ImageHistoryController.java
@@ -2,10 +2,10 @@ package com.nanri.aiimage.modules.imagehistory.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.imagehistory.model.vo.ImageHistoryListVo;
import com.nanri.aiimage.modules.imagehistory.service.ImageHistoryService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryService.java
index 36c7e644..680068c8 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryService.java
@@ -8,8 +8,8 @@ import com.nanri.aiimage.modules.imagehistory.mapper.ImageHistoryMapper;
import com.nanri.aiimage.modules.imagehistory.model.entity.ImageHistoryEntity;
import com.nanri.aiimage.modules.imagehistory.model.vo.ImageHistoryItemVo;
import com.nanri.aiimage.modules.imagehistory.model.vo.ImageHistoryListVo;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/AdminImageVideoTaskController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/AdminImageVideoTaskController.java
index 6950d75a..30f60330 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/AdminImageVideoTaskController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/AdminImageVideoTaskController.java
@@ -3,7 +3,7 @@ package com.nanri.aiimage.modules.imagevideo.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.imagevideo.service.ImageVideoAdminTaskService;
import com.nanri.aiimage.modules.imagevideo.service.ImageVideoAdminTaskService.VideoSelection;
import com.nanri.aiimage.modules.imagevideo.service.ImageVideoAdminTaskService.ZipBundle;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/ImageVideoController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/ImageVideoController.java
index f43d5050..4c1e3171 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/ImageVideoController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/imagevideo/controller/ImageVideoController.java
@@ -2,8 +2,8 @@ package com.nanri.aiimage.modules.imagevideo.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.imagevideo.model.dto.DouyinCopyRequest;
import com.nanri.aiimage.modules.imagevideo.model.dto.ImageVideoSecretSaveRequest;
import com.nanri.aiimage.modules.imagevideo.model.dto.ImageVideoVoiceCloneRequest;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataController.java
index 98e47eb9..9bb858d6 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataController.java
@@ -2,13 +2,13 @@ package com.nanri.aiimage.modules.invalidasin.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.invalidasin.model.dto.InvalidAsinDataCreateRequest;
import com.nanri.aiimage.modules.invalidasin.model.dto.InvalidAsinDataUpdateRequest;
import com.nanri.aiimage.modules.invalidasin.model.vo.InvalidAsinDataItemVo;
import com.nanri.aiimage.modules.invalidasin.model.vo.InvalidAsinDataPageVo;
import com.nanri.aiimage.modules.invalidasin.service.InvalidAsinDataService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/service/GroupDeletionGuard.java b/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/service/GroupDeletionGuard.java
new file mode 100644
index 00000000..eef76146
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/service/GroupDeletionGuard.java
@@ -0,0 +1,28 @@
+package com.nanri.aiimage.modules.invalidasin.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.nanri.aiimage.common.exception.BusinessException;
+import com.nanri.aiimage.modules.invalidasin.mapper.InvalidAsinDataMapper;
+import com.nanri.aiimage.modules.invalidasin.model.entity.InvalidAsinDataEntity;
+import com.nanri.aiimage.modules.shopkey.spi.GroupDeletionGuardPort;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * 分组删除守卫:品牌数据库(不符合 ASIN)数据(2026-09 从 shopkey 收回本模块,文案保持不变)。
+ */
+@Service
+@RequiredArgsConstructor
+public class GroupDeletionGuard implements GroupDeletionGuardPort {
+
+ private final InvalidAsinDataMapper invalidAsinDataMapper;
+
+ @Override
+ public void ensureNoGroupData(Long groupId) {
+ Long count = invalidAsinDataMapper.selectCount(new LambdaQueryWrapper()
+ .eq(InvalidAsinDataEntity::getGroupId, groupId));
+ if (count != null && count > 0) {
+ throw new BusinessException("该分组下存在品牌数据库数据,无法删除");
+ }
+ }
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataService.java
index 9abdf17e..c1732ced 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataService.java
@@ -8,7 +8,7 @@ import com.nanri.aiimage.modules.invalidasin.model.dto.InvalidAsinDataUpdateRequ
import com.nanri.aiimage.modules.invalidasin.model.entity.InvalidAsinDataEntity;
import com.nanri.aiimage.modules.invalidasin.model.vo.InvalidAsinDataItemVo;
import com.nanri.aiimage.modules.invalidasin.model.vo.InvalidAsinDataPageVo;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.shopkey.service.ShopManageGroupService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationController.java
index edbf8c80..e8308625 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationController.java
@@ -1,12 +1,12 @@
package com.nanri.aiimage.modules.notification.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.notification.model.dto.NotificationPageQuery;
import com.nanri.aiimage.modules.notification.model.vo.NotificationPageVo;
import com.nanri.aiimage.modules.notification.model.vo.NotificationSummaryVo;
import com.nanri.aiimage.modules.notification.service.NotificationService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/NotificationController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/NotificationController.java
index d2175af4..4046e59e 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/NotificationController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/controller/NotificationController.java
@@ -1,12 +1,12 @@
package com.nanri.aiimage.modules.notification.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.notification.model.dto.NotificationPageQuery;
import com.nanri.aiimage.modules.notification.model.vo.NotificationPageVo;
import com.nanri.aiimage.modules.notification.model.vo.NotificationSummaryVo;
import com.nanri.aiimage.modules.notification.service.NotificationService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchService.java
index 6c804241..044e57fa 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchService.java
@@ -1,9 +1,9 @@
package com.nanri.aiimage.modules.notification.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-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.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.support.UserDataScopeSupport;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationScanScheduler.java b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationScanScheduler.java
index bcaa40ed..5ecd2d08 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationScanScheduler.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/service/NotificationScanScheduler.java
@@ -9,8 +9,7 @@ import com.nanri.aiimage.modules.brand.mapper.BrandCrawlTaskMapper;
import com.nanri.aiimage.modules.brand.model.entity.BrandCrawlTaskEntity;
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity;
-import com.nanri.aiimage.modules.usersecret.client.JikipProxyClient;
-import com.nanri.aiimage.modules.usersecret.model.vo.UserApiSecretBalanceVo;
+import com.nanri.aiimage.modules.notification.spi.ProxyBalancePort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
@@ -67,7 +66,7 @@ public class NotificationScanScheduler {
private final NotificationDispatchService notificationDispatchService;
private final DistributedJobLockService distributedJobLockService;
private final NotificationProperties properties;
- private final JikipProxyClient jikipProxyClient;
+ private final ProxyBalancePort proxyBalancePort;
/** 上次清理日期(每天最多清理一次;双节点由分布式锁保证只有一个实例执行)。 */
private volatile LocalDate lastCleanupDate;
@@ -234,12 +233,12 @@ public class NotificationScanScheduler {
/** jikip 探测:复用余额查询接口;未配置套餐信息时跳过(返回 null 不告警)。 */
private ProbeOutcome probeJikip() {
try {
- UserApiSecretBalanceVo balance = jikipProxyClient.fetchBalance();
- if (Boolean.TRUE.equals(balance.getAvailable())) {
- return new ProbeOutcome(true, "余额接口可用 surplus=" + balance.getSurplus()
- + " balance=" + balance.getBalance());
+ ProxyBalancePort.ProxyBalanceSnapshot balance = proxyBalancePort.fetchBalance();
+ if (balance.available()) {
+ return new ProbeOutcome(true, "余额接口可用 surplus=" + balance.surplus()
+ + " balance=" + balance.balance());
}
- String message = balance.getMessage() == null ? "" : balance.getMessage();
+ String message = balance.message() == null ? "" : balance.message();
if (message.contains("未配置")) {
log.info("[notification-scan] jikip 探测跳过:{}", message);
return null;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/notification/spi/ProxyBalancePort.java b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/spi/ProxyBalancePort.java
new file mode 100644
index 00000000..bb25741c
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/notification/spi/ProxyBalancePort.java
@@ -0,0 +1,26 @@
+package com.nanri.aiimage.modules.notification.spi;
+
+/**
+ * 代理余额探测(2026-09 边界收敛:notification → usersecret 的类依赖改为端口)。
+ *
+ * 实现方在 usersecret 模块(复用其 jikip 客户端);通知巡检只做"能不能告警"的判断,
+ * 不关心上游返回结构,因此这里只暴露最小快照。
+ */
+public interface ProxyBalancePort {
+
+ /**
+ * 查询代理服务商余额。
+ *
+ * @return 余额快照;调用异常由实现方抛出(调用方负责降级)
+ */
+ ProxyBalanceSnapshot fetchBalance();
+
+ /**
+ * @param available 余额接口是否可用
+ * @param surplus 剩余额度(上游口径,可能为 null)
+ * @param balance 账户余额(上游口径,可能为 null)
+ * @param message 上游提示信息(未配置套餐时含"未配置"字样,调用方据此跳过告警)
+ */
+ record ProxyBalanceSnapshot(boolean available, String surplus, String balance, String message) {
+ }
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuController.java
index 45bc9f04..687951cd 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuController.java
@@ -1,12 +1,12 @@
package com.nanri.aiimage.modules.permission.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.permission.model.dto.PermissionMenuCreateRequest;
import com.nanri.aiimage.modules.permission.model.dto.PermissionMenuUpdateRequest;
import com.nanri.aiimage.modules.permission.model.dto.ImageVideoDataPermissionUpdateRequest;
import com.nanri.aiimage.modules.permission.model.dto.UserColumnPermissionUpdateRequest;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.model.vo.PermissionMenuItemVo;
import com.nanri.aiimage.modules.permission.model.vo.UserColumnIdsVo;
import com.nanri.aiimage.modules.permission.model.vo.ImageVideoDataPermissionUserVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/service/PermissionMenuService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/permission/service/PermissionMenuService.java
index 8ae1c348..d40e716c 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/service/PermissionMenuService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/permission/service/PermissionMenuService.java
@@ -3,13 +3,13 @@ package com.nanri.aiimage.modules.permission.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
import com.nanri.aiimage.modules.permission.mapper.PermissionMenuMapper;
import com.nanri.aiimage.modules.permission.mapper.UserColumnPermissionMapper;
import com.nanri.aiimage.modules.permission.model.dto.PermissionMenuCreateRequest;
import com.nanri.aiimage.modules.permission.model.dto.PermissionMenuUpdateRequest;
import com.nanri.aiimage.modules.permission.model.dto.UserColumnPermissionUpdateRequest;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.model.entity.PermissionMenuEntity;
import com.nanri.aiimage.modules.permission.model.entity.UserColumnPermissionEntity;
import com.nanri.aiimage.modules.permission.model.vo.ImageVideoDataPermissionUserVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/support/UserDataScopeSupport.java b/backend-java/src/main/java/com/nanri/aiimage/modules/permission/support/UserDataScopeSupport.java
index 7d31200e..3e5be769 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/permission/support/UserDataScopeSupport.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/permission/support/UserDataScopeSupport.java
@@ -1,10 +1,10 @@
package com.nanri.aiimage.modules.permission.support;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-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.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/productcategory/controller/ProductCategoryController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/productcategory/controller/ProductCategoryController.java
index 22fe4e9b..f242f210 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/productcategory/controller/ProductCategoryController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/productcategory/controller/ProductCategoryController.java
@@ -2,7 +2,7 @@ package com.nanri.aiimage.modules.productcategory.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.productcategory.model.dto.ProductCategorySaveRequest;
import com.nanri.aiimage.modules.productcategory.model.vo.ProductCategoryItemVo;
import com.nanri.aiimage.modules.productcategory.model.vo.ProductCategoryListVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskController.java
index 7dc56f1f..3359cf02 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskController.java
@@ -4,8 +4,8 @@ import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
import com.nanri.aiimage.config.HttpClientPool;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import com.nanri.aiimage.modules.shopdatacrawl.service.ShopDataCrawlTaskService;
import io.swagger.v3.oas.annotations.Operation;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTasksController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTasksController.java
index 161d8e55..02fb293e 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTasksController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTasksController.java
@@ -4,8 +4,8 @@ import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
import com.nanri.aiimage.config.HttpClientPool;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import com.nanri.aiimage.modules.shopdatacrawl.model.dto.ShopDataCrawlDownloadRowDto;
import com.nanri.aiimage.modules.shopdatacrawl.service.ShopDataCrawlAdminTasksService;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlAdminTasksService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlAdminTasksService.java
index 57ffbda0..b5ae77a2 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlAdminTasksService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlAdminTasksService.java
@@ -12,7 +12,7 @@ import com.nanri.aiimage.modules.shopdatacrawl.model.dto.ShopDataCrawlAdminRow;
import com.nanri.aiimage.modules.shopdatacrawl.model.dto.ShopDataCrawlDownloadRowDto;
import com.nanri.aiimage.modules.shopdatacrawl.model.entity.ShopDataCrawlDailyFileEntity;
import com.nanri.aiimage.modules.shopdatacrawl.model.entity.ShopDataCrawlDailyMemberEntity;
-import com.nanri.aiimage.modules.shopduplicatecheck.mapper.ShopDuplicateCheckSourceMapper;
+import com.nanri.aiimage.modules.shopdatacrawl.spi.ManagedShopNamesPort;
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
import com.nanri.aiimage.modules.task.service.TaskDistributedLockService;
@@ -47,7 +47,7 @@ public class ShopDataCrawlAdminTasksService {
private static final Set SUPPORTED_COUNTRIES = Set.of("DE", "UK", "FR", "IT", "ES");
private final ShopDataCrawlAdminTasksMapper adminTasksMapper;
- private final ShopDuplicateCheckSourceMapper duplicateCheckSourceMapper;
+ private final ManagedShopNamesPort managedShopNamesPort;
private final ShopDataCrawlItemMapper itemMapper;
private final FileResultMapper fileResultMapper;
private final ShopDataCrawlDailyFileService dailyFileService;
@@ -215,7 +215,7 @@ public class ShopDataCrawlAdminTasksService {
/** 非超管可管店铺名(trim+小写去重,与撞款可见口径一致);超管调用方传 null 表示全量。 */
public List resolveVisibleShopNameKeys(Long operatorId) {
- List names = duplicateCheckSourceMapper.selectManagedShopNames(operatorId);
+ List names = managedShopNamesPort.selectManagedShopNames(operatorId);
if (names == null || names.isEmpty()) {
return List.of();
}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/spi/ManagedShopNamesPort.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/spi/ManagedShopNamesPort.java
new file mode 100644
index 00000000..537985a2
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/spi/ManagedShopNamesPort.java
@@ -0,0 +1,14 @@
+package com.nanri.aiimage.modules.shopdatacrawl.spi;
+
+import java.util.List;
+
+/**
+ * 可管店铺名查询(2026-09 边界收敛:shopdatacrawl → shopduplicatecheck 的 Mapper 直连改为端口)。
+ *
+ * 实现方在 shopduplicatecheck 模块(口径与撞款可见范围一致)。
+ */
+public interface ManagedShopNamesPort {
+
+ /** 该操作人可管的店铺名;传 null 表示全量(超管调用方)。 */
+ List selectManagedShopNames(Long operatorId);
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopduplicatecheck/controller/AdminShopDataDuplicateCheckController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopduplicatecheck/controller/AdminShopDataDuplicateCheckController.java
index 321446c4..34dcca2a 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopduplicatecheck/controller/AdminShopDataDuplicateCheckController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopduplicatecheck/controller/AdminShopDataDuplicateCheckController.java
@@ -3,8 +3,8 @@ package com.nanri.aiimage.modules.shopduplicatecheck.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import com.nanri.aiimage.modules.shopduplicatecheck.service.ShopDataDuplicateCheckImportService;
import com.nanri.aiimage.modules.shopduplicatecheck.service.ShopDataDuplicateCheckQueryService;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopduplicatecheck/service/ManagedShopNamesPortAdapter.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopduplicatecheck/service/ManagedShopNamesPortAdapter.java
new file mode 100644
index 00000000..1812b33e
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopduplicatecheck/service/ManagedShopNamesPortAdapter.java
@@ -0,0 +1,23 @@
+package com.nanri.aiimage.modules.shopduplicatecheck.service;
+
+import com.nanri.aiimage.modules.shopdatacrawl.spi.ManagedShopNamesPort;
+import com.nanri.aiimage.modules.shopduplicatecheck.mapper.ShopDuplicateCheckSourceMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 可管店铺名端口实现(2026-09 边界收敛):Mapper 与口径留在 shopduplicatecheck 模块内。
+ */
+@Service
+@RequiredArgsConstructor
+public class ManagedShopNamesPortAdapter implements ManagedShopNamesPort {
+
+ private final ShopDuplicateCheckSourceMapper sourceMapper;
+
+ @Override
+ public List selectManagedShopNames(Long operatorId) {
+ return sourceMapper.selectManagedShopNames(operatorId);
+ }
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/QueryAsinController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/QueryAsinController.java
index 6197e95a..80acc8d0 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/QueryAsinController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/QueryAsinController.java
@@ -2,8 +2,8 @@ package com.nanri.aiimage.modules.shopkey.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.shopkey.model.dto.QueryAsinCountryUpdateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.QueryAsinCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinImportProgressVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopKeyController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopKeyController.java
index 23b6623c..c6889ae4 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopKeyController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopKeyController.java
@@ -1,7 +1,7 @@
package com.nanri.aiimage.modules.shopkey.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopKeyCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopKeyUpdateRequest;
import com.nanri.aiimage.modules.shopkey.model.vo.ShopKeyItemVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopManageController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopManageController.java
index 6efba37f..5a1a3490 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopManageController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/ShopManageController.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.shopkey.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopManageCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopManageGroupCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopManageGroupUpdateRequest;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/SkipPriceAsinController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/SkipPriceAsinController.java
index de4365c4..312ece5a 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/SkipPriceAsinController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/controller/SkipPriceAsinController.java
@@ -2,8 +2,8 @@ package com.nanri.aiimage.modules.shopkey.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.util.DownloadHeaderUtil;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.shopkey.model.dto.SkipPriceAsinCountryUpdateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.SkipPriceAsinCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinImportProgressVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java
index 42e2c1d8..a1dcb392 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/QueryAsinService.java
@@ -11,7 +11,7 @@ import com.nanri.aiimage.config.TaskPressureProperties;
import com.nanri.aiimage.modules.shopkey.mapper.QueryAsinMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.QueryAsinCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.entity.QueryAsinEntity;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinImportProgressVo;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinImportStartVo;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinItemVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageGroupService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageGroupService.java
index 1019e598..6d627f3f 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageGroupService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageGroupService.java
@@ -2,24 +2,21 @@ package com.nanri.aiimage.modules.shopkey.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.shopkey.mapper.QueryAsinMapper;
-import com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper;
+import com.nanri.aiimage.modules.shopkey.spi.GroupDeletionGuardPort;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
import com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMemberMapper;
import com.nanri.aiimage.modules.shopkey.mapper.ShopManageMapper;
import com.nanri.aiimage.modules.shopkey.mapper.SkipPriceAsinMapper;
-import com.nanri.aiimage.modules.invalidasin.mapper.InvalidAsinDataMapper;
-import com.nanri.aiimage.modules.dedupe.mapper.DedupeTotalDataMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopManageGroupCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopManageGroupUpdateRequest;
import com.nanri.aiimage.modules.shopkey.model.entity.QueryAsinEntity;
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageEntity;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupMemberEntity;
import com.nanri.aiimage.modules.shopkey.model.entity.SkipPriceAsinEntity;
-import com.nanri.aiimage.modules.invalidasin.model.entity.InvalidAsinDataEntity;
-import com.nanri.aiimage.modules.dedupe.model.entity.DedupeTotalDataEntity;
import com.nanri.aiimage.modules.shopkey.model.vo.ShopManageGroupItemVo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -40,11 +37,11 @@ public class ShopManageGroupService {
private final ShopManageGroupMapper groupMapper;
private final ShopManageGroupMemberMapper groupMemberMapper;
+ /** 各业务模块的分组删除守卫(Spring 注入全部实现) */
+ private final java.util.List groupDeletionGuards;
private final ShopManageMapper shopManageMapper;
private final QueryAsinMapper queryAsinMapper;
private final SkipPriceAsinMapper skipPriceAsinMapper;
- private final InvalidAsinDataMapper invalidAsinDataMapper;
- private final DedupeTotalDataMapper dedupeTotalDataMapper;
private final AdminUserMapper adminUserMapper;
public List list() {
@@ -166,15 +163,9 @@ public class ShopManageGroupService {
if (queryAsinCount != null && queryAsinCount > 0) {
throw new BusinessException("该分组下存在查询 ASIN,无法删除");
}
- Long invalidAsinDataCount = invalidAsinDataMapper.selectCount(new LambdaQueryWrapper()
- .eq(InvalidAsinDataEntity::getGroupId, entity.getId()));
- if (invalidAsinDataCount != null && invalidAsinDataCount > 0) {
- throw new BusinessException("该分组下存在品牌数据库数据,无法删除");
- }
- Long dedupeTotalDataCount = dedupeTotalDataMapper.selectCount(new LambdaQueryWrapper()
- .eq(DedupeTotalDataEntity::getGroupId, entity.getId()));
- if (dedupeTotalDataCount != null && dedupeTotalDataCount > 0) {
- throw new BusinessException("该分组下存在数据去重总数据,无法删除");
+ // 其它模块的数据残留由各模块自己实现的守卫校验(2026-09 边界收敛:shopkey 不再直连它们的表)
+ for (GroupDeletionGuardPort guard : groupDeletionGuards) {
+ guard.ensureNoGroupData(entity.getId());
}
groupMemberMapper.delete(new LambdaQueryWrapper()
.eq(ShopManageGroupMemberEntity::getGroupId, entity.getId()));
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java
index c1c3a2f3..5f11aad8 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java
@@ -7,7 +7,7 @@ import com.nanri.aiimage.modules.shopkey.mapper.ShopManageMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopManageCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.ShopManageUpdateRequest;
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageEntity;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.shopkey.model.vo.ShopManageCredentialVo;
import com.nanri.aiimage.modules.shopkey.model.vo.ShopManageItemVo;
import com.nanri.aiimage.modules.shopkey.model.vo.ShopManagePageVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinService.java
index 2d96b43c..d2a59a06 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinService.java
@@ -13,7 +13,7 @@ import com.nanri.aiimage.modules.shopkey.mapper.ShopManageMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.SkipPriceAsinCreateRequest;
import com.nanri.aiimage.modules.shopkey.model.dto.SkipPriceAsinDetailDto;
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageEntity;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.shopkey.model.entity.SkipPriceAsinEntity;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinImportProgressVo;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinImportStartVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/spi/GroupDeletionGuardPort.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/spi/GroupDeletionGuardPort.java
new file mode 100644
index 00000000..7a6d0040
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/spi/GroupDeletionGuardPort.java
@@ -0,0 +1,15 @@
+package com.nanri.aiimage.modules.shopkey.spi;
+
+/**
+ * 分组删除守卫(2026-09 边界收敛):分组下若仍存在某模块的业务数据,拒绝删除该分组。
+ *
+ * 原实现由 shopkey 直接 count 其它模块的表(invalidasin / dedupe),构成 shopkey → 业务 的反向依赖;
+ * 现改为各业务模块自己实现本端口(Spring 注入 List 后逐个校验),新增模块只需加一个实现 Bean。
+ *
+ *
实现应在"存在数据"时抛出 {@code BusinessException},文案自带模块名(沿用历史文案,前端无需改)。
+ */
+public interface GroupDeletionGuardPort {
+
+ /** 校验该分组下本模块无残留数据;有则抛业务异常。 */
+ void ensureNoGroupData(Long groupId);
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/softwareversion/controller/SoftwareVersionAdminController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/softwareversion/controller/SoftwareVersionAdminController.java
index cf544986..2ea5fdb4 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/softwareversion/controller/SoftwareVersionAdminController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/softwareversion/controller/SoftwareVersionAdminController.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.softwareversion.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.softwareversion.service.SoftwareVersionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandler.java b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandler.java
index 6bda251d..807eb306 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandler.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandler.java
@@ -37,6 +37,15 @@ public interface ResultFileJobHandler {
return false;
}
+ /**
+ * 模块自定义的结果对象 key 解析:返回非空时覆盖默认路径(按 resultId 查 biz_file_result)。
+ * BRAND 的结果文件 job 上没有 resultId,需要用任务 id 反查品牌侧存放的对象 key。
+ * 实现应自吞异常(解析失败按 null 处理,走默认路径)。
+ */
+ default String resolveResultFileUrl(TaskFileJobEntity job) {
+ return null;
+ }
+
default boolean supportsAsyncOffload() {
return false;
}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorker.java b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorker.java
index 454afd23..338c6dc6 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorker.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorker.java
@@ -3,11 +3,9 @@ package com.nanri.aiimage.modules.task.service;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.common.exception.TaskOwnerMismatchException;
import com.nanri.aiimage.config.InstanceMetadata;
-import com.nanri.aiimage.modules.brand.service.BrandTaskService;
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
import com.nanri.aiimage.modules.task.model.entity.TaskFileJobEntity;
-import com.nanri.aiimage.modules.withdraw.service.WithdrawTaskService;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -34,8 +32,6 @@ public class TaskResultFileJobWorker {
private final FileResultMapper fileResultMapper;
private final TaskFileJobLocalDispatcher taskFileJobLocalDispatcher;
private final InstanceMetadata instanceMetadata;
- private final WithdrawTaskService withdrawTaskService;
- private final BrandTaskService brandTaskService;
private final ResultFileJobHandlerRegistry handlerRegistry;
@Autowired
@Qualifier("taskQueueExecutor")
@@ -178,7 +174,6 @@ public class TaskResultFileJobWorker {
private void processInternal(TaskFileJobEntity job) {
long startedAt = System.currentTimeMillis();
- boolean finalizeWithdraw = false;
try {
TaskFileJobEntity latest = taskFileJobService.findById(job.getId());
if (latest != null && "SUCCESS".equals(latest.getStatus())) {
@@ -219,13 +214,15 @@ public class TaskResultFileJobWorker {
String resultFileUrl = resolveResultFileUrl(job);
taskFileJobService.markSuccess(job, resultFileUrl);
cleanupAfterSuccess(job);
- finalizeWithdraw = "WITHDRAW".equals(job.getModuleType());
log.info("[task-file-job] process success jobId={} taskId={} moduleType={} resultId={} elapsedMs={} resultFileUrl={} stage=SUCCESS",
job.getId(), job.getTaskId(), job.getModuleType(), job.getResultId(),
System.currentTimeMillis() - startedAt, resultFileUrl);
}
- if (finalizeWithdraw) {
- withdrawTaskService.tryFinalizeTask(job.getTaskId(), false);
+ // 成功后回调解耦(原 WITHDRAW 特判):时机与历史一致——锁已释放、markSuccess 之后;
+ // 各模块需要"任务收尾"时实现 ResultFileJobHandler.onSuccess,task 侧不再 import 业务模块
+ ResultFileJobHandler successHandler = handlerRegistry.asMap().get(job.getModuleType());
+ if (successHandler != null) {
+ successHandler.onSuccess(job);
}
} catch (TaskOwnerMismatchException ex) {
// 归属校验失败不是"生成失败":不能计入 retryCount,否则任务会在归属机
@@ -308,8 +305,12 @@ public class TaskResultFileJobWorker {
}
private String resolveResultFileUrl(TaskFileJobEntity job) {
- if ("BRAND".equals(job.getModuleType())) {
- return brandTaskService.resolveResultObjectKey(job.getTaskId());
+ ResultFileJobHandler handler = handlerRegistry.asMap().get(job.getModuleType());
+ if (handler != null) {
+ String custom = handler.resolveResultFileUrl(job);
+ if (custom != null && !custom.isBlank()) {
+ return custom;
+ }
}
if (job.getResultId() == null) {
return null;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/tutorial/controller/TutorialAdminController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/tutorial/controller/TutorialAdminController.java
index 44b47091..0107c34a 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/tutorial/controller/TutorialAdminController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/tutorial/controller/TutorialAdminController.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.tutorial.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.tutorial.service.TutorialPackageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserApiSecretController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserApiSecretController.java
index 9a3e00bc..1d926645 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserApiSecretController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserApiSecretController.java
@@ -2,8 +2,8 @@ package com.nanri.aiimage.modules.usersecret.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.config.UserSecretProperties;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.usersecret.model.dto.AdminUserSecretQuery;
import com.nanri.aiimage.modules.usersecret.model.vo.AdminUserSecretPageVo;
import com.nanri.aiimage.modules.usersecret.model.vo.UserApiSecretCheckResultVo;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserSecretUsageController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserSecretUsageController.java
index 561503fa..6f3d438e 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserSecretUsageController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/AdminUserSecretUsageController.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.usersecret.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.usersecret.model.dto.AdminUserSecretUsageQuery;
import com.nanri.aiimage.modules.usersecret.model.vo.AdminUserSecretUsagePageVo;
import com.nanri.aiimage.modules.usersecret.service.UserSecretUsageService;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyController.java
index b71110f3..9c8e7d25 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyController.java
@@ -2,7 +2,7 @@ package com.nanri.aiimage.modules.usersecret.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.usersecret.service.UserApiSecretService;
import com.nanri.aiimage.modules.usersecret.support.UserSecretModule;
import io.swagger.v3.oas.annotations.Operation;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserSecretUsageController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserSecretUsageController.java
index 556e2551..8bcd5f9b 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserSecretUsageController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserSecretUsageController.java
@@ -1,7 +1,7 @@
package com.nanri.aiimage.modules.usersecret.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.usersecret.model.dto.UserSecretUsageReportRequest;
import com.nanri.aiimage.modules.usersecret.service.UserSecretUsageService;
import io.swagger.v3.oas.annotations.Operation;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/UserApiSecretController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/UserApiSecretController.java
index e684520a..ccb64398 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/UserApiSecretController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/controller/UserApiSecretController.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.usersecret.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.usersecret.model.dto.UserApiSecretCheckRequest;
import com.nanri.aiimage.modules.usersecret.model.dto.UserApiSecretMigrateRequest;
import com.nanri.aiimage.modules.usersecret.model.dto.UserApiSecretSaveRequest;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/ProxyBalancePortAdapter.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/ProxyBalancePortAdapter.java
new file mode 100644
index 00000000..b4d9a511
--- /dev/null
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/ProxyBalancePortAdapter.java
@@ -0,0 +1,30 @@
+package com.nanri.aiimage.modules.usersecret.service;
+
+import com.nanri.aiimage.modules.notification.spi.ProxyBalancePort;
+import com.nanri.aiimage.modules.usersecret.client.JikipProxyClient;
+import com.nanri.aiimage.modules.usersecret.model.vo.UserApiSecretBalanceVo;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * 代理余额探测端口实现(2026-09 边界收敛):把 jikip 客户端与余额 VO 收敛在 usersecret 模块内。
+ */
+@Service
+@RequiredArgsConstructor
+public class ProxyBalancePortAdapter implements ProxyBalancePort {
+
+ private final JikipProxyClient jikipProxyClient;
+
+ @Override
+ public ProxyBalanceSnapshot fetchBalance() {
+ UserApiSecretBalanceVo balance = jikipProxyClient.fetchBalance();
+ if (balance == null) {
+ return new ProxyBalanceSnapshot(false, null, null, "余额接口返回空");
+ }
+ return new ProxyBalanceSnapshot(
+ Boolean.TRUE.equals(balance.getAvailable()),
+ balance.getSurplus(),
+ balance.getBalance(),
+ balance.getMessage());
+ }
+}
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretService.java
index 28da64f9..65dcccd0 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretService.java
@@ -2,17 +2,18 @@ package com.nanri.aiimage.modules.usersecret.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nanri.aiimage.common.exception.BusinessException;
+import com.nanri.aiimage.modules.admin.spi.UserSecretCleanupPort;
import com.nanri.aiimage.common.security.ShopCredentialCryptoService;
import com.nanri.aiimage.config.UserSecretProperties;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.notification.service.NotificationDispatchService;
import com.nanri.aiimage.modules.notification.service.NotificationService;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.support.UserDataScopeSupport;
-import com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.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;
@@ -53,7 +54,7 @@ import java.util.Set;
@Service
@RequiredArgsConstructor
@Slf4j
-public class UserApiSecretService {
+public class UserApiSecretService implements UserSecretCleanupPort {
public static final String SOURCE_CLIENT = "client";
public static final String SOURCE_ADMIN = "admin";
@@ -412,6 +413,7 @@ public class UserApiSecretService {
/** 后台:清空该用户全部密钥与代理配置。 */
@Transactional
+ @Override
public int adminClearByUser(Long userId) {
requireUserId(userId);
int deleted = userApiSecretMapper.delete(new LambdaQueryWrapper()
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageService.java
index e645c78f..da4ffa88 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageService.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageService.java
@@ -1,13 +1,13 @@
package com.nanri.aiimage.modules.usersecret.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-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.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.support.UserDataScopeSupport;
-import com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.usersecret.mapper.UserSecretUsageMapper;
import com.nanri.aiimage.modules.usersecret.model.dto.AdminUserSecretUsageQuery;
import com.nanri.aiimage.modules.usersecret.model.entity.UserSecretUsageEntity;
diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthController.java
index e2d84067..d17f59f7 100644
--- a/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthController.java
+++ b/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthController.java
@@ -1,7 +1,7 @@
package com.nanri.aiimage.modules.ziniao.controller;
import com.nanri.aiimage.common.api.ApiResponse;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.ziniao.model.cache.ZiniaoShopIndexRefreshCursorDto;
import com.nanri.aiimage.modules.ziniao.model.dto.ZiniaoOpenShopRequest;
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoOpenShopVo;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/ArchitectureBoundaryTest.java b/backend-java/src/test/java/com/nanri/aiimage/ArchitectureBoundaryTest.java
index 87385093..c68d1b0e 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/ArchitectureBoundaryTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/ArchitectureBoundaryTest.java
@@ -34,9 +34,9 @@ class ArchitectureBoundaryTest {
* 本次以实测值 119 为准恢复告警能力;经 git diff 核验,当日的 task 模块改动
* (引用计数按 task 收敛、快照 upsert 缓存、心跳条件更新、锁续期重试等)未新增任何业务依赖。
*/
- // 2026-09-14 心跳/陈旧修复/历史清理 SPI 化后实测 6(G5:task 只依赖 task.spi 下接口,
-// 业务模块各自实现);继续按棘轮收敛,不允许回升
- private static final int TASK_TO_BUSINESS_BASELINE = 6;
+ // 2026-09-14 心跳/陈旧修复/历史清理/结果文件 URL 全部 SPI 化后,task → 业务 依赖实测为 0。
+ // 基线钉死为 0:task 只允许依赖 task.spi 下的接口(业务模块各自实现),不允许回升。
+ private static final int TASK_TO_BUSINESS_BASELINE = 0;
private static volatile JavaClasses cached;
@@ -76,8 +76,9 @@ class ArchitectureBoundaryTest {
void taskToBusinessDependencyDoesNotGrow() {
// 冻结存量:Handler 迁移后 task→业务 依赖已大幅收敛;不允许继续增长
int violations = countViolations(taskRule());
- assertTrue(violations >= 1, "task→业务 依赖仍有存量(心跳/清理等待 SPI 化)");
assertTrue(violations <= TASK_TO_BUSINESS_BASELINE,
+ "task→业务 依赖必须为 0(当前 " + violations + ");跨模块动作 = 在 task/spi 加接口 + 业务模块实现");
+ assertTrue(violations >= 0,
"task→业务 依赖不得超过基线 " + TASK_TO_BUSINESS_BASELINE + ",当前 " + violations
+ ";新增依赖走 Handler SPI 或先消除存量");
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/admin/support/AdminAuthSupportTest.java b/backend-java/src/test/java/com/nanri/aiimage/common/security/AdminAuthSupportTest.java
similarity index 94%
rename from backend-java/src/test/java/com/nanri/aiimage/modules/admin/support/AdminAuthSupportTest.java
rename to backend-java/src/test/java/com/nanri/aiimage/common/security/AdminAuthSupportTest.java
index 48b194dc..6e2190bb 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/admin/support/AdminAuthSupportTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/common/security/AdminAuthSupportTest.java
@@ -1,10 +1,10 @@
-package com.nanri.aiimage.modules.admin.support;
+package com.nanri.aiimage.common.security;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.auth.config.AuthProperties;
-import com.nanri.aiimage.modules.auth.service.JwtService;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AuthProperties;
+import com.nanri.aiimage.common.security.JwtService;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/auth/support/DeviceSessionPolicyTest.java b/backend-java/src/test/java/com/nanri/aiimage/common/security/DeviceSessionPolicyTest.java
similarity index 98%
rename from backend-java/src/test/java/com/nanri/aiimage/modules/auth/support/DeviceSessionPolicyTest.java
rename to backend-java/src/test/java/com/nanri/aiimage/common/security/DeviceSessionPolicyTest.java
index 1d8a8cb0..b2f6bcd0 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/auth/support/DeviceSessionPolicyTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/common/security/DeviceSessionPolicyTest.java
@@ -1,4 +1,4 @@
-package com.nanri.aiimage.modules.auth.support;
+package com.nanri.aiimage.common.security;
import com.nanri.aiimage.common.exception.BusinessException;
import io.jsonwebtoken.Claims;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/config/AdminApiGuardFilterTest.java b/backend-java/src/test/java/com/nanri/aiimage/config/AdminApiGuardFilterTest.java
index cd0459e8..7103267f 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/config/AdminApiGuardFilterTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/config/AdminApiGuardFilterTest.java
@@ -3,8 +3,8 @@ package com.nanri.aiimage.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/admin/service/AdminUserServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/admin/service/AdminUserServiceTest.java
index 8a177399..1ff68591 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/admin/service/AdminUserServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/admin/service/AdminUserServiceTest.java
@@ -3,11 +3,11 @@ package com.nanri.aiimage.modules.admin.service;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.modules.admin.model.dto.AdminUserCreateRequest;
import com.nanri.aiimage.modules.admin.model.dto.AdminUserUpdateRequest;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.auth.util.WerkzeugPasswordEncoder;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
import com.nanri.aiimage.modules.permission.model.dto.UserColumnPermissionUpdateRequest;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import com.nanri.aiimage.modules.usersecret.service.UserApiSecretService;
import org.junit.jupiter.api.Test;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/auth/service/AuthServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/auth/service/AuthServiceTest.java
index e72eac24..e25ac764 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/auth/service/AuthServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/auth/service/AuthServiceTest.java
@@ -3,7 +3,7 @@ package com.nanri.aiimage.modules.auth.service;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.auth.config.AuthProperties;
+import com.nanri.aiimage.common.security.AuthProperties;
import com.nanri.aiimage.modules.auth.mapper.LoginUserMapper;
import com.nanri.aiimage.modules.auth.model.dto.LoginRequest;
import com.nanri.aiimage.modules.auth.model.entity.LoginUserEntity;
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.nanri.aiimage.common.security.JwtService;
class AuthServiceTest {
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataControllerTest.java
index 4f2ce793..24aeaf4f 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/controller/DedupeTotalDataControllerTest.java
@@ -1,9 +1,9 @@
package com.nanri.aiimage.modules.dedupe.controller;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.dedupe.service.DedupeTotalDataService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataServiceTest.java
index 80afcdee..e48549d8 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/dedupe/service/DedupeTotalDataServiceTest.java
@@ -11,10 +11,10 @@ import com.nanri.aiimage.common.service.NodeSharedStore;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportProgressVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataItemVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataPageVo;
-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.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryServiceTest.java
index b38da6f0..12a1cf46 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/imagehistory/service/ImageHistoryServiceTest.java
@@ -1,7 +1,7 @@
package com.nanri.aiimage.modules.imagehistory.service;
import com.nanri.aiimage.modules.imagehistory.mapper.ImageHistoryMapper;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataControllerTest.java
index 06206fef..b4fa6cac 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/controller/InvalidAsinDataControllerTest.java
@@ -1,10 +1,10 @@
package com.nanri.aiimage.modules.invalidasin.controller;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.invalidasin.model.vo.InvalidAsinDataPageVo;
import com.nanri.aiimage.modules.invalidasin.service.InvalidAsinDataService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.model.vo.PermissionMenuItemVo;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import org.junit.jupiter.api.BeforeEach;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataServiceTest.java
index 06ea1a14..ef632ff2 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/invalidasin/service/InvalidAsinDataServiceTest.java
@@ -10,7 +10,7 @@ import com.nanri.aiimage.modules.invalidasin.model.dto.InvalidAsinDataUpdateRequ
import com.nanri.aiimage.modules.invalidasin.model.entity.InvalidAsinDataEntity;
import com.nanri.aiimage.modules.invalidasin.model.vo.InvalidAsinDataItemVo;
import com.nanri.aiimage.modules.invalidasin.model.vo.InvalidAsinDataPageVo;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.shopkey.service.ShopManageGroupService;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.Test;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationControllerTest.java
index d6788dac..a63bb103 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/AdminNotificationControllerTest.java
@@ -1,9 +1,9 @@
package com.nanri.aiimage.modules.notification.controller;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.notification.model.dto.NotificationPageQuery;
import com.nanri.aiimage.modules.notification.service.NotificationService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/NotificationControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/NotificationControllerTest.java
index 6c09f210..2baf382d 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/NotificationControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/controller/NotificationControllerTest.java
@@ -1,9 +1,9 @@
package com.nanri.aiimage.modules.notification.controller;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.notification.model.dto.NotificationPageQuery;
import com.nanri.aiimage.modules.notification.service.NotificationService;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchServiceTest.java
index 35d04d81..da2cac22 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationDispatchServiceTest.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.notification.service;
-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.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.support.UserDataScopeSupport;
import org.junit.jupiter.api.Test;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationScanSchedulerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationScanSchedulerTest.java
index b133ce33..83c9819c 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationScanSchedulerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/notification/service/NotificationScanSchedulerTest.java
@@ -7,7 +7,7 @@ import com.nanri.aiimage.modules.brand.mapper.BrandCrawlTaskMapper;
import com.nanri.aiimage.modules.brand.model.entity.BrandCrawlTaskEntity;
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity;
-import com.nanri.aiimage.modules.usersecret.client.JikipProxyClient;
+import com.nanri.aiimage.modules.notification.spi.ProxyBalancePort;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -45,11 +45,11 @@ class NotificationScanSchedulerTest {
private final com.nanri.aiimage.common.service.DistributedJobLockService lockService =
mock(com.nanri.aiimage.common.service.DistributedJobLockService.class);
private final NotificationProperties properties = new NotificationProperties();
- private final JikipProxyClient jikipProxyClient = mock(JikipProxyClient.class);
+ private final ProxyBalancePort proxyBalancePort = mock(ProxyBalancePort.class);
private NotificationScanScheduler newScheduler() {
return new NotificationScanScheduler(fileTaskMapper, brandCrawlTaskMapper, notificationService,
- dispatch, lockService, properties, jikipProxyClient);
+ dispatch, lockService, properties, proxyBalancePort);
}
@Test
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuControllerTest.java
index b10a047e..aff2cbc0 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/permission/controller/PermissionMenuControllerTest.java
@@ -1,10 +1,10 @@
package com.nanri.aiimage.modules.permission.controller;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.permission.model.dto.ImageVideoDataPermissionUpdateRequest;
import com.nanri.aiimage.modules.permission.model.dto.UserColumnPermissionUpdateRequest;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.model.vo.ImageVideoDataPermissionUserVo;
import com.nanri.aiimage.modules.permission.model.vo.PermissionMenuItemVo;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuResolveTypeTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuResolveTypeTest.java
index e19f3d70..b06aeaa3 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuResolveTypeTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuResolveTypeTest.java
@@ -1,6 +1,6 @@
package com.nanri.aiimage.modules.permission.service;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
import com.nanri.aiimage.modules.permission.mapper.PermissionMenuMapper;
import com.nanri.aiimage.modules.permission.mapper.UserColumnPermissionMapper;
import com.nanri.aiimage.modules.permission.model.entity.PermissionMenuEntity;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuServiceTest.java
index bbe6c5cb..b9aac381 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/permission/service/PermissionMenuServiceTest.java
@@ -1,13 +1,13 @@
package com.nanri.aiimage.modules.permission.service;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
import com.nanri.aiimage.modules.permission.mapper.PermissionMenuMapper;
import com.nanri.aiimage.modules.permission.mapper.UserColumnPermissionMapper;
import com.nanri.aiimage.modules.permission.model.dto.PermissionMenuCreateRequest;
import com.nanri.aiimage.modules.permission.model.dto.PermissionMenuUpdateRequest;
import com.nanri.aiimage.modules.permission.model.dto.UserColumnPermissionUpdateRequest;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.model.entity.PermissionMenuEntity;
import com.nanri.aiimage.modules.permission.model.entity.UserColumnPermissionEntity;
import com.nanri.aiimage.modules.permission.model.vo.ImageVideoDataPermissionUserVo;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskControllerTest.java
index 8d125e5c..749563f8 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/controller/AdminShopDataCrawlTaskControllerTest.java
@@ -1,8 +1,8 @@
package com.nanri.aiimage.modules.shopdatacrawl.controller;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.service.PermissionMenuService;
import com.nanri.aiimage.modules.shopdatacrawl.service.ShopDataCrawlTaskService;
import jakarta.servlet.http.HttpServletRequest;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinServiceTest.java
index f4e2fcf6..e8515ea4 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/shopkey/service/SkipPriceAsinServiceTest.java
@@ -9,7 +9,7 @@ import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.modules.shopkey.mapper.ShopManageMapper;
import com.nanri.aiimage.modules.shopkey.mapper.SkipPriceAsinMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.SkipPriceAsinCreateRequest;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.model.entity.ShopManageGroupEntity;
import com.nanri.aiimage.modules.shopkey.model.entity.SkipPriceAsinEntity;
import com.nanri.aiimage.modules.shopkey.model.vo.QueryAsinImportProgressVo;
import com.nanri.aiimage.modules.shopkey.model.vo.SkipPriceAsinItemVo;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/BrandResultFileJobHandlerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/BrandResultFileJobHandlerTest.java
index 9a1d05fb..81019bda 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/BrandResultFileJobHandlerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/BrandResultFileJobHandlerTest.java
@@ -12,16 +12,19 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
/**
- * BrandResultFileJobHandler 测试(任务 71)。
- * BRAND 的 resolveResultObjectKey 特例(无 resultId 也走)保留在 Worker 公共路径 resolveResultFileUrl,
- * Handler 不接管 URL 解析;cleanup 为空(原 cleanupAfterSuccess 无 BRAND 分支,不走 payload.deleteLatest)。
+ * BrandResultFileJobHandler 测试(任务 71 / 2026-09 边界收敛)。
+ * BRAND 的 resolveResultObjectKey 特例(无 resultId 也走)由本 Handler 的 resolveResultFileUrl 钩子承担
+ * (原实现的 Worker 直连品牌模块构成 task → 业务依赖,已翻转为 SPI 钩子);
+ * cleanup 为空(原 cleanupAfterSuccess 无 BRAND 分支,不走 payload.deleteLatest)。
*/
class BrandResultFileJobHandlerTest {
@@ -49,18 +52,23 @@ class BrandResultFileJobHandlerTest {
}
@Test
- void resultUrlBrandSpecial() throws IOException {
- // Handler 不接管 URL 解析:无 resolveResultObjectKey / resolveResultFileUrl 方法
- for (java.lang.reflect.Method method : handler.getClass().getDeclaredMethods()) {
- assertFalse(method.getName().contains("resolve"),
- "Handler 不接管 URL 解析:" + method.getName());
- }
- // 特例仍留在 Worker 公共路径 resolveResultFileUrl(含 BRAND 分支,无 resultId 也走)
+ void resultUrlBrandSpecialResolvedByHandlerHook() throws IOException {
+ // 2026-09:URL 解析特例改由本 Handler 的 resolveResultFileUrl 钩子承担,Worker 不再 import 品牌模块
+ TaskFileJobEntity job = new TaskFileJobEntity();
+ job.setTaskId(33L);
+ when(brandTaskService.resolveResultObjectKey(33L)).thenReturn("oss/brand/33.json");
+
+ assertEquals("oss/brand/33.json", handler.resolveResultFileUrl(job));
+ assertNull(handler.resolveResultFileUrl(null), "空 job 返回 null 交回默认路径");
+ assertNull(handler.resolveResultFileUrl(new TaskFileJobEntity()), "无 taskId 返回 null 交回默认路径");
+
String source = workerSource();
assertTrue(source.contains("resolveResultFileUrl"), "Worker 保留 resolveResultFileUrl 公共路径");
- assertTrue(source.contains("resolveResultObjectKey"), "Worker 保留 BRAND 的 resolveResultObjectKey 特例");
- assertTrue(source.contains("\"BRAND\"") || source.contains("BRAND"),
- "Worker resolveResultFileUrl 含 BRAND 分支");
+ assertTrue(source.contains("handler.resolveResultFileUrl")
+ || source.contains("resolveResultFileUrl(job)"),
+ "Worker 通过 Handler 钩子解析模块自定义 URL");
+ assertFalse(source.contains("resolveResultObjectKey"), "Worker 不再直连品牌模块");
+ assertFalse(source.contains("modules.brand"), "Worker 不再 import 品牌模块");
}
@Test
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandlerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandlerTest.java
index 8f4a0a98..af996ceb 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandlerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/ResultFileJobHandlerTest.java
@@ -38,16 +38,17 @@ class ResultFileJobHandlerTest {
@Test
void interfaceMethodsPresent() throws Exception {
- // 2026-09:新增 fallbackAssembleOnFailure(job 重试耗尽时用部分数据兜底生成结果文件),
- // 接口方法从 7 个增到 8 个,同步更新契约断言。
- assertEquals(8, ResultFileJobHandler.class.getDeclaredMethods().length,
- "接口方法数量为 8(moduleType/process/onSuccess/cleanup/onFailure/"
- + "fallbackAssembleOnFailure/supportsAsyncOffload/isOwnerScoped)");
+ // 2026-09:新增 resolveResultFileUrl(模块自定义结果对象 key 解析,BRAND 用),
+ // 接口方法从 8 个增到 9 个,同步更新契约断言。
+ assertEquals(9, ResultFileJobHandler.class.getDeclaredMethods().length,
+ "接口方法数量为 9(moduleType/process/onSuccess/cleanup/onFailure/"
+ + "fallbackAssembleOnFailure/resolveResultFileUrl/supportsAsyncOffload/isOwnerScoped)");
assertNotNull(methodOf(ResultFileJobHandler.class, "moduleType"));
assertNotNull(methodOf(ResultFileJobHandler.class, "process", TaskFileJobEntity.class));
assertNotNull(methodOf(ResultFileJobHandler.class, "onSuccess", TaskFileJobEntity.class));
assertNotNull(methodOf(ResultFileJobHandler.class, "cleanup", TaskFileJobEntity.class));
assertNotNull(methodOf(ResultFileJobHandler.class, "onFailure", TaskFileJobEntity.class, String.class));
+ assertNotNull(methodOf(ResultFileJobHandler.class, "resolveResultFileUrl", TaskFileJobEntity.class));
assertNotNull(methodOf(ResultFileJobHandler.class, "supportsAsyncOffload"));
assertNotNull(methodOf(ResultFileJobHandler.class, "isOwnerScoped"));
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerCleanupTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerCleanupTest.java
index b9b8353d..e683bcdb 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerCleanupTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerCleanupTest.java
@@ -89,7 +89,6 @@ class TaskResultFileJobWorkerCleanupTest {
mock(FileResultMapper.class),
mock(TaskFileJobLocalDispatcher.class),
mock(InstanceMetadata.class),
- withdraw, brand,
registry);
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerDispatchTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerDispatchTest.java
index 4e942211..6f2be512 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerDispatchTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerDispatchTest.java
@@ -92,7 +92,6 @@ class TaskResultFileJobWorkerDispatchTest {
mock(FileResultMapper.class),
mock(TaskFileJobLocalDispatcher.class),
mock(InstanceMetadata.class),
- withdraw, brand,
registry);
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerEquivalenceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerEquivalenceTest.java
index dae0471d..0c49e31b 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerEquivalenceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerEquivalenceTest.java
@@ -100,7 +100,6 @@ class TaskResultFileJobWorkerEquivalenceTest {
fileResultMapper,
mock(TaskFileJobLocalDispatcher.class),
instanceMetadata,
- withdraw, brand,
registry);
Field executorField = TaskResultFileJobWorker.class.getDeclaredField("taskQueueExecutor");
executorField.setAccessible(true);
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerFailureNotifyTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerFailureNotifyTest.java
index e8c34a03..8c8d73b7 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerFailureNotifyTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerFailureNotifyTest.java
@@ -99,7 +99,6 @@ class TaskResultFileJobWorkerFailureNotifyTest {
mock(FileResultMapper.class),
mock(TaskFileJobLocalDispatcher.class),
instanceMetadata,
- withdraw, brand,
registry);
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOffloadTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOffloadTest.java
index 754c9941..c908d5e3 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOffloadTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOffloadTest.java
@@ -102,7 +102,6 @@ class TaskResultFileJobWorkerOffloadTest {
mock(FileResultMapper.class),
mock(TaskFileJobLocalDispatcher.class),
instanceMetadata,
- withdraw, brand,
registry);
setTaskQueueExecutor(worker, taskQueueExecutor);
return worker;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOrphanTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOrphanTest.java
index 5e3b583d..e82c2aa9 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOrphanTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOrphanTest.java
@@ -96,7 +96,6 @@ class TaskResultFileJobWorkerOrphanTest {
mock(FileResultMapper.class),
mock(TaskFileJobLocalDispatcher.class),
instanceMetadata,
- withdraw, brand,
registry);
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerRoutingTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerRoutingTest.java
index aa9f169f..4540f6ed 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerRoutingTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerRoutingTest.java
@@ -93,7 +93,6 @@ class TaskResultFileJobWorkerOwnerRoutingTest {
mock(FileResultMapper.class),
localDispatcher,
instanceMetadata,
- withdraw, brand,
new ResultFileJobHandlerRegistry(handlers));
set(worker, "taskQueueExecutor", (org.springframework.core.task.TaskExecutor) Runnable::run);
set(worker, "localWorkerEnabled", true);
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerScopedTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerScopedTest.java
index 6f3f639c..ffac2e00 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerScopedTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerOwnerScopedTest.java
@@ -96,7 +96,6 @@ class TaskResultFileJobWorkerOwnerScopedTest {
mock(FileResultMapper.class),
mock(TaskFileJobLocalDispatcher.class),
instanceMetadata,
- withdraw, brand,
registry);
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerSpecialRuleTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerSpecialRuleTest.java
index 0322d3a1..956e44e6 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerSpecialRuleTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerSpecialRuleTest.java
@@ -72,7 +72,6 @@ class TaskResultFileJobWorkerSpecialRuleTest {
fileResultMapper,
mock(TaskFileJobLocalDispatcher.class),
instanceMetadata,
- withdraw, brand,
handlerRegistry);
java.lang.reflect.Field executorField =
TaskResultFileJobWorker.class.getDeclaredField("taskQueueExecutor");
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerStuckScanTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerStuckScanTest.java
index 679b475d..19c40ed6 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerStuckScanTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/task/service/TaskResultFileJobWorkerStuckScanTest.java
@@ -96,7 +96,6 @@ class TaskResultFileJobWorkerStuckScanTest {
mock(FileResultMapper.class),
mock(TaskFileJobLocalDispatcher.class),
instanceMetadata,
- withdraw, brand,
registry);
}
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyControllerTest.java
index e2ff32dd..6d72c3d4 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/controller/InternalUserProxyControllerTest.java
@@ -2,7 +2,7 @@ package com.nanri.aiimage.modules.usersecret.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.common.exception.BusinessException;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.usersecret.service.UserApiSecretService;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretServiceTest.java
index 510ab70a..2b1116b0 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserApiSecretServiceTest.java
@@ -2,13 +2,13 @@ package com.nanri.aiimage.modules.usersecret.service;
import com.nanri.aiimage.common.security.ShopCredentialCryptoService;
import com.nanri.aiimage.config.UserSecretProperties;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.notification.service.NotificationDispatchService;
-import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
-import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.support.UserDataScopeSupport;
import com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef;
-import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageGroupEntity;
+import com.nanri.aiimage.common.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;
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
class UserApiSecretServiceTest {
@@ -37,8 +38,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 final com.nanri.aiimage.common.mapper.ShopManageGroupMapper adminGroupMapper =
+ mock(com.nanri.aiimage.common.mapper.ShopManageGroupMapper.class);
private final UserDataScopeSupport userDataScopeSupport = mock(UserDataScopeSupport.class);
private final NotificationDispatchService notificationDispatchService = mock(NotificationDispatchService.class);
private final UserSecretProperties userSecretProperties = new UserSecretProperties();
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageServiceTest.java
index db4515f6..2144c28a 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageServiceTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/usersecret/service/UserSecretUsageServiceTest.java
@@ -1,10 +1,10 @@
package com.nanri.aiimage.modules.usersecret.service;
-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.common.security.AdminAuthSupport;
+import com.nanri.aiimage.common.mapper.AdminUserMapper;
+import com.nanri.aiimage.common.model.entity.AdminUserEntity;
import com.nanri.aiimage.modules.permission.support.UserDataScopeSupport;
-import com.nanri.aiimage.modules.shopkey.mapper.ShopManageGroupMapper;
+import com.nanri.aiimage.common.mapper.ShopManageGroupMapper;
import com.nanri.aiimage.modules.shopkey.model.dto.UserGroupRef;
import com.nanri.aiimage.modules.usersecret.mapper.UserSecretUsageMapper;
import com.nanri.aiimage.modules.usersecret.model.dto.AdminUserSecretUsageQuery;
diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthControllerTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthControllerTest.java
index 108e6d52..9c91e101 100644
--- a/backend-java/src/test/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthControllerTest.java
+++ b/backend-java/src/test/java/com/nanri/aiimage/modules/ziniao/controller/ZiniaoAuthControllerTest.java
@@ -2,7 +2,7 @@ package com.nanri.aiimage.modules.ziniao.controller;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.modules.ziniao.model.cache.ZiniaoShopIndexRefreshCursorDto;
-import com.nanri.aiimage.modules.admin.support.AdminAuthSupport;
+import com.nanri.aiimage.common.security.AdminAuthSupport;
import com.nanri.aiimage.modules.ziniao.service.ZiniaoAuthService;
import com.nanri.aiimage.modules.ziniao.service.ZiniaoShopIndexRefreshService;
import com.nanri.aiimage.modules.ziniao.service.ZiniaoShopIndexService;