diff --git a/VERSION b/VERSION index 0848465..0ca6e94 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.31 +1.2.35 diff --git a/backend/app/routers/information.py b/backend/app/routers/information.py index b129c5d..7fb9484 100644 --- a/backend/app/routers/information.py +++ b/backend/app/routers/information.py @@ -454,6 +454,10 @@ def get_seek_match( if not info: raise HTTPException(status_code=404, detail="寻配号信息不存在") + # 更新用户配号(寻号)次数 + current_user.f99_96_search_count = (current_user.f99_96_search_count or 0) + 1 + db.commit() + # 获取用户所有藏品 collections = db.query(Collection).filter( Collection.f99_91_user_id == current_user.f99_90_id, diff --git a/backend/app/routers/ocr.py b/backend/app/routers/ocr.py index 29f8546..e9dffc1 100644 --- a/backend/app/routers/ocr.py +++ b/backend/app/routers/ocr.py @@ -113,6 +113,9 @@ async def recognize_image( # 生成OSS临时路径: temp/{year}/{month}/{day}/{uuid}.{ext} oss_key, temp_id = get_oss_path("temp", filename=image.filename) + # 初始化temp_path为空 + temp_path = None + # 上传到OSS try: image_url = upload_to_oss(image_data, oss_key) @@ -159,7 +162,7 @@ async def recognize_image( if response.status_code != 200: # 识别失败,删除临时文件 - if os.path.exists(temp_path): + if temp_path and os.path.exists(temp_path): os.remove(temp_path) raise HTTPException(status_code=500, detail=f"OCR API 调用失败:{response.text[:200]}") diff --git a/backend/app/services/sms.py b/backend/app/services/sms.py index d6953ca..f067142 100644 --- a/backend/app/services/sms.py +++ b/backend/app/services/sms.py @@ -8,10 +8,10 @@ from typing import Optional # 阿里云短信配置 SMS_CONFIG = { - "access_key_id": os.getenv("SMS_ACCESS_KEY_ID", "LTAI5t6HUnpFBLEK9194kPVG"), - "access_key_secret": os.getenv("SMS_ACCESS_KEY_SECRET", "LEr4Q8yRxb8D5b24cKfCwlt4MMoke1"), - "sign_name": "苏州算力", - "template_code": "SMS_501590956", + "access_key_id": os.getenv("SMS_ACCESS_KEY_ID", "LTAI5tQAx5niD7JQVqGE5acE"), + "access_key_secret": os.getenv("SMS_ACCESS_KEY_SECRET", "QsQFAEKBkaNynIoKyvdIi3BUyWVZu1"), + "sign_name": os.getenv("SMS_SIGN_NAME", "苏州算力"), + "template_code": os.getenv("SMS_TEMPLATE_CODE", "SMS_501590956"), } # 验证码缓存(生产环境建议用Redis) diff --git a/frontend/index.html b/frontend/index.html index 723723e..0ab0abb 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - 甲辰收藏 v1.2.30 + 甲辰收藏 v1.2.35 diff --git a/frontend/src/pages/Admin.jsx b/frontend/src/pages/Admin.jsx index 52a9fd9..56ed116 100644 --- a/frontend/src/pages/Admin.jsx +++ b/frontend/src/pages/Admin.jsx @@ -126,7 +126,16 @@ export default function Admin() { const updateData = { username: editingUser.username, email: editingUser.email, - role: editingUser.role + role: editingUser.role, + user_code: editingUser.user_code, + level: editingUser.level, + loginCount: editingUser.loginCount, + aiCount: editingUser.aiCount, + searchCount: editingUser.searchCount, + points: editingUser.points, + balance: editingUser.balance, + totalAmount: editingUser.totalAmount, + phoneVerified: editingUser.phoneVerified } // 如果有新密码,添加到更新数据 @@ -186,9 +195,9 @@ export default function Admin() {
{users.map((user) => (
-
+
-
+
{user.username}
{user.role === 'admin' ? '👑 管理员' : (user.role === 'editor' ? '📝 信息员' : '👤 用户')}
+
+ 编码: {user.user_code || '-'} +
+
+ 等级: {user.level || '青铜'} +
-
- 📧 {user.email || '未设置'} | 📱 {user.phone || '未设置'} +
+ 📧 {user.email || '未设置'} | 📱 {user.phone || '未设置'} | 📱已认证: {user.phoneVerified ? '✓' : '✗'}
@@ -208,6 +223,30 @@ export default function Admin() {
window.location.hash = '#/list?userId=' + user.id} title='点击查看'>{user.collectionCount || 0}
+ {/* 更多字段 */} +
+
+ 🔑 登录: {user.loginCount || 0}次 +
+
+ 🤖 AI识别: {user.aiCount || 0}次 +
+
+ 🎯 配号: {user.searchCount || 0}次 +
+
+ 🔍 寻号: {user.searchCount || 0}次 +
+
+ 💎 积分: {user.points || 0} +
+
+ 💰 余额: {user.balance || 0} +
+
+ 📊 累计: {user.totalAmount || 0} +
+
注册时间:{user.created_at ? new Date(user.created_at).toLocaleDateString('zh-CN') : '-'} @@ -392,6 +431,122 @@ export default function Admin() { />
+ + {/* 第三部分:扩展信息 */} +
+

📊 扩展信息

+ +
+
+ + setEditingUser({ ...editingUser, user_code: e.target.value })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+ +
+ + +
+ +
+ + setEditingUser({ ...editingUser, loginCount: parseInt(e.target.value) || 0 })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, aiCount: parseInt(e.target.value) || 0 })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, searchCount: parseInt(e.target.value) || 0 })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, searchCount: parseInt(e.target.value) || 0 })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, points: parseInt(e.target.value) || 0 })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, balance: parseFloat(e.target.value) || 0 })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, totalAmount: parseFloat(e.target.value) || 0 })} + style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} + /> +
+
+ +
+ +
+
- +
))} @@ -144,8 +144,8 @@ function UserManagement({ token, API_BASE }) {

编辑用户

setEditingUser({...editingUser, phone: e.target.value})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px', boxSizing: 'border-box' }} /> - setEditingUser({...editingUser, f99_100_points: parseInt(e.target.value)})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px', boxSizing: 'border-box' }} /> - setEditingUser({...editingUser, f01_11_balance: parseFloat(e.target.value)})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '12px', boxSizing: 'border-box' }} /> + setEditingUser({...editingUser, points: parseInt(e.target.value)})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px', boxSizing: 'border-box' }} /> + setEditingUser({...editingUser, balance: parseFloat(e.target.value)})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '12px', boxSizing: 'border-box' }} />
@@ -173,9 +173,9 @@ function Statistics({ token, API_BASE }) { const totalUsers = Array.isArray(usersData) ? usersData.length : 0 const totalCollections = collectionsData.total || 0 const levelCount = {} - if (Array.isArray(usersData)) { usersData.forEach(u => { const level = u.f99_94_level || '青铜'; levelCount[level] = (levelCount[level] || 0) + 1 }) } + if (Array.isArray(usersData)) { usersData.forEach(u => { const level = u.level || '青铜'; levelCount[level] = (levelCount[level] || 0) + 1 }) } const statusCount = { in_collection: collectionsData.byStatus?.find(s => s.status === 'in_collection')?.count || 0, sold: collectionsData.byStatus?.find(s => s.status === 'sold')?.count || 0 } - const totalBalance = Array.isArray(usersData) ? usersData.reduce((sum, u) => sum + (u.f01_11_balance || 0), 0) : 0 + const totalBalance = Array.isArray(usersData) ? usersData.reduce((sum, u) => sum + (u.balance || 0), 0) : 0 setStats({ users: { total: totalUsers, levels: levelCount }, collections: { total: totalCollections, status: statusCount }, balance: { total: totalBalance } }) } catch (e) { console.error(e) } setLoading(false)