97 lines
3.6 KiB
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();
|
|
}
|
|
}
|