2026-04-21 22:07:29 +08:00
|
|
|
package com.xuqm.tenant.service;
|
|
|
|
|
|
|
|
|
|
import com.xuqm.common.security.JwtUtil;
|
2026-05-01 21:27:39 +08:00
|
|
|
import com.xuqm.tenant.entity.AppEntity;
|
|
|
|
|
import com.xuqm.tenant.entity.FeatureServiceEntity;
|
2026-04-21 22:07:29 +08:00
|
|
|
import com.xuqm.tenant.entity.OpsAdminEntity;
|
2026-05-01 21:27:39 +08:00
|
|
|
import com.xuqm.tenant.entity.OperationLogEntity;
|
2026-04-25 06:40:27 +08:00
|
|
|
import com.xuqm.tenant.entity.ServiceActivationRequestEntity;
|
2026-04-21 22:07:29 +08:00
|
|
|
import com.xuqm.tenant.entity.TenantEntity;
|
|
|
|
|
import com.xuqm.tenant.repository.AppRepository;
|
2026-05-01 21:27:39 +08:00
|
|
|
import com.xuqm.tenant.repository.FeatureServiceRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
import com.xuqm.tenant.repository.OpsAdminRepository;
|
2026-05-01 21:27:39 +08:00
|
|
|
import com.xuqm.tenant.repository.OperationLogRepository;
|
2026-04-25 06:40:27 +08:00
|
|
|
import com.xuqm.tenant.repository.ServiceActivationRequestRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
import com.xuqm.tenant.repository.TenantRepository;
|
|
|
|
|
import org.springframework.data.domain.Page;
|
|
|
|
|
import org.springframework.data.domain.PageRequest;
|
|
|
|
|
import org.springframework.data.domain.Sort;
|
|
|
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import java.time.LocalDate;
|
|
|
|
|
import java.time.LocalDateTime;
|
2026-05-01 21:27:39 +08:00
|
|
|
import java.util.LinkedHashMap;
|
|
|
|
|
import java.util.List;
|
2026-04-21 22:07:29 +08:00
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.UUID;
|
|
|
|
|
|
|
|
|
|
@Service
|
|
|
|
|
public class OpsService {
|
|
|
|
|
|
|
|
|
|
private final TenantRepository tenantRepository;
|
|
|
|
|
private final AppRepository appRepository;
|
2026-05-01 21:27:39 +08:00
|
|
|
private final FeatureServiceRepository featureServiceRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
private final OpsAdminRepository opsAdminRepository;
|
2026-04-25 06:40:27 +08:00
|
|
|
private final ServiceActivationRequestRepository requestRepository;
|
2026-05-01 21:27:39 +08:00
|
|
|
private final OperationLogRepository operationLogRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
private final PasswordEncoder passwordEncoder;
|
|
|
|
|
private final JwtUtil jwtUtil;
|
|
|
|
|
|
|
|
|
|
public OpsService(TenantRepository tenantRepository, AppRepository appRepository,
|
2026-05-01 21:27:39 +08:00
|
|
|
FeatureServiceRepository featureServiceRepository,
|
2026-04-25 06:40:27 +08:00
|
|
|
OpsAdminRepository opsAdminRepository,
|
|
|
|
|
ServiceActivationRequestRepository requestRepository,
|
2026-05-01 21:27:39 +08:00
|
|
|
OperationLogRepository operationLogRepository,
|
2026-04-25 06:40:27 +08:00
|
|
|
PasswordEncoder passwordEncoder, JwtUtil jwtUtil) {
|
2026-04-21 22:07:29 +08:00
|
|
|
this.tenantRepository = tenantRepository;
|
|
|
|
|
this.appRepository = appRepository;
|
2026-05-01 21:27:39 +08:00
|
|
|
this.featureServiceRepository = featureServiceRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
this.opsAdminRepository = opsAdminRepository;
|
2026-04-25 06:40:27 +08:00
|
|
|
this.requestRepository = requestRepository;
|
2026-05-01 21:27:39 +08:00
|
|
|
this.operationLogRepository = operationLogRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
this.passwordEncoder = passwordEncoder;
|
|
|
|
|
this.jwtUtil = jwtUtil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String login(String username, String password) {
|
|
|
|
|
OpsAdminEntity admin = opsAdminRepository.findByUsername(username)
|
|
|
|
|
.orElseThrow(() -> new IllegalArgumentException("用户名或密码错误"));
|
|
|
|
|
if (!passwordEncoder.matches(password, admin.getPassword())) {
|
|
|
|
|
throw new IllegalArgumentException("用户名或密码错误");
|
|
|
|
|
}
|
|
|
|
|
return jwtUtil.generate(admin.getId(), Map.of("username", username, "role", "OPS"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Page<TenantEntity> listTenants(String keyword, int page, int size) {
|
|
|
|
|
return tenantRepository.searchTenants(
|
|
|
|
|
keyword,
|
|
|
|
|
PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 21:27:39 +08:00
|
|
|
public Map<String, Object> getTenantDetail(String tenantId) {
|
|
|
|
|
TenantEntity tenant = tenantRepository.findById(tenantId)
|
|
|
|
|
.orElseThrow(() -> new IllegalArgumentException("租户不存在"));
|
|
|
|
|
List<AppEntity> apps = appRepository.findByTenantId(tenantId);
|
|
|
|
|
long subAccountCount = tenantRepository.countByParentId(tenantId);
|
|
|
|
|
long activeServiceCount = apps.stream()
|
|
|
|
|
.flatMap(app -> featureServiceRepository.findByAppId(app.getId()).stream())
|
|
|
|
|
.filter(FeatureServiceEntity::isEnabled)
|
|
|
|
|
.count();
|
|
|
|
|
|
|
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
|
|
|
result.put("id", tenant.getId());
|
|
|
|
|
result.put("username", tenant.getUsername());
|
|
|
|
|
result.put("nickname", tenant.getNickname());
|
|
|
|
|
result.put("email", tenant.getEmail());
|
|
|
|
|
result.put("phone", tenant.getPhone());
|
|
|
|
|
result.put("type", tenant.getType());
|
|
|
|
|
result.put("status", tenant.getStatus());
|
|
|
|
|
result.put("parentId", tenant.getParentId());
|
|
|
|
|
result.put("createdAt", tenant.getCreatedAt());
|
|
|
|
|
result.put("apps", apps);
|
|
|
|
|
result.put("appCount", apps.size());
|
|
|
|
|
result.put("subAccountCount", subAccountCount);
|
|
|
|
|
result.put("activeServiceCount", activeServiceCount);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<AppEntity> listTenantApps(String tenantId) {
|
|
|
|
|
tenantRepository.findById(tenantId)
|
|
|
|
|
.orElseThrow(() -> new IllegalArgumentException("租户不存在"));
|
|
|
|
|
return appRepository.findByTenantId(tenantId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 22:07:29 +08:00
|
|
|
public void toggleStatus(String tenantId) {
|
|
|
|
|
TenantEntity tenant = tenantRepository.findById(tenantId)
|
|
|
|
|
.orElseThrow(() -> new IllegalArgumentException("租户不存在"));
|
|
|
|
|
if (tenant.getStatus() == TenantEntity.Status.ACTIVE) {
|
|
|
|
|
tenant.setStatus(TenantEntity.Status.DISABLED);
|
|
|
|
|
} else {
|
|
|
|
|
tenant.setStatus(TenantEntity.Status.ACTIVE);
|
|
|
|
|
}
|
|
|
|
|
tenantRepository.save(tenant);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Map<String, Object> statistics() {
|
|
|
|
|
long totalTenants = tenantRepository.count();
|
|
|
|
|
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
|
|
|
|
LocalDateTime todayEnd = todayStart.plusDays(1);
|
|
|
|
|
long todayNew = tenantRepository.countByCreatedAtBetween(todayStart, todayEnd);
|
|
|
|
|
long activeApps = appRepository.count();
|
|
|
|
|
return Map.of(
|
|
|
|
|
"totalTenants", totalTenants,
|
|
|
|
|
"todayNew", todayNew,
|
|
|
|
|
"activeApps", activeApps,
|
|
|
|
|
"onlineUsers", 0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-25 06:40:27 +08:00
|
|
|
public Page<ServiceActivationRequestEntity> listServiceRequests(String statusStr, int page, int size) {
|
|
|
|
|
var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
|
|
|
|
|
if (statusStr != null && !statusStr.isEmpty()) {
|
|
|
|
|
ServiceActivationRequestEntity.Status status =
|
|
|
|
|
ServiceActivationRequestEntity.Status.valueOf(statusStr.toUpperCase());
|
|
|
|
|
return requestRepository.findByStatusOrderByCreatedAtDesc(status, pageable);
|
|
|
|
|
}
|
|
|
|
|
return requestRepository.findAllByOrderByCreatedAtDesc(pageable);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 21:27:39 +08:00
|
|
|
public Page<AppEntity> listApps(String keyword, int page, int size) {
|
|
|
|
|
var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
|
|
|
|
|
if (keyword == null || keyword.isBlank()) {
|
|
|
|
|
return appRepository.findAll(pageable);
|
|
|
|
|
}
|
|
|
|
|
return appRepository.findByNameContainingIgnoreCaseOrAppKeyContainingIgnoreCase(keyword, keyword, pageable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Map<String, Object> getAppDetail(String appId) {
|
|
|
|
|
AppEntity app = appRepository.findById(appId)
|
|
|
|
|
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
|
|
|
|
List<FeatureServiceEntity> services = featureServiceRepository.findByAppId(appId);
|
|
|
|
|
long enabledCount = services.stream().filter(FeatureServiceEntity::isEnabled).count();
|
|
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
|
|
|
result.put("app", app);
|
|
|
|
|
result.put("tenant", tenantRepository.findById(app.getTenantId()).orElse(null));
|
|
|
|
|
result.put("services", services);
|
|
|
|
|
result.put("serviceCount", services.size());
|
|
|
|
|
result.put("enabledServiceCount", enabledCount);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<FeatureServiceEntity> listAppServices(String appId) {
|
|
|
|
|
appRepository.findById(appId)
|
|
|
|
|
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
|
|
|
|
return featureServiceRepository.findByAppId(appId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Page<OperationLogEntity> listOperationLogs(int page, int size) {
|
|
|
|
|
var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
|
|
|
|
|
return operationLogRepository.findAllByOrderByCreatedAtDesc(pageable);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 22:07:29 +08:00
|
|
|
public void initDefaultAdmin(String username, String rawPassword) {
|
|
|
|
|
if (opsAdminRepository.findByUsername(username).isPresent()) return;
|
|
|
|
|
OpsAdminEntity admin = new OpsAdminEntity();
|
|
|
|
|
admin.setId(UUID.randomUUID().toString());
|
|
|
|
|
admin.setUsername(username);
|
|
|
|
|
admin.setPassword(passwordEncoder.encode(rawPassword));
|
|
|
|
|
admin.setCreatedAt(LocalDateTime.now());
|
|
|
|
|
opsAdminRepository.save(admin);
|
|
|
|
|
}
|
|
|
|
|
}
|