fix: merge 30 general improvements from release branch

Bug fixes:
- Detached context for GetAccountConcurrencyBatch (prevent all-zero on request cancel)
- Filter soft-deleted users in GetByGroupID
- Stripe CSP policy (allow Stripe.js in script-src and frame-src)
- WebSearch API key validation on save
- RECHARGING status in payment result success check
- Windows test fixes (logger Sync deadlock, config path escaping)

Feature enhancements:
- Webhook multi-instance dispatch (extractOutTradeNo + GetWebhookProvider)
- EasyPay mobile H5 payment (device param + PayURL2)
- SSE error propagation in WebSearch emulation
- AccountStatsCost DTO field for admin usage logs
- Plans sort by sort_order instead of created_at
- UsageMapHook for streaming response usage data
- apicompat Instructions field passthrough
- EffectiveLoadFactor for ops concurrency/metrics
- Usage billing RETURNING balance for notify system
- BulkUpdate mixed channel warning with details
- println to slog migration in auth cache
- Wire ProviderSet cleanup
- CI cache-dependency-path optimization

Frontend:
- Refund eligibility check per provider (canRequestRefund)
- Plan sort_order editing
- Dead code cleanup (simulate_claude_max, client_affinity)
- GroupsView platform switch guard
- channels features_config API type
- UsageView account_stats_cost export
This commit is contained in:
erio
2026-04-14 17:35:27 +08:00
parent f1297a3694
commit 6ac8ccde46
34 changed files with 306 additions and 118 deletions

View File

@@ -22,7 +22,7 @@
<Icon name="x" size="sm" />
<span>{{ t('payment.orders.cancel') }}</span>
</button>
<button v-if="row.status === 'COMPLETED'" @click="openRefundDialog(row)" class="inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-purple-600 hover:bg-purple-50 dark:text-purple-400 dark:hover:bg-purple-900/20">
<button v-if="canRequestRefund(row)" @click="openRefundDialog(row)" class="inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-purple-600 hover:bg-purple-50 dark:text-purple-400 dark:hover:bg-purple-900/20">
<Icon name="dollar" size="sm" />
<span>{{ t('payment.orders.requestRefund') }}</span>
</button>
@@ -102,6 +102,7 @@ const appStore = useAppStore()
const loading = ref(false)
const actionLoading = ref(false)
const orders = ref<PaymentOrder[]>([])
const refundEligibleProviders = ref<Set<string>>(new Set())
const currentFilter = ref('')
const cancelTargetId = ref<number | null>(null)
const refundTarget = ref<PaymentOrder | null>(null)
@@ -171,5 +172,18 @@ async function confirmRefund() {
}
}
onMounted(() => fetchOrders())
function canRequestRefund(order: PaymentOrder): boolean {
if (order.status !== 'COMPLETED') return false
if (!order.provider_instance_id) return false
return refundEligibleProviders.value.has(order.provider_instance_id)
}
async function loadRefundEligibility() {
try {
const res = await paymentAPI.getRefundEligibleProviders()
refundEligibleProviders.value = new Set(res.data.provider_instance_ids || [])
} catch { /* ignore — default to hiding refund button */ }
}
onMounted(() => { fetchOrders(); loadRefundEligibility() })
</script>