fix(admin): set user image quota by total instead of delta

Operators expect direct total capacity like space quota; the delta-based
grant UI caused wrong results when entering a target amount on production.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-05 16:07:49 +08:00
parent f8317e8312
commit eca1aa635e
+53 -16
View File
@@ -25,7 +25,8 @@ export function UserDetailPage() {
const [spaceQuotaMb, setSpaceQuotaMb] = useState('5');
const [imageQuota, setImageQuota] = useState<ImageQuotaView | null>(null);
const [imageQuotaLoading, setImageQuotaLoading] = useState(false);
const [imageGrant, setImageGrant] = useState({ delta: '', note: '' });
const [imageTotal, setImageTotal] = useState('');
const [imageGrantNote, setImageGrantNote] = useState('');
useEffect(() => {
if (!user?.spaceQuotaBytes) return;
@@ -82,6 +83,14 @@ export function UserDetailPage() {
};
}, [user?.id, user?.role]);
useEffect(() => {
if (!imageQuota || imageQuota.unlimited || imageQuota.total == null) {
setImageTotal('');
return;
}
setImageTotal(String(imageQuota.total));
}, [imageQuota?.total, imageQuota?.unlimited]);
const handleRecharge = async (e: React.FormEvent) => {
e.preventDefault();
if (!user) return;
@@ -100,24 +109,42 @@ export function UserDetailPage() {
}
};
const handleImageGrant = async (e: React.FormEvent) => {
const handleImageQuotaSave = async (e: React.FormEvent) => {
e.preventDefault();
if (!user) return;
setMessage(null);
setLocalError(null);
setError(null);
const delta = Math.floor(Number(imageGrant.delta));
if (!Number.isFinite(delta) || delta === 0) {
setLocalError('请输入非零整数额度');
if (!imageQuota) {
setLocalError('暂无额度信息,用户可能尚无有效订阅');
return;
}
if (imageQuota.unlimited) {
setLocalError('当前为无限额度套餐,无法在此调整');
return;
}
const targetTotal = Math.floor(Number(imageTotal));
if (!Number.isFinite(targetTotal) || targetTotal < 0) {
setLocalError('请输入非负整数总额度');
return;
}
if (targetTotal < imageQuota.used) {
setLocalError(`总额度不能低于已用量(${imageQuota.used} 张)`);
return;
}
const targetBonus = targetTotal - imageQuota.limit;
const delta = targetBonus - imageQuota.bonus;
if (delta === 0) {
setMessage('额度未变化');
return;
}
try {
const result = await grantUserImageQuota(user.id, delta, imageGrant.note.trim());
const result = await grantUserImageQuota(user.id, delta, imageGrantNote.trim());
setImageQuota(result.quota);
setMessage('图片额度已更新');
setImageGrant({ delta: '', note: '' });
setImageGrantNote('');
} catch (err) {
setLocalError(err instanceof Error ? err.message : '图片额度调整失败');
setLocalError(err instanceof Error ? err.message : '图片额度设置失败');
}
};
@@ -245,21 +272,31 @@ export function UserDetailPage() {
) : (
<p className="muted">{imageQuotaSummary || '暂无额度信息(用户可能尚无订阅)'}</p>
)}
<form className="admin-form" onSubmit={handleImageGrant}>
<p className="muted">
+
</p>
<form className="admin-form" onSubmit={handleImageQuotaSave}>
<input
placeholder="调整额度(张,正数充值、负数扣减"
placeholder="图片总额度(张)"
type="number"
min="0"
step="1"
value={imageGrant.delta}
onChange={(e) => setImageGrant((s) => ({ ...s, delta: e.target.value }))}
value={imageTotal}
onChange={(e) => setImageTotal(e.target.value)}
disabled={!imageQuota || imageQuota.unlimited}
/>
<input
placeholder="备注"
value={imageGrant.note}
onChange={(e) => setImageGrant((s) => ({ ...s, note: e.target.value }))}
value={imageGrantNote}
onChange={(e) => setImageGrantNote(e.target.value)}
disabled={!imageQuota || imageQuota.unlimited}
/>
<button type="submit" className="send-btn">
<button
type="submit"
className="send-btn"
disabled={!imageQuota || imageQuota.unlimited}
>
</button>
</form>
</section>