This article discusses the implementation of a unified verification code sending system that handles multiple business scenarios through a comon interface.
Core Implementation
The system processes verification code requests through a cenrtalized service that delegates to specific handler implemantations based on business type codes.
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import com.example.captcha.handler.VerificationHandler;
import com.example.captcha.service.VerificationService;
import com.example.captcha.model.VerificationRequest;
import com.example.common.exception.BusinessException;
@Service
public class VerificationServiceImpl implements VerificationService {
@Autowired
private ListableBeanFactory beanFactory;
@Override
public void sendVerificationCode(VerificationRequest request) {
if (!isValidPhoneNumber(request.getPhoneNumber())) {
throw new BusinessException("Invalid phone number format");
}
Map<String, VerificationHandler> handlers =
beanFactory.getBeansOfType(VerificationHandler.class);
if (CollectionUtils.isEmpty(handlers)) {
throw new BusinessException("No verification handlers available");
}
VerificationHandler targetHandler = handlers.values().stream()
.filter(handler -> request.getBusinessCode().equals(handler.getHandlerCode()))
.findFirst()
.orElseThrow(() -> new BusinessException("Unsupported business type"));
targetHandler.processVerification(request.getPhoneNumber());
}
private boolean isValidPhoneNumber(String phone) {
return StringUtils.hasText(phone) && phone.matches("^1[3-9]\\d{9}$");
}
}
Handler Interface Definition
The VerificationHandler interface defines the contract for all verification code implementations:
import java.time.Duration;
import java.time.LocalDateTime;
import com.example.captcha.manager.VerificationManager;
import com.example.sms.SmsService;
import com.example.sms.model.SmsRequest;
public interface VerificationHandler {
default VerificationManager getVerificationManager() {
return ApplicationContextProvider.getBean(VerificationManager.class);
}
String getHandlerCode();
default void processVerification(String phoneNumber) {
validateBeforeProcessing(phoneNumber);
VerificationRecord existingRecord =
getVerificationManager().getVerificationRecord(getHandlerCode(), phoneNumber);
if (existingRecord != null) {
Duration timeSinceLastSend = Duration.between(
existingRecord.getCreationTime(), LocalDateTime.now());
if (timeSinceLastSend.getSeconds() < 60) {
throw new BusinessException("Please wait " +
(60 - timeSinceLastSend.getSeconds()) + " seconds before requesting again");
}
}
VerificationRecord newRecord =
getVerificationManager().createVerificationRecord(getHandlerCode(), phoneNumber);
SmsService.sendSms(phoneNumber, buildSmsContent(newRecord));
}
void validateBeforeProcessing(String phoneNumber);
SmsRequest buildSmsContent(VerificationRecord record);
default void validateCode(String phoneNumber, String code) {
VerificationRecord record =
getVerificationManager().getVerificationRecord(getHandlerCode(), phoneNumber);
if (record == null || !record.getPhoneNumber().equals(phoneNumber)) {
throw new BusinessException("Invalid verification code");
}
if (!record.getCode().equals(code)) {
throw new BusinessException("Verification code mismatch");
}
}
}
Concrete Handler Implementation
Example implementation for user registration verification:
@Component
public class UserRegistrationHandler implements VerificationHandler {
@Autowired
private UserValidationService validationService;
@Override
public String getHandlerCode() {
return "user_registration";
}
@Override
public void validateBeforeProcessing(String phoneNumber) {
validationService.validateRegistrationPhone(phoneNumber);
}
@Override
public SmsRequest buildSmsContent(VerificationRecord record) {
return SmsTemplate.createVerificationSms(
"YourCompany",
"SMS_TEMPLATE_12345",
Map.of("code", record.getCode())
);
}
}