1
This commit is contained in:
@@ -179,12 +179,14 @@ async def deduct_credits(
|
||||
biz_key: str | None = None,
|
||||
refund_for_biz_key: str | None = None,
|
||||
record_meta: CreditRecordMeta | dict | None = None,
|
||||
record_type: str = "consume",
|
||||
) -> User:
|
||||
"""扣减用户积分,并写入消费流水。
|
||||
|
||||
并发安全点:
|
||||
- 先用 SELECT ... FOR UPDATE 锁住 users 行,避免余额覆盖。
|
||||
- biz_key 不为空时,作为正式业务幂等键;重复调用直接返回当前用户,不重复扣。
|
||||
- record_type: 流水类型,默认 "consume";团队内部流转传 "team_internal"。
|
||||
"""
|
||||
amount = round(float(amount or 0), 2)
|
||||
if amount <= 0:
|
||||
@@ -218,7 +220,7 @@ async def deduct_credits(
|
||||
record = CreditRecord(
|
||||
id=generate_id(),
|
||||
user_id=user_id,
|
||||
type="consume",
|
||||
type=record_type,
|
||||
amount=-amount,
|
||||
balance_after=user.credits,
|
||||
description=description,
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-DlCxaJzD.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-CGWGqFue.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D9_3MPsN.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -522,6 +522,9 @@ const TeamManagementPage: React.FC = () => {
|
||||
onCancel={() => setCreditModal({ open: false, member: null })}
|
||||
okText="确认"
|
||||
width={480}
|
||||
okButtonProps={{
|
||||
disabled: !creditForm.getFieldValue('amount') || creditForm.getFieldValue('amount') <= 0 || creditForm.getFieldValue('amount') > 9999999,
|
||||
}}
|
||||
>
|
||||
<Form form={creditForm} layout="vertical" style={{ marginTop: 16 }} initialValues={{ direction: 'increase' }}>
|
||||
{/* 显示管理人当前积分 */}
|
||||
@@ -540,11 +543,9 @@ const TeamManagementPage: React.FC = () => {
|
||||
<Form.Item
|
||||
name="amount"
|
||||
label="积分数量"
|
||||
rules={[
|
||||
{ required: true, message: '请输入积分数量' },
|
||||
{ type: 'number', min: 0.01, message: '必须大于 0' },
|
||||
{ type: 'number', max: 9999999, message: '单次不能超过 9999999' },
|
||||
]}
|
||||
required
|
||||
validateStatus={(!creditForm.getFieldValue('amount') || creditForm.getFieldValue('amount') <= 0) ? 'error' : ''}
|
||||
help={(!creditForm.getFieldValue('amount') || creditForm.getFieldValue('amount') <= 0) ? '请输入大于 0 的正数' : (creditForm.getFieldValue('amount') > 9999999 ? '单次不能超过 9999999' : '')}
|
||||
>
|
||||
<InputNumber
|
||||
style={{ width: '100%' }}
|
||||
@@ -554,7 +555,25 @@ const TeamManagementPage: React.FC = () => {
|
||||
precision={2}
|
||||
placeholder="请输入正数积分数量"
|
||||
size="large"
|
||||
formatter={(value) => value ? `${value}`.replace(/[^0-9.]/g, '') : ''}
|
||||
onChange={(val) => {
|
||||
// 强制清除非法值(负数、零、空)
|
||||
if (val === null || val === undefined || val <= 0) {
|
||||
creditForm.setFieldsValue({ amount: undefined });
|
||||
}
|
||||
}}
|
||||
formatter={(value) => {
|
||||
if (!value) return '';
|
||||
// 只允许数字和小数点,且必须 > 0
|
||||
let str = `${value}`.replace(/[^0-9.]/g, '');
|
||||
// 移除前导零(保留小数)
|
||||
str = str.replace(/^0+(?=\d)/, '');
|
||||
return str;
|
||||
}}
|
||||
parser={(str) => {
|
||||
const num = parseFloat(str);
|
||||
if (isNaN(num) || num <= 0) return 0 as any;
|
||||
return Math.min(num, 9999999) as any;
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="备注">
|
||||
|
||||
Reference in New Issue
Block a user