voucher/rules/validators.py
yumoqing 2c56aa904a feat: 代金券模块初始实现
- 4张表: voucher_template, voucher_rule, voucher_instance, voucher_usage_log
- 可配置规则引擎: registry + validators + engine
- 8种内置规则: min_amount, max_amount, applicable_product_type,
  applicable_product, exclude_product, max_usage_count, valid_period, user_level
- CRUD定义 + API接口 + 前端页面
- SQL建表脚本 + RBAC权限配置
- 一次性使用,不找零
2026-05-29 00:28:01 +08:00

101 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""内置规则校验器,每种规则一个函数,返回 (bool, error_msg)"""
from decimal import Decimal
from datetime import datetime
from .registry import register_rule
@register_rule('min_amount')
def check_min_amount(config, context):
"""最低消费门槛"""
min_val = Decimal(str(config.get('min_value', 0)))
request_amount = Decimal(str(context.get('request_amount', 0)))
if request_amount < min_val:
return False, f"未达最低消费 {min_val}"
return True, None
@register_rule('max_amount')
def check_max_amount(config, context):
"""最高消费限制"""
max_val = Decimal(str(config.get('max_value', 0)))
if max_val <= 0:
return True, None
request_amount = Decimal(str(context.get('request_amount', 0)))
if request_amount > max_val:
return False, f"超过最高消费 {max_val}"
return True, None
@register_rule('applicable_product_type')
def check_product_type(config, context):
"""限定产品类型大类llm/image/video/audio"""
allowed_types = config.get('product_types', [])
if not allowed_types:
return True, None
current_type = context.get('product_type', '')
if current_type and current_type not in allowed_types:
return False, f"仅限 {', '.join(allowed_types)} 类型使用"
return True, None
@register_rule('applicable_product')
def check_specific_product(config, context):
"""限定特定产品(具体模型名)"""
allowed_products = config.get('products', [])
if not allowed_products:
return True, None
current_product = context.get('product_name', '')
if current_product and current_product not in allowed_products:
return False, f"仅限 {', '.join(allowed_products)} 使用"
return True, None
@register_rule('exclude_product')
def check_exclude_product(config, context):
"""排除特定产品"""
excluded = config.get('products', [])
if not excluded:
return True, None
current_product = context.get('product_name', '')
if current_product and current_product in excluded:
return False, f"{current_product} 不可使用此代金券"
return True, None
@register_rule('max_usage_count')
def check_max_usage_count(config, context):
"""最大使用次数"""
max_count = int(config.get('max_count', 0))
if max_count == 0:
return True, None
used_count = int(context.get('used_count', 0))
if used_count >= max_count:
return False, f"已达最大使用次数 {max_count}"
return True, None
@register_rule('valid_period')
def check_valid_period(config, context):
"""有效期检查(通常由 instance 的 valid_from/valid_to 自动处理,此规则用于额外限制)"""
now = datetime.now()
valid_from = context.get('valid_from')
valid_to = context.get('valid_to')
if valid_from and now < valid_from:
return False, "代金券尚未生效"
if valid_to and now > valid_to:
return False, "代金券已过期"
return True, None
@register_rule('user_level')
def check_user_level(config, context):
"""用户等级限制"""
required_level = int(config.get('min_level', 0))
if required_level == 0:
return True, None
user_level = int(context.get('user_level', 0))
if user_level < required_level:
return False, f"{required_level} 级以上用户"
return True, None