import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; /** * 滞留件管理服务类 * 核心功能:滞留件检测、通知管理、超时处理、统计分析 */ public class OverduePackageService { // 配置参数(可通过配置文件加载) private static final int MAX_FREE_DAYS = 7; // 最大免费存放天数 private static final double DAILY_STORAGE_FEE = 1.0; // 每日滞留费用(元) private static final int NOTICE_INTERVAL = 3; // 提醒间隔天数 // 依赖服务注入 private final ExpressService expressService; private final NotificationService notificationService; // 内部状态管理 private final Map overdueRecords = new HashMap<>(); private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); /** * 构造函数(依赖注入模式) */ public OverduePackageService(ExpressService expressService, NotificationService notificationService) { this.expressService = expressService; this.notificationService = notificationService; initializeOverdueMonitoring(); } /** * 初始化滞留件监控线程 */ private void initializeOverdueMonitoring() { // 创建定时任务(每24小时执行一次检测) Timer timer = new Timer(true); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { detectAndProcessOverduePackages(); } }, 0, 24 * 60 * 60 * 1000); } /** * 滞留件检测与处理主逻辑 */ public synchronized void detectAndProcessOverduePackages() { // 1. 获取所有待取件包裹 List allPackages = expressService.getAllPackages() .stream() .filter(item -> "待取件".equals(item.getStatus())) .collect(Collectors.toList()); // 2. 计算存放天数并识别滞留件 List overduePackages = new ArrayList<>(); for (ExpressItem item : allPackages) { int storageDays = calculateStorageDays(item.getArrivalTime()); item.setStorageDays(storageDays); if (storageDays > MAX_FREE_DAYS) { overduePackages.add(item); recordOverdueHistory(item, storageDays); } } // 3. 执行滞留处理流程 overduePackages.forEach(this::processOverduePackage); } /** * 计算包裹存放天数(精确到小时) */ private int calculateStorageDays(Date arrivalTime) { long now = System.currentTimeMillis(); long elapsed = now - arrivalTime.getTime(); return (int) (elapsed / (24 * 3600 * 1000)); } /** * 记录滞留历史(防重复处理) */ private void recordOverdueHistory(ExpressItem item, int currentDays) { String trackingNumber = item.getTrackingNumber(); overdueRecords.putIfAbsent(trackingNumber, new OverdueRecord()); OverdueRecord record = overdueRecords.get(trackingNumber); if (record.getLastNoticeDay() == 0 || (currentDays - record.getLastNoticeDay()) >= NOTICE_INTERVAL) { record.setLastNoticeDay(currentDays); record.incrementNoticeCount(); } } /** * 单个滞留件处理流程 */ private void processOverduePackage(ExpressItem item) { // 1. 费用计算 double overdueFee = calculateOverdueFee(item.getStorageDays()); // 2. 生成通知内容 String noticeContent = buildNoticeContent(item, overdueFee); // 3. 发送通知(支持多通道) sendMultiChannelNotice(item.getPhone(), noticeContent); // 4. 标记为滞留状态(可选扩展) if (item.getStorageDays() > MAX_FREE_DAYS * 2) { expressService.markAsOverdue(item.getTrackingNumber()); } } /** * 计算滞留费用(简单线性计费) */ private double calculateOverdueFee(int storageDays) { int overdueDays = storageDays - MAX_FREE_DAYS; return overdueDays > 0 ? overdueDays * DAILY_STORAGE_FEE : 0; } /** * 构建多格式通知内容 */ private String buildNoticeContent(ExpressItem item, double fee) { return String.format(""" 【快递滞留提醒】 运单号:%s 收件人:%s 滞留天数:%d天 当前费用:%.2f元 请尽快取件避免额外费用! """, item.getTrackingNumber(), item.getRecipient(), item.getStorageDays(), fee); } /** * 多通道通知发送(短信+邮件+系统消息) */ private void sendMultiChannelNotice(String phone, String content) { // 短信通知 notificationService.sendSMS(phone, content); // 邮件通知(需实现邮件服务) // notificationService.sendEmail(recipientEmail, "快递滞留提醒", content); // 系统内消息(需集成消息中心) // messageCenter.pushSystemNotice(userId, content); } /** * 滞留件统计报表生成 */ public String generateOverdueReport() { Map dailyCount = overdueRecords.values().stream() .collect(Collectors.groupingBy( OverdueRecord::getNoticeCount, Collectors.counting() )); StringBuilder report = new StringBuilder(); report.append("===== 滞留件统计报表 =====\n"); report.append("统计时间:").append(dateFormat.format(new Date())).append("\n"); report.append("-------------------------\n"); report.append("总滞留包裹数:").append(overdueRecords.size()).append("\n"); dailyCount.forEach((times, count) -> report.append(String.format("被提醒%d次包裹数:%d\n", times, count))); return report.toString(); } /** * 滞留记录实体类 */ private static class OverdueRecord { private int lastNoticeDay; // 最后一次通知时的存放天数 private int noticeCount; // 通知发送次数 // Getter/Setter public int getLastNoticeDay() { return lastNoticeDay; } public void setLastNoticeDay(int day) { this.lastNoticeDay = day; } public int getNoticeCount() { return noticeCount; } public void incrementNoticeCount() { this.noticeCount++; } } /** * 通知服务接口(需具体实现) */ public interface NotificationService { void sendSMS(String phone, String message); // void sendEmail(String to, String subject, String content); } /** * 滞留件查询服务 */ public List queryOverduePackages(int minDays, int maxDays) { return expressService.getAllPackages().stream() .filter(item -> { int days = item.getStorageDays(); return days >= minDays && days <= maxDays; }) .sorted(Comparator.comparingInt(ExpressItem::getStorageDays).reversed()) .collect(Collectors.toList()); } /** * 批量处理滞留件(自动退回) */ public int batchReturnOverduePackages(int maxDays) { List toReturn = queryOverduePackages(maxDays, Integer.MAX_VALUE); toReturn.forEach(item -> expressService.markAsReturned(item.getTrackingNumber())); return toReturn.size(); } /** * 滞留费用明细查询 */ public Map getOverdueFees(String trackingNumber) { OverdueRecord record = overdueRecords.get(trackingNumber); if (record == null) return Collections.emptyMap(); Map feeDetail = new HashMap<>(); feeDetail.put("baseFee", (double) (record.getNoticeCount() * NOTICE_INTERVAL)); feeDetail.put("totalFee", calculateOverdueFee(record.getNoticeCount() * NOTICE_INTERVAL)); return feeDetail; } }