|
|
@@ -0,0 +1,254 @@
|
|
|
+package com.ylx.massage.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.ylx.common.exception.ServiceException;
|
|
|
+import com.ylx.massage.domain.MomentMedia;
|
|
|
+import com.ylx.massage.domain.TechnicianMoment;
|
|
|
+import com.ylx.massage.domain.TJs;
|
|
|
+import com.ylx.massage.domain.vo.MomentDetailVO;
|
|
|
+import com.ylx.massage.domain.vo.MomentListVO;
|
|
|
+import com.ylx.massage.domain.vo.MomentMediaVO;
|
|
|
+import com.ylx.massage.mapper.MomentMediaMapper;
|
|
|
+import com.ylx.massage.mapper.TechnicianMomentMapper;
|
|
|
+import com.ylx.massage.mapper.TJsMapper;
|
|
|
+import com.ylx.massage.service.ITechnicianMomentService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 技师动态服务实现类
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMapper, TechnicianMoment>
|
|
|
+ implements ITechnicianMomentService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private TechnicianMomentMapper momentMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private MomentMediaMapper mediaMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private TJsMapper tJsMapper;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询推荐动态列表(不分地区,按日期倒序,同一天按浏览量倒序)
|
|
|
+ *
|
|
|
+ * @param pageNum 页码,默认1
|
|
|
+ * @param pageSize 每页数量,默认10
|
|
|
+ * @return R<List<MomentListVO>> 动态列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<MomentListVO> getRecommendedMoments(Integer pageNum, Integer pageSize) {
|
|
|
+ Page<TechnicianMoment> page = new Page<>(pageNum, pageSize);
|
|
|
+ List<TechnicianMoment> moments = momentMapper.selectRecommendedMoments(page);
|
|
|
+ if (moments == null || moments.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询技师信息
|
|
|
+ List<String> technicianIds = moments.stream().map(TechnicianMoment::getTechnicianId).collect(Collectors.toList());
|
|
|
+ Map<String, TJs> technicianMap = getTechnicianMap(technicianIds);
|
|
|
+
|
|
|
+ // 组装VO
|
|
|
+ return moments.stream().map(moment -> {
|
|
|
+ MomentListVO vo = new MomentListVO();
|
|
|
+ BeanUtils.copyProperties(moment, vo);
|
|
|
+
|
|
|
+ TJs technician = technicianMap.get(moment.getTechnicianId());
|
|
|
+ if (technician != null) {
|
|
|
+ vo.setTechnicianName(technician.getcNickName());
|
|
|
+ vo.setTechnicianAvatar(technician.getcPortrait());
|
|
|
+ vo.setTechnicianStatus(getTechnicianStatus(technician));
|
|
|
+ }
|
|
|
+ return vo;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询同城动态列表(按城市和发布时间倒序)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<MomentListVO> getSameCityMoments(String cityCode, Integer pageNum, Integer pageSize) {
|
|
|
+ Page<TechnicianMoment> page = new Page<>(pageNum, pageSize);
|
|
|
+ List<TechnicianMoment> moments = momentMapper.selectSameCityMoments(page, cityCode);
|
|
|
+ if (moments == null || moments.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询技师信息
|
|
|
+ List<String> technicianIds = moments.stream().map(TechnicianMoment::getTechnicianId).collect(Collectors.toList());
|
|
|
+ Map<String, TJs> technicianMap = getTechnicianMap(technicianIds);
|
|
|
+
|
|
|
+ // 组装VO
|
|
|
+ return moments.stream().map(moment -> {
|
|
|
+ MomentListVO vo = new MomentListVO();
|
|
|
+ BeanUtils.copyProperties(moment, vo);
|
|
|
+
|
|
|
+ TJs technician = technicianMap.get(moment.getTechnicianId());
|
|
|
+ if (technician != null) {
|
|
|
+ vo.setTechnicianName(technician.getcNickName());
|
|
|
+ vo.setTechnicianAvatar(technician.getcPortrait());
|
|
|
+ vo.setTechnicianStatus(getTechnicianStatus(technician));
|
|
|
+ }
|
|
|
+ return vo;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询附近动态列表(10km内,按日期倒序,同一天按距离排序)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<MomentListVO> getNearbyMoments(BigDecimal latitude, BigDecimal longitude, Integer pageNum, Integer pageSize) {
|
|
|
+ if (latitude == null || longitude == null) {
|
|
|
+ throw new ServiceException("地理位置信息不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ int offset = (pageNum - 1) * pageSize;
|
|
|
+ List<TechnicianMoment> moments = momentMapper.selectNearbyMoments(latitude, longitude, pageSize, offset);
|
|
|
+
|
|
|
+ if (moments == null || moments.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询技师信息
|
|
|
+ List<String> technicianIds = moments.stream()
|
|
|
+ .map(TechnicianMoment::getTechnicianId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ Map<String, TJs> technicianMap = getTechnicianMap(technicianIds);
|
|
|
+
|
|
|
+ // 组装VO(需要计算距离)
|
|
|
+ return moments.stream().map(moment -> {
|
|
|
+ MomentListVO vo = new MomentListVO();
|
|
|
+ BeanUtils.copyProperties(moment, vo);
|
|
|
+
|
|
|
+ TJs technician = technicianMap.get(String.valueOf(moment.getTechnicianId()));
|
|
|
+ if (technician != null) {
|
|
|
+ vo.setTechnicianName(technician.getcNickName());
|
|
|
+ vo.setTechnicianAvatar(technician.getcPortrait());
|
|
|
+ vo.setTechnicianStatus(getTechnicianStatus(technician));
|
|
|
+
|
|
|
+ // 计算距离
|
|
|
+ if (moment.getLatitude() != null && moment.getLongitude() != null) {
|
|
|
+ double distance = calculateDistance(
|
|
|
+ latitude.doubleValue(), longitude.doubleValue(),
|
|
|
+ moment.getLatitude().doubleValue(), moment.getLongitude().doubleValue()
|
|
|
+ );
|
|
|
+ vo.setDistance(Math.round(distance * 100.0) / 100.0); // 保留两位小数
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询动态详情(浏览量+1)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public MomentDetailVO getMomentDetail(Long momentId) {
|
|
|
+ if (momentId == null) {
|
|
|
+ throw new ServiceException("动态ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询动态信息
|
|
|
+ TechnicianMoment moment = momentMapper.selectById(momentId);
|
|
|
+ if (moment == null) {
|
|
|
+ throw new ServiceException("动态不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 增加浏览量
|
|
|
+ momentMapper.incrementViewCount(momentId);
|
|
|
+ moment.setViewCount(moment.getViewCount() + 1);
|
|
|
+
|
|
|
+ // 查询技师信息
|
|
|
+ TJs technician = tJsMapper.selectById(String.valueOf(moment.getTechnicianId()));
|
|
|
+
|
|
|
+ // 查询媒体列表
|
|
|
+ List<MomentMedia> mediaList = mediaMapper.selectMediaListByMomentId(momentId);
|
|
|
+ List<MomentMediaVO> mediaVOList = mediaList.stream().map(media -> {
|
|
|
+ MomentMediaVO mediaVO = new MomentMediaVO();
|
|
|
+ BeanUtils.copyProperties(media, mediaVO);
|
|
|
+ return mediaVO;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 组装VO
|
|
|
+ MomentDetailVO vo = new MomentDetailVO();
|
|
|
+ BeanUtils.copyProperties(moment, vo);
|
|
|
+
|
|
|
+ if (technician != null) {
|
|
|
+ vo.setTechnicianName(technician.getcNickName());
|
|
|
+ vo.setTechnicianAvatar(technician.getcPortrait());
|
|
|
+ vo.setTechnicianStatus(getTechnicianStatus(technician));
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setMediaList(mediaVOList);
|
|
|
+
|
|
|
+ // 计算距离(如果有位置信息)
|
|
|
+ if (moment.getLatitude() != null && moment.getLongitude() != null) {
|
|
|
+ // 这里可以传入用户位置计算距离,暂时不计算
|
|
|
+ vo.setDistance(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量查询技师信息
|
|
|
+ *
|
|
|
+ * @param technicianIds 技师ID列表
|
|
|
+ * @return Map<String, TJs> 技师ID-技师实体映射
|
|
|
+ */
|
|
|
+ private Map<String, TJs> getTechnicianMap(List<String> technicianIds) {
|
|
|
+ List<String> ids = technicianIds.stream()
|
|
|
+ .map(String::valueOf)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ return tJsMapper.selectBatchIds(ids).stream().collect(Collectors.toMap(TJs::getId, t -> t));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取技师状态
|
|
|
+ * 1-可服务(当前时间技师空闲)
|
|
|
+ * 2-可预约(当前时间技师"已接单-服务中")
|
|
|
+ */
|
|
|
+ private Integer getTechnicianStatus(TJs technician) {
|
|
|
+ if (technician.getnStatus() != null && technician.getnStatus() == 0) {
|
|
|
+ return 1; // 可服务
|
|
|
+ } else if (technician.getnStatus() != null && technician.getnStatus() == 1) {
|
|
|
+ return 2; // 可预约(服务中)
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算两点之间的距离(单位:km)
|
|
|
+ * 使用Haversine公式
|
|
|
+ */
|
|
|
+ private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
|
|
+ final double R = 6371; // 地球半径,单位km
|
|
|
+
|
|
|
+ double dLat = Math.toRadians(lat2 - lat1);
|
|
|
+ double dLon = Math.toRadians(lon2 - lon1);
|
|
|
+
|
|
|
+ double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
|
+ Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
|
|
|
+ Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
|
+
|
|
|
+ double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
|
+
|
|
|
+ return R * c;
|
|
|
+ }
|
|
|
+}
|