2026-04-21 22:07:29 +08:00
|
|
|
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;
|
2026-04-30 09:49:05 +08:00
|
|
|
import java.util.Map;
|
2026-04-21 22:07:29 +08:00
|
|
|
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;
|
2026-04-30 09:49:05 +08:00
|
|
|
private final OperationLogService operationLogService;
|
2026-04-21 22:07:29 +08:00
|
|
|
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,
|
2026-04-30 09:49:05 +08:00
|
|
|
EmailService emailService, OperationLogService operationLogService,
|
|
|
|
|
StringRedisTemplate redis) {
|
2026-04-21 22:07:29 +08:00
|
|
|
this.tenantRepository = tenantRepository;
|
|
|
|
|
this.passwordEncoder = passwordEncoder;
|
|
|
|
|
this.emailService = emailService;
|
2026-04-30 09:49:05 +08:00
|
|
|
this.operationLogService = operationLogService;
|
2026-04-21 22:07:29 +08:00
|
|
|
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());
|
2026-04-30 09:49:05 +08:00
|
|
|
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;
|
2026-04-21 22:07:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<TenantEntity> 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);
|
2026-04-30 09:49:05 +08:00
|
|
|
operationLogService.record(parentId, "SUB_ACCOUNT", "SUB_ACCOUNT", sub.getId(), "DISABLE_SUB_ACCOUNT", Map.of(
|
|
|
|
|
"username", sub.getUsername(),
|
|
|
|
|
"nickname", sub.getNickname(),
|
|
|
|
|
"email", sub.getEmail()
|
|
|
|
|
));
|
2026-04-21 22:07:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String generatePassword() {
|
|
|
|
|
byte[] bytes = new byte[9];
|
|
|
|
|
random.nextBytes(bytes);
|
|
|
|
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
|
|
|
|
|
}
|
|
|
|
|
}
|