新增服务: - NotificationService 多渠道通知服务 - 支持短信/邮件/微信/站内信/电话 - 借阅成功/归还提醒/逾期通知/罚款通知 - 预约到书/续借成功/AI推荐等通知类型 - ReservationService 预约管理服务 - 图书预约/取消预约 - 预约队列管理 - 到期自动处理 - LoanHistoryService 借阅历史服务 - 借阅操作历史记录 - 续借功能(最多1次,延期14天) - 罚款计算(每日0.5元) AI增强 (SmartAIService): - 用户借阅行为分析 - 逾期风险预测 - 智能通知内容生成 - 最佳通知时机建议 Web页面: - /notifications - 通知中心 - /history - 借阅历史 - /reservations - 预约管理 - 续借功能集成 导航栏更新: - 添加预约/历史/通知入口main
parent
cda70c5acb
commit
b777225bd2
@ -0,0 +1,149 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>借阅历史 - MCSLMS v1.5.0</title>
|
||||
<th:block th:replace="~{fragments/layout :: styles}"/>
|
||||
<style>
|
||||
.history-timeline {
|
||||
position: relative;
|
||||
padding-left: 30px;
|
||||
}
|
||||
.history-timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: #dee2e6;
|
||||
}
|
||||
.history-item {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
.history-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
top: 20px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #007bff;
|
||||
}
|
||||
.action-BORROW::before { background: #28a745; }
|
||||
.action-RETURN::before { background: #17a2b8; }
|
||||
.action-RENEWAL::before { background: #ffc107; }
|
||||
.action-OVERDUE::before { background: #dc3545; }
|
||||
.action-FINE_PAID::before { background: #6c757d; }
|
||||
.fine-alert {
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a5a 100%);
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav th:replace="~{fragments/layout :: navbar('history')}"/>
|
||||
|
||||
<main class="container mt-4">
|
||||
<h1 class="mb-4">📜 借阅历史</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>历史记录</span>
|
||||
<span class="badge bg-secondary" th:text="${#lists.size(history)} + ' 条记录'">0 条记录</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div th:if="${#lists.isEmpty(history)}" class="text-center text-muted py-5">
|
||||
📭 暂无借阅历史记录
|
||||
</div>
|
||||
|
||||
<div class="history-timeline" th:if="${!#lists.isEmpty(history)}">
|
||||
<div th:each="record : ${history}"
|
||||
class="history-item"
|
||||
th:classappend="'action-' + ${record.action().name()}">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="badge"
|
||||
th:classappend="${record.action().name() == 'BORROW'} ? 'bg-success' :
|
||||
(${record.action().name() == 'RETURN'} ? 'bg-info' :
|
||||
(${record.action().name() == 'RENEWAL'} ? 'bg-warning' :
|
||||
(${record.action().name() == 'OVERDUE'} ? 'bg-danger' : 'bg-secondary')))"
|
||||
th:text="${record.action().getDisplayName()}">操作</span>
|
||||
<strong class="ms-2" th:text="${record.bookTitle() != null} ? '《' + ${record.bookTitle()} + '》' : '图书'">书名</strong>
|
||||
</div>
|
||||
<small class="text-muted" th:text="${record.actionDate()}">时间</small>
|
||||
</div>
|
||||
<p class="mb-0 mt-2 text-muted small" th:if="${record.details()}"
|
||||
th:text="${record.details()}">详情</p>
|
||||
<div class="mt-2" th:if="${record.fineAmount() > 0}">
|
||||
<span class="badge bg-danger">罚款: ¥<span th:text="${#numbers.formatDecimal(record.fineAmount(), 1, 2)}">0.00</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<!-- 罚款汇总 -->
|
||||
<div class="fine-alert mb-4" th:if="${totalFine > 0}">
|
||||
<h5>⚠️ 待缴罚款</h5>
|
||||
<div class="display-4">¥<span th:text="${#numbers.formatDecimal(totalFine, 1, 2)}">0.00</span></div>
|
||||
<p class="mb-0 mt-2 small">请尽快处理,避免影响借阅权限</p>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4" th:if="${totalFine == 0}">
|
||||
<div class="card-body text-center">
|
||||
<span style="font-size: 3rem;">✅</span>
|
||||
<h5 class="mt-2">无待缴罚款</h5>
|
||||
<p class="text-muted small">保持良好的借阅习惯!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作说明 -->
|
||||
<div class="card">
|
||||
<div class="card-header">📌 操作说明</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li class="mb-2">
|
||||
<span class="badge bg-success">借阅</span>
|
||||
<small class="text-muted">成功借出图书</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<span class="badge bg-info">归还</span>
|
||||
<small class="text-muted">图书已归还</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<span class="badge bg-warning">续借</span>
|
||||
<small class="text-muted">延长借阅期限</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<span class="badge bg-danger">逾期</span>
|
||||
<small class="text-muted">超过应还日期</small>
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge bg-secondary">缴费</span>
|
||||
<small class="text-muted">缴纳罚款</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer th:replace="~{fragments/layout :: footer}"/>
|
||||
<th:block th:replace="~{fragments/layout :: scripts}"/>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>通知中心 - MCSLMS v1.5.0</title>
|
||||
<th:block th:replace="~{fragments/layout :: styles}"/>
|
||||
<style>
|
||||
.notification-card {
|
||||
border-left: 4px solid #007bff;
|
||||
margin-bottom: 15px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.notification-card.unread {
|
||||
background-color: #f0f7ff;
|
||||
border-left-color: #28a745;
|
||||
}
|
||||
.notification-card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
.notification-type {
|
||||
font-size: 0.75rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.type-BORROW_SUCCESS { background: #d4edda; color: #155724; }
|
||||
.type-RETURN_REMINDER { background: #fff3cd; color: #856404; }
|
||||
.type-OVERDUE_NOTICE { background: #f8d7da; color: #721c24; }
|
||||
.type-FINE_NOTICE { background: #f8d7da; color: #721c24; }
|
||||
.type-RESERVATION_AVAILABLE { background: #cce5ff; color: #004085; }
|
||||
.type-RECOMMENDATION { background: #e2d5f1; color: #4a148c; }
|
||||
.channel-icon { font-size: 1.2rem; margin-right: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav th:replace="~{fragments/layout :: navbar('notifications')}"/>
|
||||
|
||||
<main class="container mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>🔔 通知中心</h1>
|
||||
<div>
|
||||
<span class="badge bg-danger me-2" th:if="${unreadCount > 0}" th:text="${unreadCount} + ' 条未读'">0 条未读</span>
|
||||
<a th:href="@{/notifications(userId=${userId})}" class="btn btn-outline-primary btn-sm">刷新</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">通知渠道</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group-item">📱 短信通知</div>
|
||||
<div class="list-group-item">📧 邮件通知</div>
|
||||
<div class="list-group-item">💬 微信推送</div>
|
||||
<div class="list-group-item">🔔 站内消息</div>
|
||||
<div class="list-group-item">📞 电话提醒</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">通知类型</div>
|
||||
<div class="list-group list-group-flush small">
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<span>借阅成功</span>
|
||||
<span class="notification-type type-BORROW_SUCCESS">✓</span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<span>归还提醒</span>
|
||||
<span class="notification-type type-RETURN_REMINDER">⏰</span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<span>逾期通知</span>
|
||||
<span class="notification-type type-OVERDUE_NOTICE">⚠️</span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<span>预约到书</span>
|
||||
<span class="notification-type type-RESERVATION_AVAILABLE">📚</span>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<span>图书推荐</span>
|
||||
<span class="notification-type type-RECOMMENDATION">🎯</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
<div th:if="${#lists.isEmpty(notifications)}" class="alert alert-info">
|
||||
📭 暂无通知消息
|
||||
</div>
|
||||
|
||||
<div th:each="notif : ${notifications}"
|
||||
class="card notification-card"
|
||||
th:classappend="${!notif.isRead()} ? 'unread'">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="notification-type" th:classappend="'type-' + ${notif.type()}"
|
||||
th:text="${notif.type().getDisplayName()}">类型</span>
|
||||
<span class="channel-icon ms-2" th:switch="${notif.channel().name()}">
|
||||
<span th:case="'SMS'">📱</span>
|
||||
<span th:case="'EMAIL'">📧</span>
|
||||
<span th:case="'WECHAT'">💬</span>
|
||||
<span th:case="'IN_APP'">🔔</span>
|
||||
<span th:case="'PHONE'">📞</span>
|
||||
</span>
|
||||
</div>
|
||||
<small class="text-muted" th:text="${notif.sentAt()}">时间</small>
|
||||
</div>
|
||||
<h6 class="card-title mt-2" th:text="${notif.title()}">标题</h6>
|
||||
<p class="card-text small text-muted" style="white-space: pre-line;"
|
||||
th:text="${notif.content()}">内容</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer th:replace="~{fragments/layout :: footer}"/>
|
||||
<th:block th:replace="~{fragments/layout :: scripts}"/>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in new issue