feat: add server-side admin user pagination and align traffic report accounting

This commit is contained in:
2026-02-17 20:30:02 +08:00
parent 1eae645bfd
commit aed5dfdcb2
4 changed files with 378 additions and 154 deletions

View File

@@ -1013,13 +1013,16 @@ const applyConfirmedDownloadTrafficFromLogTransaction = db.transaction((userId,
: Math.min(trafficState.quota, trafficState.used + bytes);
UserDB.update(userId, { download_traffic_used: nextUsed });
DownloadTrafficReportDB.addUsage(userId, bytes, count > 0 ? count : 1, eventDate);
const addedBytes = Math.max(0, nextUsed - trafficState.used);
if (addedBytes > 0) {
DownloadTrafficReportDB.addUsage(userId, addedBytes, count > 0 ? count : 1, eventDate);
}
const consumeResult = DownloadTrafficReservationDB.consumePendingBytes(userId, bytes);
return {
userId,
confirmed: bytes,
added: Math.max(0, nextUsed - trafficState.used),
added: addedBytes,
usedBefore: trafficState.used,
usedAfter: nextUsed,
consumedReserved: Number(consumeResult?.consumed || 0),
@@ -7054,39 +7057,59 @@ app.get('/api/admin/storage-stats', authMiddleware, adminMiddleware, async (req,
// 获取所有用户
app.get('/api/admin/users', authMiddleware, adminMiddleware, (req, res) => {
try {
const users = UserDB.getAll().map(user => {
const toAdminUserPayload = (u) => ({
id: u.id,
username: u.username,
email: u.email,
is_admin: u.is_admin,
is_active: u.is_active,
is_verified: u.is_verified,
is_banned: u.is_banned,
has_oss_config: u.has_oss_config,
created_at: u.created_at,
storage_permission: u.storage_permission || 'oss_only',
current_storage_type: u.current_storage_type || 'oss',
local_storage_quota: u.local_storage_quota || DEFAULT_LOCAL_STORAGE_QUOTA_BYTES,
local_storage_used: u.local_storage_used || 0,
oss_storage_quota: normalizeOssQuota(u.oss_storage_quota),
storage_used: u.storage_used || 0,
download_traffic_quota: normalizeDownloadTrafficQuota(u.download_traffic_quota),
download_traffic_used: normalizeDownloadTrafficUsed(
u.download_traffic_used,
normalizeDownloadTrafficQuota(u.download_traffic_quota)
),
download_traffic_quota_expires_at: u.download_traffic_quota_expires_at || null,
download_traffic_reset_cycle: u.download_traffic_reset_cycle || 'none',
download_traffic_last_reset_at: u.download_traffic_last_reset_at || null
});
const hasPagedQuery = req.query?.paged === '1'
|| ['page', 'pageSize', 'keyword', 'role', 'status', 'storage', 'sort']
.some((key) => req.query && req.query[key] !== undefined);
if (hasPagedQuery) {
const queryResult = UserDB.queryAdminUsers(req.query || {});
const users = queryResult.rows.map((user) => {
const policyState = enforceDownloadTrafficPolicy(user.id, 'admin_list_page');
return policyState?.user || user;
});
return res.json({
success: true,
users: users.map(toAdminUserPayload),
pagination: queryResult.pagination,
summary: queryResult.summary
});
}
const users = UserDB.getAll().map((user) => {
const policyState = enforceDownloadTrafficPolicy(user.id, 'admin_list');
return policyState?.user || user;
});
res.json({
success: true,
users: users.map(u => ({
id: u.id,
username: u.username,
email: u.email,
is_admin: u.is_admin,
is_active: u.is_active,
is_verified: u.is_verified,
is_banned: u.is_banned,
has_oss_config: u.has_oss_config,
created_at: u.created_at,
// 新增:存储相关字段
storage_permission: u.storage_permission || 'oss_only',
current_storage_type: u.current_storage_type || 'oss',
local_storage_quota: u.local_storage_quota || DEFAULT_LOCAL_STORAGE_QUOTA_BYTES,
local_storage_used: u.local_storage_used || 0,
oss_storage_quota: normalizeOssQuota(u.oss_storage_quota),
storage_used: u.storage_used || 0,
download_traffic_quota: normalizeDownloadTrafficQuota(u.download_traffic_quota),
download_traffic_used: normalizeDownloadTrafficUsed(
u.download_traffic_used,
normalizeDownloadTrafficQuota(u.download_traffic_quota)
),
download_traffic_quota_expires_at: u.download_traffic_quota_expires_at || null,
download_traffic_reset_cycle: u.download_traffic_reset_cycle || 'none',
download_traffic_last_reset_at: u.download_traffic_last_reset_at || null
}))
users: users.map(toAdminUserPayload)
});
} catch (error) {
console.error('获取用户列表失败:', error);