新增授权码数据的管理功能,改名原本的授权码管理为订单管理
This commit is contained in:
parent
49fcf172ce
commit
9c518bf352
@ -116,7 +116,7 @@ public class AdminController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有效的授权码
|
||||
* 获取所有有效的授权码 (用于订单管理)
|
||||
* 仅管理员可访问。
|
||||
*
|
||||
* @return 授权码列表
|
||||
@ -128,6 +128,46 @@ public class AdminController {
|
||||
return jdbcTemplate.queryForList("SELECT code, order_no, login_limit, login_count, expires_at FROM auth_codes WHERE status = 1 ORDER BY created_at DESC");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有授权码 (用于授权码管理)
|
||||
* 支持分页查询
|
||||
*/
|
||||
@GetMapping("/auth-codes/list")
|
||||
@ResponseBody
|
||||
@SaCheckRole("admin")
|
||||
public Map<String, Object> getAuthCodesList(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) {
|
||||
int offset = (page - 1) * size;
|
||||
String countSql = "SELECT COUNT(*) FROM auth_codes";
|
||||
int total = jdbcTemplate.queryForObject(countSql, Integer.class);
|
||||
|
||||
String sql = "SELECT * FROM auth_codes ORDER BY created_at DESC LIMIT ? OFFSET ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, size, offset);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("total", total);
|
||||
result.put("list", list);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新授权码信息
|
||||
*/
|
||||
@PostMapping("/auth-codes/update")
|
||||
@ResponseBody
|
||||
@SaCheckRole("admin")
|
||||
public String updateAuthCode(@RequestBody Map<String, Object> payload) {
|
||||
String code = (String) payload.get("code");
|
||||
String orderNo = (String) payload.get("order_no");
|
||||
String expiresAt = (String) payload.get("expires_at");
|
||||
Integer loginLimit = Integer.parseInt(payload.get("login_limit").toString());
|
||||
Integer status = Integer.parseInt(payload.get("status").toString());
|
||||
|
||||
String sql = "UPDATE auth_codes SET order_no = ?, expires_at = ?, login_limit = ?, status = ? WHERE code = ?";
|
||||
int rows = jdbcTemplate.update(sql, orderNo, expiresAt, loginLimit, status, code);
|
||||
|
||||
return rows > 0 ? "ok" : "error";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定授权码的购物车内容
|
||||
* 仅管理员可访问。
|
||||
|
||||
@ -56,7 +56,7 @@ public class PageController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 授权码管理页面
|
||||
* 订单管理页面 (原授权码管理)
|
||||
*/
|
||||
@GetMapping("/admin/codes")
|
||||
@SaCheckRole("admin")
|
||||
@ -64,6 +64,15 @@ public class PageController {
|
||||
return "admin_codes";
|
||||
}
|
||||
|
||||
/**
|
||||
* 授权码管理页面 (新增)
|
||||
*/
|
||||
@GetMapping("/admin/auth-codes")
|
||||
@SaCheckRole("admin")
|
||||
public String adminAuthCodesPage() {
|
||||
return "admin_auth_codes";
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章管理页面
|
||||
*/
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
border-radius: 20px;
|
||||
box-shadow: var(--card-shadow);
|
||||
text-align: center;
|
||||
width: 400px;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@ -49,7 +49,7 @@
|
||||
|
||||
.menu-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
@ -102,6 +102,10 @@
|
||||
<h1>后台管理系统</h1>
|
||||
<div class="menu-grid">
|
||||
<a href="/admin/codes" class="menu-item">
|
||||
<div class="menu-icon">📦</div>
|
||||
<div class="menu-title">订单管理</div>
|
||||
</a>
|
||||
<a href="/admin/auth-codes" class="menu-item">
|
||||
<div class="menu-icon">🔑</div>
|
||||
<div class="menu-title">授权码管理</div>
|
||||
</a>
|
||||
|
||||
209
src/main/resources/templates/admin_auth_codes.html
Normal file
209
src/main/resources/templates/admin_auth_codes.html
Normal file
@ -0,0 +1,209 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>授权码管理</title>
|
||||
<script src="https://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #007bff;
|
||||
--bg-color: #e9f5ff;
|
||||
--glass-bg: rgba(255, 255, 255, 0.6);
|
||||
--card-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.07);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background-color: var(--bg-color);
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#particles-js {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
h1 { margin-top: 0; color: #333; }
|
||||
table { width: 100%; border-collapse: collapse; margin-top: 20px; background: #fff; border-radius: 8px; overflow: hidden; }
|
||||
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
|
||||
th { background-color: #f8f9fa; font-weight: 600; }
|
||||
input[type="text"], input[type="number"], input[type="datetime-local"], select {
|
||||
width: 100%; padding: 6px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;
|
||||
}
|
||||
.btn { padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; color: #fff; font-size: 14px; }
|
||||
.btn-save { background-color: #28a745; }
|
||||
.btn-save:hover { background-color: #218838; }
|
||||
.pagination { margin-top: 20px; display: flex; justify-content: center; gap: 10px; }
|
||||
.page-btn { padding: 8px 12px; border: 1px solid #ddd; background: #fff; cursor: pointer; border-radius: 4px; }
|
||||
.page-btn.active { background-color: #007bff; color: #fff; border-color: #007bff; }
|
||||
.back-link { display: inline-block; margin-bottom: 20px; color: #007bff; text-decoration: none; font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="particles-js"></div>
|
||||
|
||||
<div class="container">
|
||||
<a href="/admin" class="back-link">← 返回后台首页</a>
|
||||
<h1>授权码管理</h1>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>授权码</th>
|
||||
<th>订单号</th>
|
||||
<th>过期时间</th>
|
||||
<th>登录限制</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="table-body">
|
||||
<!-- Data will be populated here -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pagination" id="pagination"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentPage = 1;
|
||||
const pageSize = 10;
|
||||
|
||||
async function loadData(page) {
|
||||
currentPage = page;
|
||||
const response = await fetch(`/api/admin/auth-codes/list?page=${page}&size=${pageSize}`);
|
||||
const data = await response.json();
|
||||
renderTable(data.list);
|
||||
renderPagination(data.total);
|
||||
}
|
||||
|
||||
function renderTable(list) {
|
||||
const tbody = document.getElementById('table-body');
|
||||
tbody.innerHTML = '';
|
||||
list.forEach(item => {
|
||||
const tr = document.createElement('tr');
|
||||
// 格式化日期时间,适配 datetime-local 输入框 (yyyy-MM-ddTHH:mm)
|
||||
let formattedDate = '';
|
||||
if (item.expires_at) {
|
||||
// 尝试解析 ISO 格式
|
||||
try {
|
||||
const date = new Date(item.expires_at);
|
||||
// 调整时区偏移,转为本地时间字符串
|
||||
const offset = date.getTimezoneOffset() * 60000;
|
||||
const localISOTime = (new Date(date - offset)).toISOString().slice(0, 16);
|
||||
formattedDate = localISOTime;
|
||||
} catch (e) {
|
||||
// 如果解析失败,尝试直接使用(假设已经是正确格式)
|
||||
formattedDate = item.expires_at;
|
||||
}
|
||||
}
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>${item.code}</td>
|
||||
<td><input type="text" value="${item.order_no || ''}" id="order-${item.code}"></td>
|
||||
<td><input type="datetime-local" value="${formattedDate}" id="expire-${item.code}"></td>
|
||||
<td><input type="number" value="${item.login_limit}" id="limit-${item.code}"></td>
|
||||
<td>
|
||||
<select id="status-${item.code}">
|
||||
<option value="1" ${item.status === 1 ? 'selected' : ''}>有效</option>
|
||||
<option value="0" ${item.status === 0 ? 'selected' : ''}>无效</option>
|
||||
<option value="2" ${item.status === 2 ? 'selected' : ''}>已交付</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-save" onclick="saveChanges('${item.code}')">保存</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function renderPagination(total) {
|
||||
const totalPages = Math.ceil(total / pageSize);
|
||||
const pagination = document.getElementById('pagination');
|
||||
pagination.innerHTML = '';
|
||||
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = `page-btn ${i === currentPage ? 'active' : ''}`;
|
||||
btn.innerText = i;
|
||||
btn.onclick = () => loadData(i);
|
||||
pagination.appendChild(btn);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveChanges(code) {
|
||||
const orderNo = document.getElementById(`order-${code}`).value;
|
||||
const expiresAt = document.getElementById(`expire-${code}`).value;
|
||||
const loginLimit = document.getElementById(`limit-${code}`).value;
|
||||
const status = document.getElementById(`status-${code}`).value;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/auth-codes/update', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
code: code,
|
||||
order_no: orderNo,
|
||||
expires_at: expiresAt,
|
||||
login_limit: loginLimit,
|
||||
status: status
|
||||
})
|
||||
});
|
||||
|
||||
if (await response.text() === 'ok') {
|
||||
alert('保存成功');
|
||||
loadData(currentPage);
|
||||
} else {
|
||||
alert('保存失败');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络错误');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadData(1);
|
||||
particlesJS('particles-js', {
|
||||
"particles": {
|
||||
"number": { "value": 80, "density": { "enable": true, "value_area": 800 } },
|
||||
"color": { "value": "#007bff" },
|
||||
"shape": { "type": "circle", "stroke": { "width": 0, "color": "#000000" }, "polygon": { "nb_sides": 5 } },
|
||||
"opacity": { "value": 0.5, "random": false, "anim": { "enable": false, "speed": 1, "opacity_min": 0.1, "sync": false } },
|
||||
"size": { "value": 3, "random": true, "anim": { "enable": false, "speed": 40, "size_min": 0.1, "sync": false } },
|
||||
"line_linked": { "enable": true, "distance": 150, "color": "#007bff", "opacity": 0.4, "width": 1 },
|
||||
"move": { "enable": true, "speed": 6, "direction": "none", "random": false, "straight": false, "out_mode": "out", "bounce": false, "attract": { "enable": false, "rotateX": 600, "rotateY": 1200 } }
|
||||
},
|
||||
"interactivity": {
|
||||
"detect_on": "canvas",
|
||||
"events": { "onhover": { "enable": true, "mode": "repulse" }, "onclick": { "enable": true, "mode": "push" }, "resize": true },
|
||||
"modes": { "grab": { "distance": 400, "line_linked": { "opacity": 1 } }, "bubble": { "distance": 400, "size": 40, "duration": 2, "opacity": 8, "speed": 3 }, "repulse": { "distance": 200, "duration": 0.4 }, "push": { "particles_nb": 4 }, "remove": { "particles_nb": 2 } }
|
||||
},
|
||||
"retina_detect": true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user