feat: make zero download quota block downloads and use -1 for unlimited
This commit is contained in:
@@ -1426,17 +1426,27 @@ function migrateDownloadTrafficFields() {
|
||||
console.log('[数据库迁移] ✓ download_traffic_last_reset_at 字段已添加');
|
||||
}
|
||||
|
||||
// 统一策略:download_traffic_quota <= 0 表示不限流量
|
||||
// 统一策略:download_traffic_quota 为空时回填为 0(0 表示禁止下载,-1 表示不限流量)
|
||||
const quotaBackfillResult = db.prepare(`
|
||||
UPDATE users
|
||||
SET download_traffic_quota = 0
|
||||
WHERE download_traffic_quota IS NULL OR download_traffic_quota < 0
|
||||
WHERE download_traffic_quota IS NULL
|
||||
`).run();
|
||||
|
||||
if (quotaBackfillResult.changes > 0) {
|
||||
console.log(`[数据库迁移] ✓ 下载流量配额默认值已回填: ${quotaBackfillResult.changes} 条记录`);
|
||||
}
|
||||
|
||||
const quotaUnlimitedNormalizeResult = db.prepare(`
|
||||
UPDATE users
|
||||
SET download_traffic_quota = -1
|
||||
WHERE download_traffic_quota < 0
|
||||
`).run();
|
||||
|
||||
if (quotaUnlimitedNormalizeResult.changes > 0) {
|
||||
console.log(`[数据库迁移] ✓ 不限流量配额已标准化为 -1: ${quotaUnlimitedNormalizeResult.changes} 条记录`);
|
||||
}
|
||||
|
||||
const usedBackfillResult = db.prepare(`
|
||||
UPDATE users
|
||||
SET download_traffic_used = 0
|
||||
@@ -1450,7 +1460,7 @@ function migrateDownloadTrafficFields() {
|
||||
const usedCapResult = db.prepare(`
|
||||
UPDATE users
|
||||
SET download_traffic_used = download_traffic_quota
|
||||
WHERE download_traffic_quota > 0 AND download_traffic_used > download_traffic_quota
|
||||
WHERE download_traffic_quota >= 0 AND download_traffic_used > download_traffic_quota
|
||||
`).run();
|
||||
|
||||
if (usedCapResult.changes > 0) {
|
||||
@@ -1468,14 +1478,14 @@ function migrateDownloadTrafficFields() {
|
||||
console.log(`[数据库迁移] ✓ 下载流量重置周期已回填: ${resetCycleBackfillResult.changes} 条记录`);
|
||||
}
|
||||
|
||||
const clearExpiryForUnlimitedResult = db.prepare(`
|
||||
const clearExpiryForNonPositiveQuotaResult = db.prepare(`
|
||||
UPDATE users
|
||||
SET download_traffic_quota_expires_at = NULL
|
||||
WHERE download_traffic_quota <= 0
|
||||
`).run();
|
||||
|
||||
if (clearExpiryForUnlimitedResult.changes > 0) {
|
||||
console.log(`[数据库迁移] ✓ 不限流量用户已清理到期时间: ${clearExpiryForUnlimitedResult.changes} 条记录`);
|
||||
if (clearExpiryForNonPositiveQuotaResult.changes > 0) {
|
||||
console.log(`[数据库迁移] ✓ 非正下载配额用户已清理到期时间: ${clearExpiryForNonPositiveQuotaResult.changes} 条记录`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[数据库迁移] 下载流量字段迁移失败:', error);
|
||||
|
||||
@@ -650,8 +650,11 @@ function normalizeOssQuota(rawQuota) {
|
||||
|
||||
function normalizeDownloadTrafficQuota(rawQuota) {
|
||||
const parsedQuota = Number(rawQuota);
|
||||
if (!Number.isFinite(parsedQuota) || parsedQuota <= 0) {
|
||||
return 0; // 0 表示不限流量
|
||||
if (!Number.isFinite(parsedQuota)) {
|
||||
return 0; // 0 表示禁止下载
|
||||
}
|
||||
if (parsedQuota < 0) {
|
||||
return -1; // -1 表示不限流量
|
||||
}
|
||||
return Math.min(MAX_DOWNLOAD_TRAFFIC_BYTES, Math.floor(parsedQuota));
|
||||
}
|
||||
@@ -661,7 +664,7 @@ function normalizeDownloadTrafficUsed(rawUsed, quota = 0) {
|
||||
const normalizedUsed = Number.isFinite(parsedUsed) && parsedUsed > 0
|
||||
? Math.floor(parsedUsed)
|
||||
: 0;
|
||||
if (quota > 0) {
|
||||
if (quota >= 0) {
|
||||
return Math.min(normalizedUsed, quota);
|
||||
}
|
||||
return normalizedUsed;
|
||||
@@ -670,11 +673,12 @@ function normalizeDownloadTrafficUsed(rawUsed, quota = 0) {
|
||||
function getDownloadTrafficState(user) {
|
||||
const quota = normalizeDownloadTrafficQuota(user?.download_traffic_quota);
|
||||
const used = normalizeDownloadTrafficUsed(user?.download_traffic_used, quota);
|
||||
const isUnlimited = quota < 0;
|
||||
return {
|
||||
quota,
|
||||
used,
|
||||
isUnlimited: quota <= 0,
|
||||
remaining: quota > 0 ? Math.max(0, quota - used) : Number.POSITIVE_INFINITY
|
||||
isUnlimited,
|
||||
remaining: isUnlimited ? Number.POSITIVE_INFINITY : Math.max(0, quota - used)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3077,11 +3081,13 @@ app.get('/api/user/download-traffic-report', authMiddleware, (req, res) => {
|
||||
return peak;
|
||||
}, null);
|
||||
|
||||
const safeQuota = trafficState.quota > 0 ? trafficState.quota : 0;
|
||||
const remainingBytes = safeQuota > 0 ? Math.max(0, safeQuota - trafficState.used) : null;
|
||||
const usagePercentage = safeQuota > 0
|
||||
? Math.min(100, Math.round((trafficState.used / safeQuota) * 100))
|
||||
: null;
|
||||
const safeQuota = trafficState.isUnlimited ? 0 : Math.max(0, trafficState.quota);
|
||||
const remainingBytes = trafficState.isUnlimited ? null : Math.max(0, safeQuota - trafficState.used);
|
||||
const usagePercentage = trafficState.isUnlimited
|
||||
? null
|
||||
: (safeQuota > 0
|
||||
? Math.min(100, Math.round((trafficState.used / safeQuota) * 100))
|
||||
: 100);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
@@ -7602,7 +7608,7 @@ app.post('/api/admin/users/:id/storage-permission',
|
||||
body('storage_permission').isIn(['local_only', 'oss_only', 'user_choice']).withMessage('无效的存储权限'),
|
||||
body('local_storage_quota').optional({ nullable: true }).isInt({ min: 1048576, max: 10995116277760 }).withMessage('本地配额必须在 1MB 到 10TB 之间'),
|
||||
body('oss_storage_quota').optional({ nullable: true }).isInt({ min: 1048576, max: 10995116277760 }).withMessage('OSS配额必须在 1MB 到 10TB 之间'),
|
||||
body('download_traffic_quota').optional({ nullable: true }).isInt({ min: 0, max: MAX_DOWNLOAD_TRAFFIC_BYTES }).withMessage('下载流量配额必须在 0 到 10TB 之间(0表示不限)'),
|
||||
body('download_traffic_quota').optional({ nullable: true }).isInt({ min: -1, max: MAX_DOWNLOAD_TRAFFIC_BYTES }).withMessage('下载流量配额必须在 -1 到 10TB 之间(-1表示不限,0表示禁止下载)'),
|
||||
body('download_traffic_delta').optional({ nullable: true }).isInt({ min: -MAX_DOWNLOAD_TRAFFIC_BYTES, max: MAX_DOWNLOAD_TRAFFIC_BYTES }).withMessage('下载流量增减值必须在 -10TB 到 10TB 之间'),
|
||||
body('download_traffic_reset_cycle').optional({ nullable: true }).isIn(['none', 'daily', 'weekly', 'monthly']).withMessage('下载流量重置周期无效'),
|
||||
body('download_traffic_quota_expires_at').optional({ nullable: true }).custom((value) => {
|
||||
|
||||
Reference in New Issue
Block a user