From b5d6071a3e3c9a7f6b3eac80ae8ac2b78ca9400e Mon Sep 17 00:00:00 2001 From: txai-dev Date: Mon, 14 Sep 2026 13:48:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E4=BD=99=E9=A2=9D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=94=A8=E6=88=B7=E6=90=9C=E7=B4=A2=E9=80=89?= =?UTF-8?q?=E6=8B=A9=EF=BC=88=E6=98=B5=E7=A7=B0/openid=E5=89=8D=E7=BC=80?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=86=8D=E9=9C=80=E8=A6=81=E6=89=8B=E6=89=93?= =?UTF-8?q?28=E4=BD=8D=E5=AE=8C=E6=95=B4openid=EF=BC=89+=20API=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/src/views/VpayOrders.vue | 71 +++++++++++++++++++++++++++++++--- server/src/routes/admin.js | 10 ++++- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/admin/src/views/VpayOrders.vue b/admin/src/views/VpayOrders.vue index bc7c9eb..0c88c83 100644 --- a/admin/src/views/VpayOrders.vue +++ b/admin/src/views/VpayOrders.vue @@ -102,9 +102,21 @@ - + - + + +
+
+ {{ u.nickname || '未命名' }} + {{ (u.openid||'').slice(0,14) }}… | 星尘 {{ u.vpayCoins || 0 }} +
+
+
+ ✓ {{ pickedUser.nickname || '未命名' }} | 星尘 {{ pickedUser.vpayCoins || 0 }} +
+
正=赠送 负=扣减 @@ -113,7 +125,7 @@
@@ -134,7 +146,28 @@ const refundCoins = computed(() => { if (!o) return 0 return o.metadata?.coins || Math.round((o.amount || 0) * 100) }) -const adjustDlg = ref(false), adjustForm = ref({ openid: '', amount: 100, reason: '' }) +const adjustDlg = ref(false), adjustForm = ref({ search: '', amount: 100, reason: '' }) +const searchResults = ref([]), pickedUser = ref(null) +let searchTimer = null + +function searchUsers() { + if (searchTimer) clearTimeout(searchTimer) + pickedUser.value = null + searchResults.value = [] + const kw = adjustForm.value.search?.trim() + if (!kw || kw.length < 2) return + searchTimer = setTimeout(async () => { + try { + const r = await apiGet(`/api/admin/users?search=${encodeURIComponent(kw)}&limit=5`) + searchResults.value = (r.items || []).slice(0, 5) + } catch (e) {} + }, 300) +} +function pickUser(u) { + pickedUser.value = u + searchResults.value = [] + adjustForm.value.search = u.nickname || (u.openid||'').slice(0,12) +} function fmt(t) { return t ? new Date(t).toLocaleString('zh-CN', { hour12: false }) : '—' } @@ -173,10 +206,17 @@ async function doRefund() { } } async function doAdjust() { + if (!pickedUser.value) return ElMessage.warning('请先搜索并选择用户') try { - await apiPost('/api/admin/vpay/adjust', adjustForm.value) - ElMessage.success('调整成功') + await apiPost('/api/admin/vpay/adjust', { + openid: pickedUser.value.openid, + amount: adjustForm.value.amount, + reason: adjustForm.value.reason, + }) + ElMessage.success(`调整成功:${pickedUser.value.nickname || '用户'} ${(adjustForm.value.amount > 0 ? '+' : '') + adjustForm.value.amount} 星尘`) adjustDlg.value = false + pickedUser.value = null + adjustForm.value = { search: '', amount: 100, reason: '' } loadLedger(); loadStats() } catch (e) { ElMessage.error(e.message || '调整失败') } } @@ -192,3 +232,22 @@ onMounted(() => { loadStats(); load(); loadLedger() }) .scard i { font-size: 11px; color: #a6abbb; font-style: normal; } .toolbar { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; } + + diff --git a/server/src/routes/admin.js b/server/src/routes/admin.js index 195fcbc..0ff243e 100755 --- a/server/src/routes/admin.js +++ b/server/src/routes/admin.js @@ -1339,8 +1339,14 @@ router.post('/vpay/adjust', requirePermission('orders:write'), async (req, res) try { const { openid, amount, reason } = req.body if (!openid || !amount || Math.abs(amount) > 100000) return ApiResponse.error(res, '参数无效', 400) - const user = await User.findOne({ openid }) - if (!user) return ApiResponse.notFound(res, '用户不存在') + // 支持前缀匹配:输入至少8位openid前缀即可(完整openid有28位,手打太长) + let user = await User.findOne({ openid }) + if (!user && openid.length >= 8) { + const matches = await User.find({ openid: { $regex: '^' + openid } }).limit(5) + if (matches.length === 1) user = matches[0] + else if (matches.length > 1) return ApiResponse.error(res, `匹配到 ${matches.length} 个用户,请输入更多字符`, 400) + } + if (!user) return ApiResponse.notFound(res, '用户不存在(支持输入openid前8位以上)') user.vpayCoins = (user.vpayCoins || 0) + amount await user.save()