package com.tourguide.modules.system.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.tourguide.common.exception.BusinessException;
|
import com.tourguide.modules.order.entity.TourOrder;
|
import com.tourguide.modules.system.dto.GuideNotificationResponse;
|
import com.tourguide.modules.system.entity.GuideNoticeRead;
|
import com.tourguide.modules.system.entity.GuideNotification;
|
import com.tourguide.modules.system.entity.SystemNotice;
|
import com.tourguide.modules.system.mapper.GuideNotificationMapper;
|
import com.tourguide.modules.system.mapper.GuideNoticeReadMapper;
|
import com.tourguide.modules.system.mapper.SystemNoticeMapper;
|
import com.tourguide.modules.user.service.GuidePushDeviceService;
|
import org.springframework.stereotype.Service;
|
|
import java.time.LocalDateTime;
|
import java.util.Collections;
|
import java.util.Comparator;
|
import java.util.List;
|
import java.util.Set;
|
import java.util.stream.Collectors;
|
|
@Service
|
public class GuideNotificationService {
|
private final SystemNoticeMapper noticeMapper;
|
private final GuideNoticeReadMapper readMapper;
|
private final GuideNotificationMapper guideNotificationMapper;
|
private final GuidePushDeviceService pushDeviceService;
|
|
public GuideNotificationService(SystemNoticeMapper noticeMapper, GuideNoticeReadMapper readMapper,
|
GuideNotificationMapper guideNotificationMapper,
|
GuidePushDeviceService pushDeviceService) {
|
this.noticeMapper = noticeMapper;
|
this.readMapper = readMapper;
|
this.guideNotificationMapper = guideNotificationMapper;
|
this.pushDeviceService = pushDeviceService;
|
}
|
|
public long unreadCount(Long guideId) {
|
List<SystemNotice> notices = publishedNotices();
|
if (notices.isEmpty()) return 0L;
|
Set<Long> readIds = readNoticeIds(guideId, notices);
|
long systemUnread = notices.stream().filter(notice -> !readIds.contains(notice.getId())).count();
|
Long personalUnread = guideNotificationMapper.selectCount(new LambdaQueryWrapper<GuideNotification>()
|
.eq(GuideNotification::getGuideId, guideId)
|
.isNull(GuideNotification::getReadAt));
|
return systemUnread + personalUnread;
|
}
|
|
public List<GuideNotificationResponse> list(Long guideId) {
|
List<SystemNotice> notices = publishedNotices();
|
Set<Long> readIds = readNoticeIds(guideId, notices);
|
List<GuideNotificationResponse> responses = notices.stream()
|
.map(notice -> new GuideNotificationResponse(notice, readIds.contains(notice.getId())))
|
.collect(Collectors.toList());
|
responses.addAll(guideNotificationMapper.selectList(new LambdaQueryWrapper<GuideNotification>()
|
.eq(GuideNotification::getGuideId, guideId)
|
.orderByDesc(GuideNotification::getCreatedAt))
|
.stream().map(GuideNotificationResponse::new).collect(Collectors.toList()));
|
responses.sort(Comparator.comparing(GuideNotificationResponse::getTime,
|
Comparator.nullsLast(Comparator.reverseOrder())));
|
return responses;
|
}
|
|
public void markRead(Long guideId, String notificationId) {
|
if (notificationId != null && notificationId.startsWith("guide:")) {
|
markPersonalRead(guideId, parseId(notificationId, "guide:"));
|
return;
|
}
|
Long noticeId = parseId(notificationId, "system:");
|
SystemNotice notice = noticeMapper.selectOne(new LambdaQueryWrapper<SystemNotice>()
|
.eq(SystemNotice::getId, noticeId)
|
.eq(SystemNotice::getStatus, 1)
|
.last("LIMIT 1"));
|
if (notice == null) throw new BusinessException(404, "Notification not found");
|
Long count = readMapper.selectCount(new LambdaQueryWrapper<GuideNoticeRead>()
|
.eq(GuideNoticeRead::getGuideId, guideId)
|
.eq(GuideNoticeRead::getNoticeId, noticeId));
|
if (count > 0) return;
|
GuideNoticeRead read = new GuideNoticeRead();
|
read.setGuideId(guideId);
|
read.setNoticeId(noticeId);
|
read.setReadAt(LocalDateTime.now());
|
readMapper.insert(read);
|
}
|
|
public void notifyOrderAssigned(TourOrder order) {
|
if (order == null || order.getGuideId() == null) return;
|
String title = "您有新的讲解订单";
|
String content = "景区:" + valueOrUnknown(order.getScenicName())
|
+ ";汇合点:" + valueOrUnknown(order.getMeetingPointName())
|
+ ";服务类型:" + valueOrUnknown(order.getPackageName(), order.getOrderType())
|
+ ";人数:" + (order.getPeopleCount() == null ? 0 : order.getPeopleCount())
|
+ "。请及时接单。";
|
String targetUrl = orderTargetUrl(order.getId());
|
create(order.getGuideId(), "ORDER_ASSIGNED", title, content, order.getId(), targetUrl);
|
pushDeviceService.push(order.getGuideId(), title, content, targetUrl);
|
}
|
|
public void notifyVisitorCancelledOrder(TourOrder order) {
|
if (order == null || order.getGuideId() == null) return;
|
String title = "订单已取消";
|
String content = "游客已取消订单,该订单无需再前往服务。";
|
String targetUrl = orderTargetUrl(order.getId());
|
create(order.getGuideId(), "ORDER_CANCELLED", title, content, order.getId(), targetUrl);
|
pushDeviceService.push(order.getGuideId(), title, content, targetUrl);
|
}
|
|
public void notifyOrderCancelled(TourOrder order) {
|
if (order == null || order.getGuideId() == null) return;
|
create(order.getGuideId(), "ORDER_CANCELLED", "取消提醒", "您有一个被取消的讲解订单,请查看",
|
order.getId(), orderTargetUrl(order.getId()));
|
}
|
|
public boolean pushServiceReminder(TourOrder order) {
|
if (order == null || order.getGuideId() == null) return false;
|
String title = "请准备前往汇合点";
|
String content = "您的讲解服务即将开始,请提前到达 "
|
+ valueOrUnknown(order.getMeetingPointName()) + "。";
|
return pushDeviceService.push(order.getGuideId(), title, content, orderTargetUrl(order.getId()));
|
}
|
|
public void notifyAuditResult(Long guideId, boolean approved) {
|
create(guideId, approved ? "AUDIT_APPROVED" : "AUDIT_REJECTED", "【名导精讲】",
|
approved ? "您的资料审核已通过,请前往应用查看。" : "您的资料审核未通过,请前往应用更新提交。",
|
null, null);
|
}
|
|
private void create(Long guideId, String type, String title, String content, Long orderId, String targetUrl) {
|
if (guideId == null) return;
|
GuideNotification notification = new GuideNotification();
|
notification.setGuideId(guideId);
|
notification.setNotificationType(type);
|
notification.setTitle(title);
|
notification.setContent(content);
|
notification.setOrderId(orderId);
|
notification.setTargetUrl(targetUrl);
|
notification.setCreatedAt(LocalDateTime.now());
|
guideNotificationMapper.insert(notification);
|
}
|
|
private String orderTargetUrl(Long orderId) {
|
return orderId == null ? null : "/pages/order-detail/index?id=" + orderId;
|
}
|
|
private String valueOrUnknown(String primary, String fallback) {
|
if (primary != null && !primary.trim().isEmpty()) return primary;
|
return valueOrUnknown(fallback);
|
}
|
|
private String valueOrUnknown(String value) {
|
return value == null || value.trim().isEmpty() ? "未知" : value;
|
}
|
|
private void markPersonalRead(Long guideId, Long notificationId) {
|
GuideNotification notification = guideNotificationMapper.selectOne(new LambdaQueryWrapper<GuideNotification>()
|
.eq(GuideNotification::getId, notificationId)
|
.eq(GuideNotification::getGuideId, guideId)
|
.last("LIMIT 1"));
|
if (notification == null) throw new BusinessException(404, "Notification not found");
|
if (notification.getReadAt() != null) return;
|
notification.setReadAt(LocalDateTime.now());
|
guideNotificationMapper.updateById(notification);
|
}
|
|
private Long parseId(String value, String prefix) {
|
String raw = value == null ? "" : value;
|
if (raw.startsWith(prefix)) raw = raw.substring(prefix.length());
|
try {
|
return Long.valueOf(raw);
|
} catch (NumberFormatException exception) {
|
throw new BusinessException(400, "Notification id is invalid");
|
}
|
}
|
|
private List<SystemNotice> publishedNotices() {
|
return noticeMapper.selectList(new LambdaQueryWrapper<SystemNotice>()
|
.eq(SystemNotice::getStatus, 1)
|
.isNotNull(SystemNotice::getPublishTime)
|
.le(SystemNotice::getPublishTime, LocalDateTime.now())
|
.orderByDesc(SystemNotice::getPublishTime));
|
}
|
|
private Set<Long> readNoticeIds(Long guideId, List<SystemNotice> notices) {
|
if (notices.isEmpty()) return Collections.emptySet();
|
List<Long> noticeIds = notices.stream().map(SystemNotice::getId).collect(Collectors.toList());
|
return readMapper.selectList(new LambdaQueryWrapper<GuideNoticeRead>()
|
.eq(GuideNoticeRead::getGuideId, guideId)
|
.in(GuideNoticeRead::getNoticeId, noticeIds))
|
.stream().map(GuideNoticeRead::getNoticeId).collect(Collectors.toSet());
|
}
|
}
|