feat: ops应用列表支持租户筛选 + 返回租户名称 + 服务按类型展示

- AppRepository: 新增 JOIN 查询,返回应用+租户名称
- OpsService: listApps 支持 tenantId 筛选,getAppDetail 按 serviceType 去重
- OpsController: listApps 新增 tenantId 可选参数

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-05-16 00:24:10 +08:00
父节点 805ef3900b
当前提交 afb57a5d5f
共有 3 个文件被更改,包括 46 次插入6 次删除

查看文件

@ -135,9 +135,10 @@ public class OpsController {
@PreAuthorize("hasAuthority('ROLE_OPS')") @PreAuthorize("hasAuthority('ROLE_OPS')")
public ResponseEntity<ApiResponse<Map<String, Object>>> listApps( public ResponseEntity<ApiResponse<Map<String, Object>>> listApps(
@RequestParam(defaultValue = "") String keyword, @RequestParam(defaultValue = "") String keyword,
@RequestParam(required = false) String tenantId,
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) { @RequestParam(defaultValue = "20") int size) {
Page<AppEntity> result = opsService.listApps(keyword, page, size); Page<Map<String, Object>> result = opsService.listApps(keyword, tenantId, page, size);
return ResponseEntity.ok(ApiResponse.success(Map.of( return ResponseEntity.ok(ApiResponse.success(Map.of(
"content", result.getContent(), "content", result.getContent(),
"total", result.getTotalElements(), "total", result.getTotalElements(),

查看文件

@ -4,6 +4,8 @@ import com.xuqm.tenant.entity.AppEntity;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -14,4 +16,16 @@ public interface AppRepository extends JpaRepository<AppEntity, String> {
boolean existsByPackageNameAndTenantId(String packageName, String tenantId); boolean existsByPackageNameAndTenantId(String packageName, String tenantId);
long count(); long count();
Page<AppEntity> findByNameContainingIgnoreCaseOrAppKeyContainingIgnoreCase(String name, String appKey, Pageable pageable); Page<AppEntity> findByNameContainingIgnoreCaseOrAppKeyContainingIgnoreCase(String name, String appKey, Pageable pageable);
@Query("SELECT a, t.nickname FROM AppEntity a LEFT JOIN TenantEntity t ON a.tenantId = t.id WHERE a.tenantId = :tenantId")
Page<Object[]> findByTenantIdJoined(@Param("tenantId") String tenantId, Pageable pageable);
@Query("SELECT a, t.nickname FROM AppEntity a LEFT JOIN TenantEntity t ON a.tenantId = t.id")
Page<Object[]> findAllJoined(Pageable pageable);
@Query("SELECT a, t.nickname FROM AppEntity a LEFT JOIN TenantEntity t ON a.tenantId = t.id WHERE a.tenantId = :tenantId AND (LOWER(a.name) LIKE LOWER(CONCAT('%',:kw,'%')) OR LOWER(a.appKey) LIKE LOWER(CONCAT('%',:kw,'%')))")
Page<Object[]> findByTenantIdAndKeywordJoined(@Param("tenantId") String tenantId, @Param("kw") String keyword, Pageable pageable);
@Query("SELECT a, t.nickname FROM AppEntity a LEFT JOIN TenantEntity t ON a.tenantId = t.id WHERE LOWER(a.name) LIKE LOWER(CONCAT('%',:kw,'%')) OR LOWER(a.appKey) LIKE LOWER(CONCAT('%',:kw,'%'))")
Page<Object[]> findByKeywordJoined(@Param("kw") String keyword, Pageable pageable);
} }

查看文件

@ -36,6 +36,7 @@ public class OpsService {
private final TenantRepository tenantRepository; private final TenantRepository tenantRepository;
private final AppRepository appRepository; private final AppRepository appRepository;
private final FeatureServiceRepository featureServiceRepository; private final FeatureServiceRepository featureServiceRepository;
private final FeatureServiceManager featureServiceManager;
private final OpsAdminRepository opsAdminRepository; private final OpsAdminRepository opsAdminRepository;
private final ServiceActivationRequestRepository requestRepository; private final ServiceActivationRequestRepository requestRepository;
private final OperationLogRepository operationLogRepository; private final OperationLogRepository operationLogRepository;
@ -44,6 +45,7 @@ public class OpsService {
public OpsService(TenantRepository tenantRepository, AppRepository appRepository, public OpsService(TenantRepository tenantRepository, AppRepository appRepository,
FeatureServiceRepository featureServiceRepository, FeatureServiceRepository featureServiceRepository,
FeatureServiceManager featureServiceManager,
OpsAdminRepository opsAdminRepository, OpsAdminRepository opsAdminRepository,
ServiceActivationRequestRepository requestRepository, ServiceActivationRequestRepository requestRepository,
OperationLogRepository operationLogRepository, OperationLogRepository operationLogRepository,
@ -51,6 +53,7 @@ public class OpsService {
this.tenantRepository = tenantRepository; this.tenantRepository = tenantRepository;
this.appRepository = appRepository; this.appRepository = appRepository;
this.featureServiceRepository = featureServiceRepository; this.featureServiceRepository = featureServiceRepository;
this.featureServiceManager = featureServiceManager;
this.opsAdminRepository = opsAdminRepository; this.opsAdminRepository = opsAdminRepository;
this.requestRepository = requestRepository; this.requestRepository = requestRepository;
this.operationLogRepository = operationLogRepository; this.operationLogRepository = operationLogRepository;
@ -196,18 +199,40 @@ public class OpsService {
); );
} }
public Page<AppEntity> listApps(String keyword, int page, int size) { public Page<Map<String, Object>> listApps(String keyword, String tenantId, int page, int size) {
var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt")); var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
if (keyword == null || keyword.isBlank()) { boolean hasKw = keyword != null && !keyword.isBlank();
return appRepository.findAll(pageable); boolean hasTenant = tenantId != null && !tenantId.isBlank();
Page<Object[]> result;
if (hasKw && hasTenant) {
result = appRepository.findByTenantIdAndKeywordJoined(tenantId, keyword, pageable);
} else if (hasTenant) {
result = appRepository.findByTenantIdJoined(tenantId, pageable);
} else if (hasKw) {
result = appRepository.findByKeywordJoined(keyword, pageable);
} else {
result = appRepository.findAllJoined(pageable);
} }
return appRepository.findByNameContainingIgnoreCaseOrAppKeyContainingIgnoreCase(keyword, keyword, pageable); return result.map(row -> {
AppEntity app = (AppEntity) row[0];
String tenantName = row[1] != null ? (String) row[1] : null;
Map<String, Object> m = new LinkedHashMap<>();
m.put("id", app.getId());
m.put("appKey", app.getAppKey());
m.put("appSecret", app.getAppSecret());
m.put("name", app.getName());
m.put("packageName", app.getPackageName());
m.put("tenantId", app.getTenantId());
m.put("tenantName", tenantName);
m.put("createdAt", app.getCreatedAt());
return m;
});
} }
public Map<String, Object> getAppDetail(String appKey) { public Map<String, Object> getAppDetail(String appKey) {
AppEntity app = appRepository.findByAppKey(appKey) AppEntity app = appRepository.findByAppKey(appKey)
.orElseThrow(() -> new IllegalArgumentException("应用不存在")); .orElseThrow(() -> new IllegalArgumentException("应用不存在"));
List<FeatureServiceEntity> services = featureServiceRepository.findByAppKey(app.getAppKey()); List<FeatureServiceEntity> services = featureServiceManager.listByApp(appKey);
long enabledCount = services.stream().filter(FeatureServiceEntity::isEnabled).count(); long enabledCount = services.stream().filter(FeatureServiceEntity::isEnabled).count();
Map<String, Object> result = new LinkedHashMap<>(); Map<String, Object> result = new LinkedHashMap<>();
result.put("app", app); result.put("app", app);