package com.tourguide.modules.user.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.tourguide.common.exception.BusinessException;
|
import com.tourguide.common.validation.ChineseIdCardValidator;
|
import com.tourguide.modules.operation.entity.OperationCity;
|
import com.tourguide.modules.operation.mapper.OperationCityMapper;
|
import com.tourguide.modules.user.dto.GuideRegistrationResponse;
|
import com.tourguide.modules.user.dto.GuideRegistrationSubmitRequest;
|
import com.tourguide.modules.user.dto.GuideRegistrationTokenResponse;
|
import com.tourguide.modules.user.dto.SmsCodeResponse;
|
import com.tourguide.modules.user.entity.GuideApplication;
|
import com.tourguide.modules.user.mapper.GuideApplicationMapper;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
|
import java.time.Duration;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.UUID;
|
|
@Service
|
public class GuideRegistrationService {
|
private static final int PENDING = 0;
|
private static final int APPROVED = 1;
|
private static final Duration TOKEN_TTL = Duration.ofMinutes(30);
|
private static final String TOKEN_KEY_PREFIX = "tourguide:guide-registration:token:";
|
private static final DateTimeFormatter APPLICATION_NO_TIME = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
|
private final GuideVerificationCodeService verificationCodeService;
|
private final StringRedisTemplate redisTemplate;
|
private final GuideApplicationMapper applicationMapper;
|
private final GuideUserService guideUserService;
|
private final OperationCityMapper cityMapper;
|
|
public GuideRegistrationService(GuideVerificationCodeService verificationCodeService,
|
StringRedisTemplate redisTemplate,
|
GuideApplicationMapper applicationMapper,
|
GuideUserService guideUserService,
|
OperationCityMapper cityMapper) {
|
this.verificationCodeService = verificationCodeService;
|
this.redisTemplate = redisTemplate;
|
this.applicationMapper = applicationMapper;
|
this.guideUserService = guideUserService;
|
this.cityMapper = cityMapper;
|
}
|
|
public GuideRegistrationTokenResponse verifyPhone(String phone, String code) {
|
ensurePhoneCanRegister(phone);
|
verificationCodeService.verifyAndConsume(phone, code);
|
String token = UUID.randomUUID().toString().replace("-", "");
|
redisTemplate.opsForValue().set(tokenKey(token), phone, TOKEN_TTL);
|
return new GuideRegistrationTokenResponse(token, TOKEN_TTL.getSeconds());
|
}
|
|
public SmsCodeResponse sendCode(String phone) {
|
ensurePhoneCanRegister(phone);
|
return verificationCodeService.send(phone);
|
}
|
|
public String requireVerifiedPhone(String registrationToken) {
|
if (!StringUtils.hasText(registrationToken)) {
|
throw new BusinessException(401, "注册验证已失效,请重新验证手机号");
|
}
|
String phone = redisTemplate.opsForValue().get(tokenKey(registrationToken));
|
if (!StringUtils.hasText(phone)) {
|
throw new BusinessException(401, "注册验证已失效,请重新验证手机号");
|
}
|
return phone;
|
}
|
|
@Transactional
|
public GuideRegistrationResponse submit(String registrationToken, GuideRegistrationSubmitRequest request) {
|
String phone = requireVerifiedPhone(registrationToken);
|
ensurePhoneCanRegister(phone);
|
String idCardNo = request.getIdCardNo().trim().toUpperCase();
|
if (!ChineseIdCardValidator.isValid(idCardNo)) {
|
throw new BusinessException(400, "身份证号格式不正确");
|
}
|
if (applicationMapper.selectCount(new LambdaQueryWrapper<GuideApplication>()
|
.eq(GuideApplication::getIdCardNo, idCardNo)
|
.in(GuideApplication::getAuditStatus, PENDING, APPROVED)) > 0) {
|
throw new BusinessException(409, "该身份证号已提交过导游申请");
|
}
|
OperationCity city = cityMapper.selectById(request.getBankCityId());
|
if (city == null || city.getStatus() == null || city.getStatus() != 1) {
|
throw new BusinessException(400, "开户城市未开放或已停用");
|
}
|
|
GuideApplication application = new GuideApplication();
|
application.setApplicationNo(buildApplicationNo());
|
application.setRealName(request.getRealName().trim());
|
application.setMobile(phone);
|
application.setGender(request.getGender());
|
application.setAgeRange(normalizeOptional(request.getAgeRange()));
|
application.setWorkYearRange(normalizeOptional(request.getWorkYearRange()));
|
application.setGuideType(request.getGuideType());
|
application.setIdCardNo(idCardNo);
|
application.setIdCardFrontUrl(request.getIdCardFrontUrl());
|
application.setIdCardBackUrl(request.getIdCardBackUrl());
|
application.setCertificateUrl(request.getGuideCertificateUrl());
|
application.setQualificationCertificateUrl(normalizeOptional(request.getQualificationCertificateUrl()));
|
application.setCertificateValidUntil(request.getCertificateValidUntil());
|
application.setBankCardNo(request.getBankCardNo());
|
application.setBankCityId(city.getId());
|
application.setBankCityName(city.getCityName());
|
application.setAuditStatus(PENDING);
|
application.setDeleted(0);
|
applicationMapper.insert(application);
|
guideUserService.createPendingFromApplication(application);
|
redisTemplate.delete(tokenKey(registrationToken));
|
return new GuideRegistrationResponse(application);
|
}
|
|
private void ensurePhoneCanRegister(String phone) {
|
com.tourguide.modules.user.entity.GuideUser guide = guideUserService.findByPhone(phone);
|
if (guide != null && (guide.getStatus() == null || guide.getStatus() != 2)) {
|
throw new BusinessException(409, "该手机号已注册为导游");
|
}
|
if (applicationMapper.selectCount(new LambdaQueryWrapper<GuideApplication>()
|
.eq(GuideApplication::getMobile, phone)
|
.eq(GuideApplication::getAuditStatus, PENDING)) > 0) {
|
throw new BusinessException(409, "该手机号已有待审核申请");
|
}
|
}
|
|
private String buildApplicationNo() {
|
return "GA" + LocalDateTime.now().format(APPLICATION_NO_TIME)
|
+ UUID.randomUUID().toString().replace("-", "").substring(0, 6).toUpperCase();
|
}
|
|
private String normalizeOptional(String value) {
|
return StringUtils.hasText(value) ? value.trim() : null;
|
}
|
|
private String tokenKey(String token) { return TOKEN_KEY_PREFIX + token; }
|
}
|