Files
my_site/backend/src/main/java/com/yoyuzh/auth/RegistrationInviteService.java

97 lines
3.6 KiB
Java

package com.yoyuzh.auth;
import com.yoyuzh.common.BusinessException;
import com.yoyuzh.common.ErrorCode;
import com.yoyuzh.config.RegistrationProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.security.SecureRandom;
@Service
@RequiredArgsConstructor
public class RegistrationInviteService {
private static final Long STATE_ID = 1L;
private static final String INVITE_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
private static final int INVITE_LENGTH = 16;
private final RegistrationInviteStateRepository registrationInviteStateRepository;
private final RegistrationProperties registrationProperties;
private final SecureRandom secureRandom = new SecureRandom();
@Transactional
public String getCurrentInviteCode() {
return ensureCurrentState().getInviteCode();
}
@Transactional
public void consumeInviteCode(String inviteCode) {
RegistrationInviteState state = ensureCurrentStateForUpdate();
String candidateCode = normalize(inviteCode);
if (!state.getInviteCode().equals(candidateCode)) {
throw new BusinessException(ErrorCode.PERMISSION_DENIED, "邀请码错误");
}
state.setInviteCode(generateNextInviteCode(state.getInviteCode()));
registrationInviteStateRepository.save(state);
}
private RegistrationInviteState ensureCurrentState() {
return registrationInviteStateRepository.findById(STATE_ID)
.orElseGet(this::createInitialState);
}
private RegistrationInviteState ensureCurrentStateForUpdate() {
return registrationInviteStateRepository.findByIdForUpdate(STATE_ID)
.orElseGet(() -> {
createInitialState();
return registrationInviteStateRepository.findByIdForUpdate(STATE_ID)
.orElseThrow(() -> new IllegalStateException("邀请码状态初始化失败"));
});
}
private RegistrationInviteState createInitialState() {
RegistrationInviteState state = new RegistrationInviteState();
state.setId(STATE_ID);
state.setInviteCode(resolveInitialInviteCode());
try {
return registrationInviteStateRepository.saveAndFlush(state);
} catch (DataIntegrityViolationException ignored) {
return registrationInviteStateRepository.findById(STATE_ID)
.orElseThrow(() -> ignored);
}
}
private String resolveInitialInviteCode() {
String configuredInviteCode = normalize(registrationProperties.getInviteCode());
if (StringUtils.hasText(configuredInviteCode)) {
return configuredInviteCode;
}
return generateInviteCode();
}
private String generateNextInviteCode(String currentInviteCode) {
String nextCode = generateInviteCode();
while (nextCode.equals(currentInviteCode)) {
nextCode = generateInviteCode();
}
return nextCode;
}
private String generateInviteCode() {
StringBuilder builder = new StringBuilder(INVITE_LENGTH);
for (int i = 0; i < INVITE_LENGTH; i += 1) {
builder.append(INVITE_CHARS.charAt(secureRandom.nextInt(INVITE_CHARS.length())));
}
return builder.toString();
}
private String normalize(String value) {
return value == null ? "" : value.trim();
}
}