package com.tourguide.modules.user.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.tourguide.common.api.PageResponse; import com.tourguide.common.exception.BusinessException; import com.tourguide.modules.user.dto.GuideTeamResponse; import com.tourguide.modules.user.dto.GuideTeamScenicRequest; import com.tourguide.modules.user.dto.GuideTeamSaveRequest; import com.tourguide.modules.user.entity.GuideTeam; import com.tourguide.modules.user.entity.GuideTeamScenicConfig; import com.tourguide.modules.user.mapper.GuideTeamMapper; import com.tourguide.modules.user.mapper.GuideTeamScenicConfigMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.time.LocalDate; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @Service public class GuideTeamService { private final GuideTeamMapper teamMapper; private final GuideTeamScenicConfigMapper scenicConfigMapper; public GuideTeamService(GuideTeamMapper teamMapper, GuideTeamScenicConfigMapper scenicConfigMapper) { this.teamMapper = teamMapper; this.scenicConfigMapper = scenicConfigMapper; } public GuideTeamResponse detail(Long id) { GuideTeam team = requireTeam(id); List scenicConfigs = scenicConfigMapper.selectList( new LambdaQueryWrapper() .eq(GuideTeamScenicConfig::getTeamId, id) .orderByAsc(GuideTeamScenicConfig::getId)); return GuideTeamResponse.from(team, scenicConfigs); } public PageResponse page(long page, long size, String keyword, Integer status, LocalDate startDate, LocalDate endDate) { LambdaQueryWrapper query = new LambdaQueryWrapper(); if (StringUtils.hasText(keyword)) { String searchKeyword = keyword.trim(); query.and(wrapper -> wrapper.like(GuideTeam::getTeamName, searchKeyword) .or().like(GuideTeam::getContactPhone, searchKeyword)); } query.eq(status != null, GuideTeam::getStatus, status) .ge(startDate != null, GuideTeam::getCreatedAt, startDate == null ? null : startDate.atStartOfDay()) .lt(endDate != null, GuideTeam::getCreatedAt, endDate == null ? null : endDate.plusDays(1).atStartOfDay()) .orderByDesc(GuideTeam::getId); Page result = teamMapper.selectPage(new Page(page, size), query); return new PageResponse( result.getRecords().stream().map(GuideTeamResponse::from).collect(Collectors.toList()), result.getTotal(), result.getCurrent(), result.getSize()); } @Transactional public GuideTeamResponse create(GuideTeamSaveRequest request) { ensureNameAvailable(request.getTeamName(), null); GuideTeam team = new GuideTeam(); apply(team, request); team.setStatus(1); teamMapper.insert(team); replaceScenicConfigs(team.getId(), request.getScenicConfigs()); return detail(team.getId()); } @Transactional public GuideTeamResponse update(Long id, GuideTeamSaveRequest request) { GuideTeam team = requireTeam(id); ensureNameAvailable(request.getTeamName(), id); apply(team, request); teamMapper.updateById(team); replaceScenicConfigs(id, request.getScenicConfigs()); return detail(id); } @Transactional public void updateStatus(Long id, Integer status) { GuideTeam team = requireTeam(id); team.setStatus(status); teamMapper.updateById(team); } private GuideTeam requireTeam(Long id) { GuideTeam team = teamMapper.selectById(id); if (team == null) { throw new BusinessException(404, "讲解队不存在"); } return team; } private void ensureNameAvailable(String teamName, Long excludedId) { LambdaQueryWrapper query = new LambdaQueryWrapper() .eq(GuideTeam::getTeamName, teamName.trim()) .ne(excludedId != null, GuideTeam::getId, excludedId); if (teamMapper.selectCount(query) > 0) { throw new BusinessException(409, "讲解队名称已存在"); } } private void apply(GuideTeam team, GuideTeamSaveRequest request) { List scenicConfigs = request.getScenicConfigs() == null ? Collections.emptyList() : request.getScenicConfigs(); team.setTeamName(request.getTeamName().trim()); team.setContactPhone(request.getContactPhone().trim()); team.setOperatingArea(scenicConfigs.stream() .map(GuideTeamScenicRequest::getScenicName) .filter(StringUtils::hasText) .map(String::trim) .distinct() .collect(Collectors.joining(","))); team.setCompanyName(trimToNull(request.getCompanyName())); team.setUnifiedSocialCreditCode(trimToNull(request.getUnifiedSocialCreditCode())); team.setCommissionRatio(request.getCommissionRatio()); team.setWechatOpenId(trimToNull(request.getWechatOpenId())); team.setMerchantId(trimToNull(request.getMerchantId())); } private void replaceScenicConfigs(Long teamId, List requests) { scenicConfigMapper.markDeletedByTeamId(teamId); if (requests == null) { return; } Set scenicNames = new HashSet(); for (GuideTeamScenicRequest request : requests) { String scenicName = request.getScenicName().trim(); if (!scenicNames.add(scenicName)) { throw new BusinessException(409, "同一讲解队不能重复配置相同景区"); } GuideTeamScenicConfig config = new GuideTeamScenicConfig(); config.setTeamId(teamId); config.setScenicName(scenicName); config.setCityName(request.getCityName().trim()); config.setMeetingPoints(request.getMeetingPoints().trim()); scenicConfigMapper.upsert(config); } } private String trimToNull(String value) { return StringUtils.hasText(value) ? value.trim() : null; } }