feat: enhance download traffic quota lifecycle controls
This commit is contained in:
@@ -501,6 +501,24 @@ const UserDB = {
|
||||
* @private
|
||||
*/
|
||||
_validateFieldValue(fieldName, value) {
|
||||
const NULLABLE_STRING_FIELDS = new Set([
|
||||
'oss_provider',
|
||||
'oss_region',
|
||||
'oss_access_key_id',
|
||||
'oss_access_key_secret',
|
||||
'oss_bucket',
|
||||
'oss_endpoint',
|
||||
'upload_api_key',
|
||||
'verification_token',
|
||||
'verification_expires_at',
|
||||
'storage_permission',
|
||||
'current_storage_type',
|
||||
'download_traffic_quota_expires_at',
|
||||
'download_traffic_reset_cycle',
|
||||
'download_traffic_last_reset_at',
|
||||
'theme_preference'
|
||||
]);
|
||||
|
||||
// 字段类型白名单(根据数据库表结构定义)
|
||||
const FIELD_TYPES = {
|
||||
// 文本类型字段
|
||||
@@ -518,6 +536,9 @@ const UserDB = {
|
||||
'verification_expires_at': 'string',
|
||||
'storage_permission': 'string',
|
||||
'current_storage_type': 'string',
|
||||
'download_traffic_quota_expires_at': 'string',
|
||||
'download_traffic_reset_cycle': 'string',
|
||||
'download_traffic_last_reset_at': 'string',
|
||||
'theme_preference': 'string',
|
||||
|
||||
// 数值类型字段
|
||||
@@ -542,6 +563,9 @@ const UserDB = {
|
||||
|
||||
// 检查类型匹配
|
||||
if (expectedType === 'string') {
|
||||
if (value === null) {
|
||||
return NULLABLE_STRING_FIELDS.has(fieldName);
|
||||
}
|
||||
return typeof value === 'string';
|
||||
} else if (expectedType === 'number') {
|
||||
// 允许数值或可转换为数值的字符串
|
||||
@@ -593,6 +617,9 @@ const UserDB = {
|
||||
'oss_storage_quota': 'oss_storage_quota',
|
||||
'download_traffic_quota': 'download_traffic_quota',
|
||||
'download_traffic_used': 'download_traffic_used',
|
||||
'download_traffic_quota_expires_at': 'download_traffic_quota_expires_at',
|
||||
'download_traffic_reset_cycle': 'download_traffic_reset_cycle',
|
||||
'download_traffic_last_reset_at': 'download_traffic_last_reset_at',
|
||||
|
||||
// 偏好设置
|
||||
'theme_preference': 'theme_preference'
|
||||
@@ -676,6 +703,9 @@ const UserDB = {
|
||||
'oss_storage_quota': 'oss_storage_quota',
|
||||
'download_traffic_quota': 'download_traffic_quota',
|
||||
'download_traffic_used': 'download_traffic_used',
|
||||
'download_traffic_quota_expires_at': 'download_traffic_quota_expires_at',
|
||||
'download_traffic_reset_cycle': 'download_traffic_reset_cycle',
|
||||
'download_traffic_last_reset_at': 'download_traffic_last_reset_at',
|
||||
|
||||
// 偏好设置
|
||||
'theme_preference': 'theme_preference'
|
||||
@@ -726,6 +756,9 @@ const UserDB = {
|
||||
'upload_api_key': 'string', 'verification_token': 'string',
|
||||
'verification_expires_at': 'string', 'storage_permission': 'string',
|
||||
'current_storage_type': 'string', 'theme_preference': 'string',
|
||||
'download_traffic_quota_expires_at': 'string',
|
||||
'download_traffic_reset_cycle': 'string',
|
||||
'download_traffic_last_reset_at': 'string',
|
||||
'is_admin': 'number', 'is_active': 'number', 'is_banned': 'number',
|
||||
'has_oss_config': 'number', 'is_verified': 'number',
|
||||
'local_storage_quota': 'number', 'local_storage_used': 'number',
|
||||
@@ -1298,6 +1331,9 @@ function migrateDownloadTrafficFields() {
|
||||
const columns = db.prepare("PRAGMA table_info(users)").all();
|
||||
const hasDownloadTrafficQuota = columns.some(col => col.name === 'download_traffic_quota');
|
||||
const hasDownloadTrafficUsed = columns.some(col => col.name === 'download_traffic_used');
|
||||
const hasDownloadTrafficQuotaExpiresAt = columns.some(col => col.name === 'download_traffic_quota_expires_at');
|
||||
const hasDownloadTrafficResetCycle = columns.some(col => col.name === 'download_traffic_reset_cycle');
|
||||
const hasDownloadTrafficLastResetAt = columns.some(col => col.name === 'download_traffic_last_reset_at');
|
||||
|
||||
if (!hasDownloadTrafficQuota) {
|
||||
console.log('[数据库迁移] 添加 download_traffic_quota 字段...');
|
||||
@@ -1311,6 +1347,24 @@ function migrateDownloadTrafficFields() {
|
||||
console.log('[数据库迁移] ✓ download_traffic_used 字段已添加');
|
||||
}
|
||||
|
||||
if (!hasDownloadTrafficQuotaExpiresAt) {
|
||||
console.log('[数据库迁移] 添加 download_traffic_quota_expires_at 字段...');
|
||||
db.exec('ALTER TABLE users ADD COLUMN download_traffic_quota_expires_at DATETIME DEFAULT NULL');
|
||||
console.log('[数据库迁移] ✓ download_traffic_quota_expires_at 字段已添加');
|
||||
}
|
||||
|
||||
if (!hasDownloadTrafficResetCycle) {
|
||||
console.log('[数据库迁移] 添加 download_traffic_reset_cycle 字段...');
|
||||
db.exec("ALTER TABLE users ADD COLUMN download_traffic_reset_cycle TEXT DEFAULT 'none'");
|
||||
console.log('[数据库迁移] ✓ download_traffic_reset_cycle 字段已添加');
|
||||
}
|
||||
|
||||
if (!hasDownloadTrafficLastResetAt) {
|
||||
console.log('[数据库迁移] 添加 download_traffic_last_reset_at 字段...');
|
||||
db.exec('ALTER TABLE users ADD COLUMN download_traffic_last_reset_at DATETIME DEFAULT NULL');
|
||||
console.log('[数据库迁移] ✓ download_traffic_last_reset_at 字段已添加');
|
||||
}
|
||||
|
||||
// 统一策略:download_traffic_quota <= 0 表示不限流量
|
||||
const quotaBackfillResult = db.prepare(`
|
||||
UPDATE users
|
||||
@@ -1341,6 +1395,27 @@ function migrateDownloadTrafficFields() {
|
||||
if (usedCapResult.changes > 0) {
|
||||
console.log(`[数据库迁移] ✓ 下载流量已用值已按配额校准: ${usedCapResult.changes} 条记录`);
|
||||
}
|
||||
|
||||
const resetCycleBackfillResult = db.prepare(`
|
||||
UPDATE users
|
||||
SET download_traffic_reset_cycle = 'none'
|
||||
WHERE download_traffic_reset_cycle IS NULL
|
||||
OR download_traffic_reset_cycle NOT IN ('none', 'daily', 'weekly', 'monthly')
|
||||
`).run();
|
||||
|
||||
if (resetCycleBackfillResult.changes > 0) {
|
||||
console.log(`[数据库迁移] ✓ 下载流量重置周期已回填: ${resetCycleBackfillResult.changes} 条记录`);
|
||||
}
|
||||
|
||||
const clearExpiryForUnlimitedResult = 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} 条记录`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[数据库迁移] 下载流量字段迁移失败:', error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user