fix: v0.13.1 烂图修复 + 额度检查修正
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m46s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m46s
【烂图修复】 ①backendToFrontend 过滤空 URL 引用 ②@ 弹窗音频显示音符符号而非烂图 ③hover 预览音频显示音符而非烂图 ④视频详情空 previewUrl 显示「无预览」 【额度检查修正】 ⑤spending_limit 检查:已完成用 cost_amount,处理中用 frozen_amount ⑥超限提示改为显示总额度/已消费/剩余/本次预估 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
727be720b4
commit
7a0be57227
@ -204,14 +204,18 @@ def video_generate_view(request):
|
|||||||
# Layer 2.5: 用户总消费额度 (skip if -1)
|
# Layer 2.5: 用户总消费额度 (skip if -1)
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
if user.spending_limit != Decimal('-1'):
|
if user.spending_limit != Decimal('-1'):
|
||||||
total_spent = GenerationRecord.objects.filter(
|
# 已完成的用 cost_amount,处理中/排队的用 frozen_amount(预估费用)
|
||||||
user=user,
|
completed_cost = GenerationRecord.objects.filter(
|
||||||
status__in=['completed', 'processing', 'queued'],
|
user=user, status='completed',
|
||||||
).aggregate(total=Sum('cost_amount'))['total'] or Decimal('0')
|
).aggregate(total=Sum('cost_amount'))['total'] or Decimal('0')
|
||||||
|
pending_cost = GenerationRecord.objects.filter(
|
||||||
|
user=user, status__in=['processing', 'queued'],
|
||||||
|
).aggregate(total=Sum('frozen_amount'))['total'] or Decimal('0')
|
||||||
|
total_spent = completed_cost + pending_cost
|
||||||
if total_spent + estimated_cost > user.spending_limit:
|
if total_spent + estimated_cost > user.spending_limit:
|
||||||
return Response({
|
return Response({
|
||||||
'error': 'spending_limit_exceeded',
|
'error': 'spending_limit_exceeded',
|
||||||
'message': f'您的总消费已达上限(¥{user.spending_limit}),请联系管理员',
|
'message': f'余额不足,总额度 ¥{user.spending_limit},已消费 ¥{total_spent:.2f},剩余 ¥{(user.spending_limit - total_spent):.2f},本次预估 ¥{estimated_cost:.2f}',
|
||||||
'spending_limit': float(user.spending_limit),
|
'spending_limit': float(user.spending_limit),
|
||||||
'total_spent': float(total_spent),
|
'total_spent': float(total_spent),
|
||||||
}, status=status.HTTP_429_TOO_MANY_REQUESTS)
|
}, status=status.HTTP_429_TOO_MANY_REQUESTS)
|
||||||
|
|||||||
@ -616,13 +616,15 @@ export function PromptInput() {
|
|||||||
<div className={styles.mentionThumb}>
|
<div className={styles.mentionThumb}>
|
||||||
{ref.type === 'video' ? (
|
{ref.type === 'video' ? (
|
||||||
<video src={ref.previewUrl} muted className={styles.thumbMedia} />
|
<video src={ref.previewUrl} muted className={styles.thumbMedia} />
|
||||||
|
) : ref.type === 'audio' ? (
|
||||||
|
<span style={{ fontSize: 16 }}>{'\u266B'}</span>
|
||||||
) : (
|
) : (
|
||||||
<img src={tosThumb(ref.previewUrl, 72)} alt="" className={styles.thumbMedia} />
|
<img src={tosThumb(ref.previewUrl, 72)} alt="" className={styles.thumbMedia} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className={styles.mentionLabel}>{ref.label}</span>
|
<span className={styles.mentionLabel}>{ref.label}</span>
|
||||||
<span className={styles.mentionType}>
|
<span className={styles.mentionType}>
|
||||||
{ref.type === 'video' ? '视频' : '图片'}
|
{ref.type === 'video' ? '视频' : ref.type === 'audio' ? '音频' : '图片'}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
@ -671,6 +673,8 @@ export function PromptInput() {
|
|||||||
playsInline
|
playsInline
|
||||||
className={styles.previewMedia}
|
className={styles.previewMedia}
|
||||||
/>
|
/>
|
||||||
|
) : hoverRef.type === 'audio' ? (
|
||||||
|
<div style={{ width: 120, height: 80, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 32 }}>{'\u266B'}</div>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
src={tosThumb(hoverRef.previewUrl, 200)}
|
src={tosThumb(hoverRef.previewUrl, 200)}
|
||||||
|
|||||||
@ -493,8 +493,10 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
|
|||||||
<circle cx="18" cy="16" r="3" />
|
<circle cx="18" cy="16" r="3" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : ref.previewUrl ? (
|
||||||
<img src={tosThumb(ref.previewUrl, 300)} alt={ref.label} className={styles.refImg} style={{ cursor: 'zoom-in' }} onClick={() => setLightboxSrc(ref.previewUrl)} />
|
<img src={tosThumb(ref.previewUrl, 300)} alt={ref.label} className={styles.refImg} style={{ cursor: 'zoom-in' }} onClick={() => setLightboxSrc(ref.previewUrl)} />
|
||||||
|
) : (
|
||||||
|
<div className={styles.refAudioPlaceholder} style={{ fontSize: 12, color: 'var(--color-text-disabled)' }}>无预览</div>
|
||||||
)}
|
)}
|
||||||
<span className={styles.refLabel}>{ref.label}</span>
|
<span className={styles.refLabel}>{ref.label}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -62,7 +62,9 @@ function backendToFrontend(bt: BackendTask): GenerationTask {
|
|||||||
const references: ReferenceSnapshot[] = allRefs
|
const references: ReferenceSnapshot[] = allRefs
|
||||||
.filter((ref) => {
|
.filter((ref) => {
|
||||||
const url = ref.url || '';
|
const url = ref.url || '';
|
||||||
return !url.startsWith('asset://') && !url.startsWith('Asset://');
|
if (!url || url.trim() === '') return false;
|
||||||
|
if (url.startsWith('asset://') || url.startsWith('Asset://')) return false;
|
||||||
|
return true;
|
||||||
})
|
})
|
||||||
.map((ref, i) => ({
|
.map((ref, i) => ({
|
||||||
id: `ref_${bt.task_id}_${i}`,
|
id: `ref_${bt.task_id}_${i}`,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user