Add refund received timestamp column, record time when marking refund
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
23
database.py
23
database.py
@@ -106,18 +106,20 @@ class DatabaseManager:
|
|||||||
proxy_share TEXT DEFAULT 'exclusive',
|
proxy_share TEXT DEFAULT 'exclusive',
|
||||||
proxy_purchase_date TEXT DEFAULT '',
|
proxy_purchase_date TEXT DEFAULT '',
|
||||||
refund_received TEXT DEFAULT '0',
|
refund_received TEXT DEFAULT '0',
|
||||||
|
refund_received_at TEXT DEFAULT '',
|
||||||
checked_at TEXT
|
checked_at TEXT
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
# 兼容旧表:动态添加缺少的列
|
# 兼容旧表:动态添加缺少的列
|
||||||
for col in ['suspended_time', 'title', 'remark', 'card_number', 'proxy', 'proxy_expire_days', 'proxy_share', 'proxy_purchase_date', 'refund_received']:
|
for col in ['suspended_time', 'title', 'remark', 'card_number', 'proxy', 'proxy_expire_days', 'proxy_share', 'proxy_purchase_date', 'refund_received', 'refund_received_at']:
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"SELECT {col} FROM claude_payment_status LIMIT 1")
|
cursor.execute(f"SELECT {col} FROM claude_payment_status LIMIT 1")
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
defaults = {'title': "DEFAULT ''", 'remark': "DEFAULT ''", 'card_number': "DEFAULT ''", 'proxy': "DEFAULT ''",
|
defaults = {'title': "DEFAULT ''", 'remark': "DEFAULT ''", 'card_number': "DEFAULT ''", 'proxy': "DEFAULT ''",
|
||||||
'proxy_expire_days': "DEFAULT 30", 'proxy_share': "DEFAULT 'exclusive'", 'proxy_purchase_date': "DEFAULT ''",
|
'proxy_expire_days': "DEFAULT 30", 'proxy_share': "DEFAULT 'exclusive'", 'proxy_purchase_date': "DEFAULT ''",
|
||||||
'refund_received': "DEFAULT '0'"}
|
'refund_received': "DEFAULT '0'",
|
||||||
|
'refund_received_at': "DEFAULT ''"}
|
||||||
default = defaults.get(col, '')
|
default = defaults.get(col, '')
|
||||||
cursor.execute(f"ALTER TABLE claude_payment_status ADD COLUMN {col} TEXT {default}")
|
cursor.execute(f"ALTER TABLE claude_payment_status ADD COLUMN {col} TEXT {default}")
|
||||||
logger.info(f"已为 claude_payment_status 添加 {col} 列")
|
logger.info(f"已为 claude_payment_status 添加 {col} 列")
|
||||||
@@ -527,7 +529,7 @@ class DatabaseManager:
|
|||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
checked_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
checked_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
# 先查已有的 remark/card_number,避免被覆盖
|
# 先查已有的 remark/card_number,避免被覆盖
|
||||||
cursor.execute('SELECT title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received FROM claude_payment_status WHERE email = ?', (email,))
|
cursor.execute('SELECT title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received, refund_received_at FROM claude_payment_status WHERE email = ?', (email,))
|
||||||
existing = cursor.fetchone()
|
existing = cursor.fetchone()
|
||||||
old_title = existing['title'] if existing else ''
|
old_title = existing['title'] if existing else ''
|
||||||
old_remark = existing['remark'] if existing else ''
|
old_remark = existing['remark'] if existing else ''
|
||||||
@@ -537,10 +539,11 @@ class DatabaseManager:
|
|||||||
old_share = existing['proxy_share'] if existing else 'exclusive'
|
old_share = existing['proxy_share'] if existing else 'exclusive'
|
||||||
old_purchase = existing['proxy_purchase_date'] if existing else ''
|
old_purchase = existing['proxy_purchase_date'] if existing else ''
|
||||||
old_refund_received = existing['refund_received'] if existing else '0'
|
old_refund_received = existing['refund_received'] if existing else '0'
|
||||||
|
old_refund_received_at = existing['refund_received_at'] if existing else ''
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
INSERT OR REPLACE INTO claude_payment_status (email, status, payment_time, refund_time, suspended_time, title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received, checked_at)
|
INSERT OR REPLACE INTO claude_payment_status (email, status, payment_time, refund_time, suspended_time, title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received, refund_received_at, checked_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
''', (email, status, payment_time, refund_time, suspended_time, old_title or '', old_remark or '', old_card or '', old_proxy or '', old_expire or 30, old_share or 'exclusive', old_purchase or '', old_refund_received or '0', checked_at))
|
''', (email, status, payment_time, refund_time, suspended_time, old_title or '', old_remark or '', old_card or '', old_proxy or '', old_expire or 30, old_share or 'exclusive', old_purchase or '', old_refund_received or '0', old_refund_received_at or '', checked_at))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -554,7 +557,7 @@ class DatabaseManager:
|
|||||||
def _sync_get():
|
def _sync_get():
|
||||||
conn = self.get_connection()
|
conn = self.get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('SELECT status, payment_time, refund_time, suspended_time, title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received, checked_at FROM claude_payment_status WHERE email = ?', (email,))
|
cursor.execute('SELECT status, payment_time, refund_time, suspended_time, title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received, refund_received_at, checked_at FROM claude_payment_status WHERE email = ?', (email,))
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
if row:
|
if row:
|
||||||
return {
|
return {
|
||||||
@@ -570,6 +573,7 @@ class DatabaseManager:
|
|||||||
'proxy_share': row['proxy_share'] or 'exclusive',
|
'proxy_share': row['proxy_share'] or 'exclusive',
|
||||||
'proxy_purchase_date': row['proxy_purchase_date'] or '',
|
'proxy_purchase_date': row['proxy_purchase_date'] or '',
|
||||||
'refund_received': row['refund_received'] or '0',
|
'refund_received': row['refund_received'] or '0',
|
||||||
|
'refund_received_at': row['refund_received_at'] or '',
|
||||||
'checked_at': row['checked_at']
|
'checked_at': row['checked_at']
|
||||||
}
|
}
|
||||||
return None
|
return None
|
||||||
@@ -581,7 +585,7 @@ class DatabaseManager:
|
|||||||
def _sync_get():
|
def _sync_get():
|
||||||
conn = self.get_connection()
|
conn = self.get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('SELECT email, status, payment_time, refund_time, suspended_time, title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received, checked_at FROM claude_payment_status')
|
cursor.execute('SELECT email, status, payment_time, refund_time, suspended_time, title, remark, card_number, proxy, proxy_expire_days, proxy_share, proxy_purchase_date, refund_received, refund_received_at, checked_at FROM claude_payment_status')
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
result = {}
|
result = {}
|
||||||
for row in rows:
|
for row in rows:
|
||||||
@@ -598,6 +602,7 @@ class DatabaseManager:
|
|||||||
'proxy_share': row['proxy_share'] or 'exclusive',
|
'proxy_share': row['proxy_share'] or 'exclusive',
|
||||||
'proxy_purchase_date': row['proxy_purchase_date'] or '',
|
'proxy_purchase_date': row['proxy_purchase_date'] or '',
|
||||||
'refund_received': row['refund_received'] or '0',
|
'refund_received': row['refund_received'] or '0',
|
||||||
|
'refund_received_at': row['refund_received_at'] or '',
|
||||||
'checked_at': row['checked_at']
|
'checked_at': row['checked_at']
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -606,7 +611,7 @@ class DatabaseManager:
|
|||||||
|
|
||||||
async def update_claude_payment_note(self, email: str, **kwargs) -> bool:
|
async def update_claude_payment_note(self, email: str, **kwargs) -> bool:
|
||||||
"""更新备注、卡号、代理等字段"""
|
"""更新备注、卡号、代理等字段"""
|
||||||
allowed = {'title', 'remark', 'card_number', 'proxy', 'proxy_expire_days', 'proxy_share', 'proxy_purchase_date', 'refund_received'}
|
allowed = {'title', 'remark', 'card_number', 'proxy', 'proxy_expire_days', 'proxy_share', 'proxy_purchase_date', 'refund_received', 'refund_received_at'}
|
||||||
def _sync_update():
|
def _sync_update():
|
||||||
try:
|
try:
|
||||||
conn = self.get_connection()
|
conn = self.get_connection()
|
||||||
|
|||||||
@@ -1278,10 +1278,11 @@ async def toggle_refund_received(email: str) -> ApiResponse:
|
|||||||
status = await db_manager.get_claude_payment_status(email)
|
status = await db_manager.get_claude_payment_status(email)
|
||||||
current = status.get('refund_received', '0') if status else '0'
|
current = status.get('refund_received', '0') if status else '0'
|
||||||
new_val = '0' if current == '1' else '1'
|
new_val = '0' if current == '1' else '1'
|
||||||
ok = await db_manager.update_claude_payment_note(email, refund_received=new_val)
|
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') if new_val == '1' else ''
|
||||||
|
ok = await db_manager.update_claude_payment_note(email, refund_received=new_val, refund_received_at=now)
|
||||||
if ok:
|
if ok:
|
||||||
await cache.invalidate_payment()
|
await cache.invalidate_payment()
|
||||||
return ApiResponse(success=True, data={"refund_received": new_val})
|
return ApiResponse(success=True, data={"refund_received": new_val, "refund_received_at": now})
|
||||||
return ApiResponse(success=False, message="更新失败")
|
return ApiResponse(success=False, message="更新失败")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"切换退款状态失败: {e}")
|
logger.error(f"切换退款状态失败: {e}")
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
<th>支付时间</th>
|
<th>支付时间</th>
|
||||||
<th>退款时间</th>
|
<th>退款时间</th>
|
||||||
<th>封号时间</th>
|
<th>封号时间</th>
|
||||||
|
<th>到账时间</th>
|
||||||
<th>备注/卡号</th>
|
<th>备注/卡号</th>
|
||||||
<th>代理</th>
|
<th>代理</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
@@ -105,7 +106,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody id="accountTableBody">
|
<tbody id="accountTableBody">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="12">
|
<td colspan="13">
|
||||||
<div class="no-data">
|
<div class="no-data">
|
||||||
<i class="bi bi-inbox"></i>
|
<i class="bi bi-inbox"></i>
|
||||||
<div>暂无邮箱数据,点击"粘贴导入"添加</div>
|
<div>暂无邮箱数据,点击"粘贴导入"添加</div>
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ class MailManager {
|
|||||||
|
|
||||||
async loadAccounts() {
|
async loadAccounts() {
|
||||||
const tbody = document.getElementById('accountTableBody');
|
const tbody = document.getElementById('accountTableBody');
|
||||||
tbody.innerHTML = `<tr><td colspan="12"><div class="table-loading"><div class="spinner-border spinner-border-sm"></div> 加载中...</div></td></tr>`;
|
tbody.innerHTML = `<tr><td colspan="13"><div class="table-loading"><div class="spinner-border spinner-border-sm"></div> 加载中...</div></td></tr>`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 有筛选时加载全部数据,否则分页加载
|
// 有筛选时加载全部数据,否则分页加载
|
||||||
@@ -300,11 +300,11 @@ class MailManager {
|
|||||||
await this.loadClaudePaymentStatuses();
|
await this.loadClaudePaymentStatuses();
|
||||||
this.applyFilterAndRender();
|
this.applyFilterAndRender();
|
||||||
} else {
|
} else {
|
||||||
tbody.innerHTML = `<tr><td colspan="12"><div class="no-data"><i class="bi bi-exclamation-circle"></i><div>${this.escapeHtml(result.message || '加载失败')}</div></div></td></tr>`;
|
tbody.innerHTML = `<tr><td colspan="13"><div class="no-data"><i class="bi bi-exclamation-circle"></i><div>${this.escapeHtml(result.message || '加载失败')}</div></div></td></tr>`;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载账号失败:', err);
|
console.error('加载账号失败:', err);
|
||||||
tbody.innerHTML = `<tr><td colspan="12"><div class="no-data"><i class="bi bi-wifi-off"></i><div>网络错误</div></div></td></tr>`;
|
tbody.innerHTML = `<tr><td colspan="13"><div class="no-data"><i class="bi bi-wifi-off"></i><div>网络错误</div></div></td></tr>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +339,7 @@ class MailManager {
|
|||||||
renderAccounts() {
|
renderAccounts() {
|
||||||
const tbody = document.getElementById('accountTableBody');
|
const tbody = document.getElementById('accountTableBody');
|
||||||
if (!this.accounts.length) {
|
if (!this.accounts.length) {
|
||||||
tbody.innerHTML = `<tr><td colspan="12"><div class="no-data"><i class="bi bi-inbox"></i><div>暂无邮箱数据</div></div></td></tr>`;
|
tbody.innerHTML = `<tr><td colspan="13"><div class="no-data"><i class="bi bi-inbox"></i><div>暂无邮箱数据</div></div></td></tr>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,9 +777,9 @@ class MailManager {
|
|||||||
const resp = await fetch(`/api/tools/refund-received/${encodeURIComponent(email)}`, { method: 'POST' });
|
const resp = await fetch(`/api/tools/refund-received/${encodeURIComponent(email)}`, { method: 'POST' });
|
||||||
const result = await resp.json();
|
const result = await resp.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// 更新本地状态
|
|
||||||
if (this.claudePaymentStatuses[email]) {
|
if (this.claudePaymentStatuses[email]) {
|
||||||
this.claudePaymentStatuses[email].refund_received = result.data.refund_received;
|
this.claudePaymentStatuses[email].refund_received = result.data.refund_received;
|
||||||
|
this.claudePaymentStatuses[email].refund_received_at = result.data.refund_received_at || '';
|
||||||
}
|
}
|
||||||
this.renderAccounts();
|
this.renderAccounts();
|
||||||
this.showToast(result.data.refund_received === '1' ? '已标记到账' : '已取消到账', 'success');
|
this.showToast(result.data.refund_received === '1' ? '已标记到账' : '已取消到账', 'success');
|
||||||
@@ -809,6 +809,7 @@ class MailManager {
|
|||||||
<td class="claude-time">-</td>
|
<td class="claude-time">-</td>
|
||||||
<td class="claude-time">-</td>
|
<td class="claude-time">-</td>
|
||||||
<td class="claude-time">-</td>
|
<td class="claude-time">-</td>
|
||||||
|
<td class="claude-time">-</td>
|
||||||
<td><button class="note-cell-btn" data-note-email="${this.escapeHtml(email)}"><i class="bi bi-pencil"></i> 编辑</button></td>
|
<td><button class="note-cell-btn" data-note-email="${this.escapeHtml(email)}"><i class="bi bi-pencil"></i> 编辑</button></td>
|
||||||
<td><button class="proxy-btn" data-email="${this.escapeHtml(email)}"><i class="bi bi-globe"></i> 设置</button></td>`;
|
<td><button class="proxy-btn" data-email="${this.escapeHtml(email)}"><i class="bi bi-globe"></i> 设置</button></td>`;
|
||||||
|
|
||||||
@@ -843,11 +844,16 @@ class MailManager {
|
|||||||
? `<span class="claude-badge claude-suspended">${fmtTime(info.suspended_time)}</span>`
|
? `<span class="claude-badge claude-suspended">${fmtTime(info.suspended_time)}</span>`
|
||||||
: '-';
|
: '-';
|
||||||
|
|
||||||
|
const refundReceivedTime = info.refund_received === '1' && info.refund_received_at
|
||||||
|
? fmtTime(info.refund_received_at)
|
||||||
|
: '-';
|
||||||
|
|
||||||
return `<td>${paidBadge}</td>
|
return `<td>${paidBadge}</td>
|
||||||
<td>${refundBadge}</td>
|
<td>${refundBadge}</td>
|
||||||
<td class="claude-time">${fmtTime(info.payment_time)}</td>
|
<td class="claude-time">${fmtTime(info.payment_time)}</td>
|
||||||
<td class="claude-time">${fmtTime(info.refund_time)}</td>
|
<td class="claude-time">${fmtTime(info.refund_time)}</td>
|
||||||
<td>${suspendedHtml}</td>
|
<td>${suspendedHtml}</td>
|
||||||
|
<td class="claude-time">${refundReceivedTime}</td>
|
||||||
<td>${this.renderNoteCell(email, info)}</td>
|
<td>${this.renderNoteCell(email, info)}</td>
|
||||||
<td class="proxy-cell">
|
<td class="proxy-cell">
|
||||||
<div class="proxy-cell-inner">
|
<div class="proxy-cell-inner">
|
||||||
|
|||||||
@@ -445,9 +445,10 @@ body {
|
|||||||
.data-table th:nth-child(7) { width: 85px; } /* 支付时间 */
|
.data-table th:nth-child(7) { width: 85px; } /* 支付时间 */
|
||||||
.data-table th:nth-child(8) { width: 85px; } /* 退款时间 */
|
.data-table th:nth-child(8) { width: 85px; } /* 退款时间 */
|
||||||
.data-table th:nth-child(9) { width: 85px; } /* 封号时间 */
|
.data-table th:nth-child(9) { width: 85px; } /* 封号时间 */
|
||||||
.data-table th:nth-child(10) { width: 120px; } /* 备注/卡号 */
|
.data-table th:nth-child(10) { width: 85px; } /* 到账时间 */
|
||||||
.data-table th:nth-child(11) { width: 150px; } /* 代理 */
|
.data-table th:nth-child(11) { width: 120px; } /* 备注/卡号 */
|
||||||
.data-table th:nth-child(12) { width: 220px; } /* 操作 */
|
.data-table th:nth-child(12) { width: 150px; } /* 代理 */
|
||||||
|
.data-table th:nth-child(13) { width: 220px; } /* 操作 */
|
||||||
|
|
||||||
/* 单元格内容 */
|
/* 单元格内容 */
|
||||||
.value-container {
|
.value-container {
|
||||||
|
|||||||
Reference in New Issue
Block a user