package com.ylx.massage.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ylx.common.core.domain.model.WxLoginUser; 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.dto.MomentAuditDTO; import com.ylx.massage.domain.dto.MomentManageQueryDTO; import com.ylx.massage.domain.dto.PublishMomentDTO; import com.ylx.massage.domain.dto.EditMomentDTO; import com.ylx.massage.domain.vo.MomentDetailVO; import com.ylx.massage.domain.vo.MomentListVO; import com.ylx.massage.domain.vo.MomentManageVO; import com.ylx.massage.domain.vo.MomentMediaVO; import com.ylx.massage.domain.vo.MomentSimpleDetailVO; import com.ylx.massage.domain.vo.MyMomentVO; 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.apache.commons.lang3.StringUtils; 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.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * 技师动态服务实现类 */ @Service @Slf4j public class TechnicianMomentServiceImpl extends ServiceImpl implements ITechnicianMomentService { @Autowired private TechnicianMomentMapper momentMapper; @Autowired private MomentMediaMapper mediaMapper; @Autowired private TJsMapper tJsMapper; /** * 查询推荐动态列表(不分地区,按日期倒序,同一天按浏览量倒序) * * @param pageNum 页码,默认1 * @param pageSize 每页数量,默认10 * @return R> 动态列表 */ @Override public List getRecommendedMoments(Integer pageNum, Integer pageSize) { Page page = new Page<>(pageNum, pageSize); List moments = momentMapper.selectRecommendedMoments(page); if (moments == null || moments.isEmpty()) { return new ArrayList<>(); } // 查询技师信息 List technicianIds = moments.stream().map(TechnicianMoment::getTechnicianId).collect(Collectors.toList()); Map 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.setTechnicianNickName(technician.getcNickName()); vo.setTechnicianAvatar(technician.getcPortrait()); vo.setTechnicianStatus(getTechnicianStatus(technician)); } return vo; }).collect(Collectors.toList()); } /** * 查询同城动态列表(按城市和发布时间倒序) */ @Override public List getSameCityMoments(String cityCode, Integer pageNum, Integer pageSize) { Page page = new Page<>(pageNum, pageSize); List moments = momentMapper.selectSameCityMoments(page, cityCode); if (moments == null || moments.isEmpty()) { return new ArrayList<>(); } // 查询技师信息 List technicianIds = moments.stream().map(TechnicianMoment::getTechnicianId).collect(Collectors.toList()); Map 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.setTechnicianNickName(technician.getcNickName()); vo.setTechnicianAvatar(technician.getcPortrait()); vo.setTechnicianStatus(getTechnicianStatus(technician)); } return vo; }).collect(Collectors.toList()); } /** * 查询附近动态列表(10km内,按日期倒序,同一天按距离排序) * * @param longitude 经度 * @param latitude 纬度 * @param pageNum 页码,默认1 * @param pageSize 每页数量,默认10 * @return R> 动态列表 */ @Override public List getNearbyMoments(BigDecimal longitude, BigDecimal latitude, Integer pageNum, Integer pageSize) { if (latitude == null || longitude == null) { throw new ServiceException("地理位置信息不能为空"); } Page page = new Page<>(pageNum, pageSize); List moments = momentMapper.selectNearbyMoments(page, longitude, latitude); if (moments == null || moments.isEmpty()) { return new ArrayList<>(); } // 查询技师信息 List technicianIds = moments.stream() .map(TechnicianMoment::getTechnicianId) .collect(Collectors.toList()); Map 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.setTechnicianNickName(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) * * @param momentId 动态ID * @return MomentDetailVO 动态详情 */ @Override @Transactional(rollbackFor = Exception.class) public MomentDetailVO getMomentDetail(Long momentId, BigDecimal longitude, BigDecimal latitude) { 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(moment.getTechnicianId()); // 查询媒体列表 List mediaList = mediaMapper.selectMediaListByMomentId(momentId); List 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.setTechnicianNickName(technician.getcNickName()); vo.setTechnicianAvatar(technician.getcPortrait()); vo.setTechnicianStatus(getTechnicianStatus(technician)); } vo.setMediaList(mediaVOList); // 计算距离(如果有位置信息) 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; } /** * 批量查询技师信息 * * @param technicianIds 技师ID列表 * @return Map 技师ID-技师实体映射 */ private Map getTechnicianMap(List technicianIds) { List 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) * * @param lat1 纬度1(用户纬度) * @param lon1 经度1(用户经度) * @param lat2 纬度2(技师纬度) * @param lon2 经度2(技师经度) * @return double 距离(单位: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; } /** * 发布动态或保存草稿 * * @param dto 发布动态请求对象 * @param openId 技师OpenID * @return Long 动态ID */ @Override @Transactional(rollbackFor = Exception.class) public Long publishMoment(PublishMomentDTO dto, String openId) { // 1. 参数校验 if (dto == null) { throw new ServiceException("发布动态信息不能为空"); } if (openId == null || openId.trim().isEmpty()) { throw new ServiceException("openId不能为空"); } // 2. 根据openId查询技师信息 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(TJs::getcOpenId, openId); TJs technician = tJsMapper.selectOne(queryWrapper); if (technician == null) { throw new ServiceException("技师信息不存在"); } // 3. 校验媒体类型和媒体文件 if (dto.getMediaType() == null) { throw new ServiceException("媒体类型不能为空"); } if (dto.getMediaType() == 1) { // 图片类型 if (dto.getImageUrls() == null || dto.getImageUrls().isEmpty()) { throw new ServiceException("图片不能为空"); } if (dto.getImageUrls().size() > 9) { throw new ServiceException("最多只能上传9张图片"); } } else if (dto.getMediaType() == 2) { // 视频类型 if (dto.getVideoUrl() == null || dto.getVideoUrl().trim().isEmpty()) { throw new ServiceException("视频不能为空"); } } else { throw new ServiceException("媒体类型不正确"); } // 4. 校验可见范围 if (dto.getVisibleRange() == null || dto.getVisibleRange() != 1) { throw new ServiceException("可见范围只能选择公开"); } // 5. 创建动态对象 TechnicianMoment moment = new TechnicianMoment(); moment.setTechnicianId(technician.getId()); moment.setTitle(dto.getTitle()); moment.setContent(dto.getContent()); moment.setMediaType(dto.getMediaType()); moment.setVisibleRange(dto.getVisibleRange()); moment.setLatitude(dto.getLatitude()); moment.setLongitude(dto.getLongitude()); moment.setLocation(dto.getLocation()); moment.setAddress(dto.getAddress()); // 设置封面图 if (dto.getMediaType() == 1) { // 图片类型,默认取第一张作为封面 if (dto.getImageUrls() != null && !dto.getImageUrls().isEmpty()) { moment.setCoverUrl(dto.getImageUrls().get(0)); } } else if (dto.getMediaType() == 2) { // 视频类型,使用视频URL作为封面 moment.setCoverUrl(dto.getVideoUrl()); } // 6. 设置审核状态和发布时间 if (dto.getIsDraft() != null && dto.getIsDraft() == 1) { // 保存草稿 moment.setAuditStatus(0); moment.setPublishTime(null); } else { // 立即发布,进入待审核状态 moment.setAuditStatus(1); moment.setPublishTime(LocalDateTime.now()); } // 7. 设置初始状态 moment.setStatus(1); // 正常 moment.setViewCount(0); moment.setCreateTime(LocalDateTime.now()); moment.setUpdateTime(LocalDateTime.now()); // 8. 保存动态到数据库 int insertResult = momentMapper.insert(moment); if (insertResult <= 0) { throw new ServiceException("发布动态失败"); } Long momentId = moment.getId(); // 9. 保存媒体信息到媒体表 if (dto.getMediaType() == 1) { // 保存图片 if (dto.getImageUrls() != null && !dto.getImageUrls().isEmpty()) { for (int i = 0; i < dto.getImageUrls().size(); i++) { MomentMedia media = new MomentMedia(); media.setMomentId(momentId); media.setMediaUrl(dto.getImageUrls().get(i)); media.setMediaType(1); // 图片 media.setSortOrder(i + 1); media.setCreateTime(LocalDateTime.now()); //获取文件的格式 String fileFormat = dto.getImageUrls().get(i).substring(dto.getImageUrls().get(i).lastIndexOf(".") + 1); media.setFileFormat(fileFormat); mediaMapper.insert(media); } } } else if (dto.getMediaType() == 2) { // 保存视频 MomentMedia media = new MomentMedia(); media.setMomentId(momentId); media.setMediaUrl(dto.getVideoUrl()); media.setMediaType(2); // 视频 media.setSortOrder(1); media.setCreateTime(LocalDateTime.now()); mediaMapper.insert(media); } log.info("发布动态成功,动态ID:{},技师ID:{},是否草稿:{}", momentId, technician.getId(), dto.getIsDraft()); return momentId; } /** * 编辑动态 * * @param dto 编辑动态请求对象 * @param openId 技师OpenID * @return Long 动态ID */ @Override @Transactional(rollbackFor = Exception.class) public Long editMoment(EditMomentDTO dto, String openId) { // 1. 参数校验 if (dto == null) { throw new ServiceException("编辑动态信息不能为空"); } if (openId == null || openId.trim().isEmpty()) { throw new ServiceException("openId不能为空"); } if (dto.getMomentId() == null) { throw new ServiceException("动态ID不能为空"); } // 2. 根据openId查询技师信息 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(TJs::getcOpenId, openId); TJs technician = tJsMapper.selectOne(queryWrapper); if (technician == null) { throw new ServiceException("技师信息不存在"); } // 3. 查询动态信息 TechnicianMoment moment = momentMapper.selectById(dto.getMomentId()); if (moment == null) { throw new ServiceException("动态不存在"); } // 4. 验证动态是否属于当前技师 if (!moment.getTechnicianId().equals(technician.getId())) { throw new ServiceException("无权编辑此动态"); } // 5. 验证动态状态是否允许编辑(只能编辑草稿和审核拒绝的动态) if (moment.getAuditStatus() == null || (moment.getAuditStatus() != 0 && moment.getAuditStatus() != 3)) { throw new ServiceException("只能编辑草稿或审核拒绝的动态"); } // 6. 校验媒体类型和媒体文件 if (dto.getMediaType() == null) { throw new ServiceException("媒体类型不能为空"); } if (dto.getMediaType() == 1) { // 图片类型 if (dto.getImageUrls() == null || dto.getImageUrls().isEmpty()) { throw new ServiceException("图片不能为空"); } if (dto.getImageUrls().size() > 9) { throw new ServiceException("最多只能上传9张图片"); } } else if (dto.getMediaType() == 2) { // 视频类型 if (dto.getVideoUrl() == null || dto.getVideoUrl().trim().isEmpty()) { throw new ServiceException("视频不能为空"); } } else { throw new ServiceException("媒体类型不正确"); } // 7. 校验可见范围 if (dto.getVisibleRange() == null || dto.getVisibleRange() != 1) { throw new ServiceException("可见范围只能选择公开"); } // 8. 更新动态对象 moment.setTitle(dto.getTitle()); moment.setContent(dto.getContent()); moment.setMediaType(dto.getMediaType()); moment.setVisibleRange(dto.getVisibleRange()); moment.setLongitude(dto.getLongitude()); moment.setLatitude(dto.getLatitude()); moment.setLocation(dto.getLocation()); moment.setAddress(dto.getAddress()); // 设置封面图 if (dto.getMediaType() == 1) { // 图片类型,默认取第一张作为封面 if (dto.getImageUrls() != null && !dto.getImageUrls().isEmpty()) { moment.setCoverUrl(dto.getImageUrls().get(0)); } } else if (dto.getMediaType() == 2) { // 视频类型,使用视频封面URL(如果提供了)或视频URL作为封面 if (dto.getVideoCoverUrl() != null && !dto.getVideoCoverUrl().trim().isEmpty()) { moment.setCoverUrl(dto.getVideoCoverUrl()); } else { moment.setCoverUrl(dto.getVideoUrl()); } } // 9. 设置审核状态和发布时间 if (dto.getIsDraft() != null && dto.getIsDraft() == 1) { // 保存草稿 moment.setAuditStatus(0); // 草稿 moment.setPublishTime(null); moment.setRejectReason(null); // 清空拒绝原因 } else { // 提交发布,进入待审核状态 moment.setAuditStatus(1); if (moment.getPublishTime() == null) { moment.setPublishTime(LocalDateTime.now()); } moment.setRejectReason(null); // 清空拒绝原因 } moment.setUpdateTime(LocalDateTime.now()); // 10. 更新动态到数据库 int updateResult = momentMapper.updateById(moment); if (updateResult <= 0) { throw new ServiceException("编辑动态失败"); } Long momentId = moment.getId(); // 11. 删除旧的媒体信息 LambdaQueryWrapper mediaWrapper = new LambdaQueryWrapper<>(); mediaWrapper.eq(MomentMedia::getMomentId, momentId); mediaMapper.delete(mediaWrapper); // 12. 保存新的媒体信息到媒体表 if (dto.getMediaType() == 1) { // 保存图片 if (dto.getImageUrls() != null && !dto.getImageUrls().isEmpty()) { for (int i = 0; i < dto.getImageUrls().size(); i++) { MomentMedia media = new MomentMedia(); media.setMomentId(momentId); media.setMediaUrl(dto.getImageUrls().get(i)); media.setMediaType(1); // 图片 media.setSortOrder(i + 1); media.setCreateTime(LocalDateTime.now()); // 获取文件的格式 String fileFormat = dto.getImageUrls().get(i).substring(dto.getImageUrls().get(i).lastIndexOf(".") + 1); media.setFileFormat(fileFormat); mediaMapper.insert(media); } } } else if (dto.getMediaType() == 2) { // 保存视频 MomentMedia media = new MomentMedia(); media.setMomentId(momentId); media.setMediaUrl(dto.getVideoUrl()); media.setMediaType(2); // 视频 media.setSortOrder(1); media.setCreateTime(LocalDateTime.now()); mediaMapper.insert(media); } log.info("编辑动态成功,动态ID:{},技师ID:{},是否草稿:{}", momentId, technician.getId(), dto.getIsDraft()); return momentId; } /** * 查询草稿箱列表 * * @param openId 技师OpenID * @param pageNum 页码 * @param pageSize 每页数量 * @return List 草稿列表 */ @Override public Page getDraftMoments(String openId, Integer pageNum, Integer pageSize) { if (openId == null || openId.trim().isEmpty()) { throw new ServiceException("openId不能为空"); } // 1. 根据openId查询技师信息 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(TJs::getcOpenId, openId); TJs technician = tJsMapper.selectOne(queryWrapper); if (technician == null) { throw new ServiceException("技师信息不存在"); } // 2. 查询该技师的草稿列表 Page page = new Page<>(pageNum, pageSize); LambdaQueryWrapper momentWrapper = new LambdaQueryWrapper<>(); momentWrapper.eq(TechnicianMoment::getTechnicianId, technician.getId()) .eq(TechnicianMoment::getAuditStatus, 0) // 草稿状态 .eq(TechnicianMoment::getStatus, 1) // 正常状态 .orderByDesc(TechnicianMoment::getCreateTime); Page momentPage = momentMapper.selectPage(page, momentWrapper); List moments = momentPage.getRecords(); Page voPage = new Page<>(pageNum, pageSize); if (moments == null || moments.isEmpty()) { // 草稿列表为空时,返回空Page对象 return voPage; } // 3. 组装VO List collect = moments.stream().map(moment -> { MomentListVO vo = new MomentListVO(); BeanUtils.copyProperties(moment, vo); vo.setTechnicianNickName(technician.getcNickName()); vo.setTechnicianAvatar(technician.getcPortrait()); vo.setTechnicianStatus(getTechnicianStatus(technician)); // 草稿的发布时间使用创建时间 vo.setPublishTime(moment.getCreateTime()); return vo; }).collect(Collectors.toList()); //返回Page对象 voPage.setTotal(momentPage.getTotal()); voPage.setRecords(collect); return voPage; } /** * 查询我的动态列表(已发布的动态:审核中+审核通过) * * @param openId 技师OpenID * @param pageNum 页码 * @param pageSize 每页数量 * @return Page 我的动态列表 */ @Override public Page getMyMoments(String openId, Integer pageNum, Integer pageSize) { // 1. 参数校验 if (openId == null || openId.trim().isEmpty()) { throw new ServiceException("openId不能为空"); } // 2. 根据openId查询技师信息 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(TJs::getcOpenId, openId); TJs technician = tJsMapper.selectOne(queryWrapper); if (technician == null) { throw new ServiceException("技师信息不存在"); } // 3. 查询该技师的已发布动态(待审核、审核通过) Page page = new Page<>(pageNum, pageSize); LambdaQueryWrapper momentWrapper = new LambdaQueryWrapper<>(); momentWrapper.eq(TechnicianMoment::getTechnicianId, technician.getId()) .in(TechnicianMoment::getAuditStatus, 1, 2) // 1-待审核, 2-审核通过 .eq(TechnicianMoment::getStatus, 1) // 正常状态 .orderByDesc(TechnicianMoment::getPublishTime); Page momentPage = momentMapper.selectPage(page, momentWrapper); // 4. 组装分页返回结果 Page voPage = new Page<>(pageNum, pageSize); voPage.setTotal(momentPage.getTotal()); voPage.setCurrent(momentPage.getCurrent()); voPage.setSize(momentPage.getSize()); List moments = momentPage.getRecords(); if (moments == null || moments.isEmpty()) { voPage.setRecords(new ArrayList<>()); return voPage; } // 5. 组装VO列表 List voList = moments.stream().map(moment -> { MyMomentVO vo = new MyMomentVO(); vo.setId(moment.getId()); vo.setTitle(moment.getTitle()); vo.setAuditStatus(moment.getAuditStatus()); vo.setCoverUrl(moment.getCoverUrl()); vo.setPublishTime(moment.getPublishTime()); vo.setViewCount(moment.getViewCount()); return vo; }).collect(Collectors.toList()); voPage.setRecords(voList); log.info("查询我的动态列表成功,技师ID:{},页码:{},每页数量:{},总记录数:{}", technician.getId(), pageNum, pageSize, voPage.getTotal()); return voPage; } /** * 查询动态列表(管理后台) * * @param queryDTO 查询参数 * @return Page分页结果 */ @Override public Page getMomentManageList(MomentManageQueryDTO queryDTO) { // 1. 参数处理 Integer pageNum = queryDTO.getPageNum() != null ? queryDTO.getPageNum() : 1; Integer pageSize = queryDTO.getPageSize() != null ? queryDTO.getPageSize() : 10; String technicianName = queryDTO.getTechnicianName(); Integer auditStatus = queryDTO.getAuditStatus(); String startTime = queryDTO.getStartTime(); if(StringUtils.isNotBlank(startTime)){ startTime = startTime + " 00:00:00"; } String endTime = queryDTO.getEndTime(); if(StringUtils.isNotBlank(endTime)){ endTime = endTime + " 23:59:59"; } // 2. 构建查询条件 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(TechnicianMoment::getStatus, 1); // 只查询正常状态的动态 // 如果传入了审核状态,则按照审核状态查询 if (auditStatus != null) { queryWrapper.eq(TechnicianMoment::getAuditStatus, auditStatus); } else { // 默认查询全部的动态(待审核、审核通过、审核拒绝) queryWrapper.in(TechnicianMoment::getAuditStatus, 1, 2, 3); } // 技师姓名查询 if (technicianName != null && !technicianName.trim().isEmpty()) { //根据技师姓名查询技师的ID LambdaQueryWrapper jsQueryWrapper = new LambdaQueryWrapper<>(); jsQueryWrapper.like(TJs::getcNickName, technicianName); TJs technician = tJsMapper.selectOne(jsQueryWrapper); if (technician == null) { throw new ServiceException("技师不存在"); } queryWrapper.eq(TechnicianMoment::getTechnicianId, technician.getId()); } // 时间范围查询 if (startTime != null && !startTime.trim().isEmpty()) { queryWrapper.ge(TechnicianMoment::getPublishTime, startTime); } if (endTime != null && !endTime.trim().isEmpty()) { queryWrapper.le(TechnicianMoment::getPublishTime, endTime); } // 按创建时间倒序排序 queryWrapper.orderByDesc(TechnicianMoment::getCreateTime); // 3. 分页查询 Page page = new Page<>(pageNum, pageSize); Page momentPage = momentMapper.selectPage(page, queryWrapper); // 4. 组装VO Page voPage = new Page<>(pageNum, pageSize); voPage.setTotal(momentPage.getTotal()); voPage.setCurrent(momentPage.getCurrent()); voPage.setSize(momentPage.getSize()); List moments = momentPage.getRecords(); if (moments == null || moments.isEmpty()) { voPage.setRecords(new ArrayList<>()); return voPage; } // 5. 批量查询技师信息 List technicianIds = moments.stream() .map(TechnicianMoment::getTechnicianId) .collect(Collectors.toList()); Map technicianMap = getTechnicianMap(technicianIds); // 6. 组装VO列表 List voList = moments.stream().map(moment -> { MomentManageVO vo = new MomentManageVO(); BeanUtils.copyProperties(moment, vo); // 设置技师信息 TJs technician = technicianMap.get(moment.getTechnicianId()); if (technician != null) { // 设置技师姓名 vo.setTechnicianName(technician.getcName()); // 设置技师昵称 vo.setTechnicianNickname(technician.getcNickName()); } return vo; }).collect(Collectors.toList()); voPage.setRecords(voList); log.info("查询动态管理列表,页码:{},每页数量:{},总记录数:{}", pageNum, pageSize, voPage.getTotal()); return voPage; } /** * 审核动态(通过/拒绝) * * @param momentId 动态ID * @param auditStatus 审核状态:2-通过,3-拒绝 * @param rejectReason 拒绝原因(拒绝时必填) * @return 是否成功 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean auditMoment(MomentAuditDTO dto) { // 1. 参数校验 if (dto.getMomentId() == null) { throw new ServiceException("动态ID不能为空"); } if (dto.getAuditStatus() == null || (dto.getAuditStatus() != 2 && dto.getAuditStatus() != 3)) { throw new ServiceException("审核状态不正确"); } if (dto.getAuditStatus() == 3 && (dto.getRejectReason() == null || dto.getRejectReason().trim().isEmpty())) { throw new ServiceException("拒绝时必须填写拒绝原因"); } // 2. 查询动态信息 TechnicianMoment moment = momentMapper.selectById(dto.getMomentId()); if (moment == null) { throw new ServiceException("动态不存在"); } // 3. 校验动态状态 if (moment.getStatus() != 1) { throw new ServiceException("动态已删除,无法审核"); } if (moment.getAuditStatus() != 1) { throw new ServiceException("只能审核待审核状态的动态"); } // 4. 更新审核状态 moment.setAuditStatus(dto.getAuditStatus()); moment.setRejectReason(dto.getRejectReason()); moment.setUpdateTime(LocalDateTime.now()); int updateResult = momentMapper.updateById(moment); if (updateResult <= 0) { throw new ServiceException("审核失败"); } log.info("审核动态成功,动态ID:{},审核结果:{},拒绝原因:{}", dto.getMomentId(), dto.getAuditStatus(), dto.getRejectReason()); return true; } /** * 根据动态ID查询动态简要详情(包含媒体URL列表) * * @param momentId 动态ID * @return MomentSimpleDetailVO动态简要详情 */ @Override public MomentSimpleDetailVO getMomentSimpleDetail(Long momentId) { // 1. 参数校验 if (momentId == null) { throw new ServiceException("动态ID不能为空"); } // 2. 查询动态信息 Map result = momentMapper.selectMomentSimpleDetail(momentId); if (result == null || result.isEmpty()) { throw new ServiceException("动态不存在"); } // 3. 组装VO MomentSimpleDetailVO vo = new MomentSimpleDetailVO(); vo.setId(((Number) result.get("id")).longValue()); vo.setTitle((String) result.get("title")); vo.setContent((String) result.get("content")); vo.setMediaType((Integer) result.get("media_type")); // 4. 处理媒体URL列表 String mediaUrlsStr = (String) result.get("media_urls"); if (mediaUrlsStr != null && !mediaUrlsStr.trim().isEmpty()) { // 将逗号分隔的字符串转换为List List mediaUrls = Arrays.asList(mediaUrlsStr.split(",")); vo.setMediaUrls(mediaUrls); } else { vo.setMediaUrls(new ArrayList<>()); } log.info("查询动态简要详情成功,动态ID:{}", momentId); return vo; } }