package com.xuqm.tenant.service; import com.xuqm.common.exception.BusinessException; import com.xuqm.tenant.dto.CreateSubAccountRequest; import com.xuqm.tenant.entity.TenantEntity; import com.xuqm.tenant.repository.TenantRepository; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.security.SecureRandom; import java.time.LocalDateTime; import java.util.Base64; import java.util.Map; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; @Service public class SubAccountService { private final TenantRepository tenantRepository; private final PasswordEncoder passwordEncoder; private final EmailService emailService; private final OperationLogService operationLogService; private final StringRedisTemplate redis; private static final String SUB_VERIFY_PREFIX = "sub_verified:"; private static final SecureRandom random = new SecureRandom(); public SubAccountService(TenantRepository tenantRepository, PasswordEncoder passwordEncoder, EmailService emailService, OperationLogService operationLogService, StringRedisTemplate redis) { this.tenantRepository = tenantRepository; this.passwordEncoder = passwordEncoder; this.emailService = emailService; this.operationLogService = operationLogService; this.redis = redis; } public boolean isEmailVerifiedInSession(String tenantId) { return Boolean.TRUE.toString().equals(redis.opsForValue().get(SUB_VERIFY_PREFIX + tenantId)); } public void verifyEmail(String tenantId, String email, String code) { emailService.verify(email, code, "SUB_ACCOUNT"); redis.opsForValue().set(SUB_VERIFY_PREFIX + tenantId, Boolean.TRUE.toString(), 24, TimeUnit.HOURS); } public TenantEntity create(String parentId, CreateSubAccountRequest req) { if (tenantRepository.existsByUsername(req.username())) { throw new BusinessException("用户名已存在"); } TenantEntity sub = new TenantEntity(); sub.setId(UUID.randomUUID().toString()); sub.setUsername(req.username()); sub.setPassword(passwordEncoder.encode(req.password())); sub.setEmail(req.email() != null ? req.email() : ""); sub.setNickname(req.nickname()); sub.setPhone(req.phone()); sub.setType(TenantEntity.Type.SUB); sub.setStatus(TenantEntity.Status.ACTIVE); sub.setParentId(parentId); sub.setCreatedAt(LocalDateTime.now()); TenantEntity saved = tenantRepository.save(sub); operationLogService.record(parentId, "SUB_ACCOUNT", "SUB_ACCOUNT", saved.getId(), "CREATE_SUB_ACCOUNT", Map.of( "username", saved.getUsername(), "nickname", saved.getNickname(), "email", saved.getEmail() )); return saved; } public List listByParent(String parentId) { return tenantRepository.findByParentId(parentId); } public void disable(String subId, String parentId) { TenantEntity sub = tenantRepository.findById(subId) .orElseThrow(() -> new BusinessException(404, "子账号不存在")); if (!parentId.equals(sub.getParentId())) { throw new BusinessException(403, "无权操作"); } sub.setStatus(TenantEntity.Status.DISABLED); tenantRepository.save(sub); operationLogService.record(parentId, "SUB_ACCOUNT", "SUB_ACCOUNT", sub.getId(), "DISABLE_SUB_ACCOUNT", Map.of( "username", sub.getUsername(), "nickname", sub.getNickname(), "email", sub.getEmail() )); } public String generatePassword() { byte[] bytes = new byte[9]; random.nextBytes(bytes); return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); } }