liujie
2026-09-14 15defa991fcd30b9fa60bab17d2230b5929bf2a2
backend/src/main/java/com/tourguide/modules/user/service/GuideRegistrationService.java
@@ -10,7 +10,9 @@
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.entity.GuideTeam;
import com.tourguide.modules.user.mapper.GuideApplicationMapper;
import com.tourguide.modules.user.mapper.GuideTeamMapper;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -25,6 +27,7 @@
public class GuideRegistrationService {
    private static final int PENDING = 0;
    private static final int APPROVED = 1;
    private static final String REGISTRATION_SCENE = "registration";
    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");
@@ -34,22 +37,25 @@
    private final GuideApplicationMapper applicationMapper;
    private final GuideUserService guideUserService;
    private final OperationCityMapper cityMapper;
    private final GuideTeamMapper teamMapper;
    public GuideRegistrationService(GuideVerificationCodeService verificationCodeService,
                                    StringRedisTemplate redisTemplate,
                                    GuideApplicationMapper applicationMapper,
                                    GuideUserService guideUserService,
                                    OperationCityMapper cityMapper) {
                                    OperationCityMapper cityMapper,
                                    GuideTeamMapper teamMapper) {
        this.verificationCodeService = verificationCodeService;
        this.redisTemplate = redisTemplate;
        this.applicationMapper = applicationMapper;
        this.guideUserService = guideUserService;
        this.cityMapper = cityMapper;
        this.teamMapper = teamMapper;
    }
    public GuideRegistrationTokenResponse verifyPhone(String phone, String code) {
        ensurePhoneCanRegister(phone);
        verificationCodeService.verifyAndConsume(phone, code);
        verificationCodeService.verifyAndConsume(phone, code, REGISTRATION_SCENE);
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(tokenKey(token), phone, TOKEN_TTL);
        return new GuideRegistrationTokenResponse(token, TOKEN_TTL.getSeconds());
@@ -57,7 +63,7 @@
    public SmsCodeResponse sendCode(String phone) {
        ensurePhoneCanRegister(phone);
        return verificationCodeService.send(phone);
        return verificationCodeService.send(phone, REGISTRATION_SCENE);
    }
    public String requireVerifiedPhone(String registrationToken) {
@@ -81,13 +87,12 @@
        }
        if (applicationMapper.selectCount(new LambdaQueryWrapper<GuideApplication>()
                .eq(GuideApplication::getIdCardNo, idCardNo)
                .ne(GuideApplication::getMobile, phone)
                .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, "开户城市未开放或已停用");
        }
        OperationCity city = findEnabledBankCity(request.getBankCityId());
        GuideTeam team = findEnabledGuideTeam(request.getGuideType(), request.getTeamId());
        GuideApplication application = new GuideApplication();
        application.setApplicationNo(buildApplicationNo());
@@ -97,15 +102,18 @@
        application.setAgeRange(normalizeOptional(request.getAgeRange()));
        application.setWorkYearRange(normalizeOptional(request.getWorkYearRange()));
        application.setGuideType(request.getGuideType());
        application.setTeamId(team == null ? null : team.getId());
        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.setBankCardNo(normalizeOptional(request.getBankCardNo()));
        if (city != null) {
            application.setBankCityId(city.getId());
            application.setBankCityName(city.getCityName());
        }
        application.setAuditStatus(PENDING);
        application.setDeleted(0);
        applicationMapper.insert(application);
@@ -116,7 +124,9 @@
    private void ensurePhoneCanRegister(String phone) {
        com.tourguide.modules.user.entity.GuideUser guide = guideUserService.findByPhone(phone);
        if (guide != null && (guide.getStatus() == null || guide.getStatus() != 2)) {
        if (guide != null && (guide.getStatus() == null
                || (guide.getStatus() != GuideUserService.STATUS_PENDING
                && guide.getStatus() != GuideUserService.STATUS_CANCELLED))) {
            throw new BusinessException(409, "该手机号已注册为导游");
        }
        if (applicationMapper.selectCount(new LambdaQueryWrapper<GuideApplication>()
@@ -135,5 +145,30 @@
        return StringUtils.hasText(value) ? value.trim() : null;
    }
    private OperationCity findEnabledBankCity(Long bankCityId) {
        if (bankCityId == null) {
            return null;
        }
        OperationCity city = cityMapper.selectById(bankCityId);
        if (city == null || city.getStatus() == null || city.getStatus() != 1) {
            throw new BusinessException(400, "开户城市未开放或已停用");
        }
        return city;
    }
    private GuideTeam findEnabledGuideTeam(String guideType, Long teamId) {
        if ("personal".equals(guideType)) {
            return null;
        }
        if (teamId == null) {
            throw new BusinessException(400, "请选择讲解队");
        }
        GuideTeam team = teamMapper.selectById(teamId);
        if (team == null || team.getStatus() == null || team.getStatus() != 1) {
            throw new BusinessException(400, "讲解队不存在或已停用");
        }
        return team;
    }
    private String tokenKey(String token) { return TOKEN_KEY_PREFIX + token; }
}