## 计费体系 - 团队额度从秒数改为金额(余额/冻结/月消费上限) - 用户限额从秒数改为次数(每日50次/每月1500次) - 新增 billing.py 工具模块(分辨率→像素映射 + token/费用计算) - 扣费流程:预扣制→冻结制(提交冻结预估金额,完成按实际tokens扣费,失败释放) - 允许小额透支(实际费用超预估时余额可变负) - 团队加价比例(markup_percentage),创建团队时必填 ## Token 追踪 - GenerationRecord 新增 tokens_consumed/cost_amount/base_cost_amount - 任务完成时从 Seedance API usage.total_tokens 获取精确值 - 生成页显示预估消耗(tokens + 金额),按团队售价计算 ## 管理后台 - 仪表盘新增利润分析板块(总收入/成本/利润/利润率 + 团队利润排行) - 消费记录新增 Tokens/售价/成本/利润列 - 团队管理:充值改为充金额,新增加价比例设置 - 系统设置:默认限额改为次数,新增基础token单价配置 ## Bug 修复 - 登录弹窗:拖选输入框内容不再误关闭(onClick→mousedown+mouseup) - 视频详情弹窗:遮罩层覆盖全视口(left:76px→0),admin/团管侧栏不再露出 ## UI 增强 - 图片大图预览:上传区和视频详情弹窗的图片支持点击查看大图(ImageLightbox) - 移除 adaptive 比例和智能时长选项,确保 token 预估可精确计算 - 视频详情弹窗显示实际消耗 tokens 和费用 ## 前端全量更新 - 所有页面秒数显示替换为金额(元)和次数(次) - TypeScript 类型全量更新 - API 调用参数同步更新 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
124 lines
7.5 KiB
Python
124 lines
7.5 KiB
Python
from rest_framework import serializers
|
|
|
|
|
|
class VideoGenerateSerializer(serializers.Serializer):
|
|
prompt = serializers.CharField(required=False, allow_blank=True, default='')
|
|
mode = serializers.ChoiceField(choices=['universal', 'keyframe'])
|
|
model = serializers.ChoiceField(choices=['seedance_2.0', 'seedance_2.0_fast'])
|
|
aspect_ratio = serializers.CharField(max_length=10)
|
|
duration = serializers.IntegerField()
|
|
references = serializers.ListField(child=serializers.DictField(), required=False, default=list)
|
|
|
|
|
|
class QuotaUpdateSerializer(serializers.Serializer):
|
|
daily_generation_limit = serializers.IntegerField(min_value=-1)
|
|
monthly_generation_limit = serializers.IntegerField(min_value=-1)
|
|
|
|
|
|
class UserStatusSerializer(serializers.Serializer):
|
|
is_active = serializers.BooleanField()
|
|
|
|
|
|
class AdminCreateUserSerializer(serializers.Serializer):
|
|
username = serializers.CharField(max_length=150)
|
|
email = serializers.EmailField()
|
|
password = serializers.CharField(min_length=6)
|
|
daily_seconds_limit = serializers.IntegerField(min_value=-1, required=False, default=600)
|
|
monthly_seconds_limit = serializers.IntegerField(min_value=-1, required=False, default=6000)
|
|
daily_generation_limit = serializers.IntegerField(min_value=-1, required=False, default=50)
|
|
monthly_generation_limit = serializers.IntegerField(min_value=-1, required=False, default=1500)
|
|
is_staff = serializers.BooleanField(required=False, default=False)
|
|
|
|
|
|
class SystemSettingsSerializer(serializers.Serializer):
|
|
default_daily_seconds_limit = serializers.IntegerField(min_value=0, required=False)
|
|
default_monthly_seconds_limit = serializers.IntegerField(min_value=0, required=False)
|
|
default_daily_generation_limit = serializers.IntegerField(min_value=0, required=False)
|
|
default_monthly_generation_limit = serializers.IntegerField(min_value=0, required=False)
|
|
base_token_price = serializers.DecimalField(max_digits=10, decimal_places=2, min_value=0, required=False)
|
|
announcement = serializers.CharField(required=False, allow_blank=True, default='')
|
|
announcement_enabled = serializers.BooleanField(required=False, default=False)
|
|
max_desktop_sessions = serializers.IntegerField(min_value=1, required=False, default=1)
|
|
max_mobile_sessions = serializers.IntegerField(min_value=0, required=False, default=0)
|
|
# 异常检测配置
|
|
anomaly_detection_enabled = serializers.BooleanField(required=False, default=False)
|
|
r1_enabled_default = serializers.BooleanField(required=False, default=True)
|
|
r2_enabled_default = serializers.BooleanField(required=False, default=True)
|
|
r2_window_seconds = serializers.IntegerField(min_value=60, required=False, default=3600)
|
|
r3_enabled_default = serializers.BooleanField(required=False, default=True)
|
|
r3_window_seconds = serializers.IntegerField(min_value=60, required=False, default=3600)
|
|
r3_max_count = serializers.IntegerField(min_value=1, required=False, default=10)
|
|
r4_enabled_default = serializers.BooleanField(required=False, default=True)
|
|
r4_window_seconds = serializers.IntegerField(min_value=60, required=False, default=3600)
|
|
r4_city_count = serializers.IntegerField(min_value=1, required=False, default=5)
|
|
r5_enabled_default = serializers.BooleanField(required=False, default=True)
|
|
r5_days = serializers.IntegerField(min_value=1, required=False, default=7)
|
|
r5_country_count = serializers.IntegerField(min_value=1, required=False, default=10)
|
|
feishu_alert_mobiles = serializers.CharField(required=False, allow_blank=True, default='')
|
|
sms_alert_mobiles = serializers.CharField(required=False, allow_blank=True, default='')
|
|
alert_cooldown_seconds = serializers.IntegerField(min_value=0, required=False, default=1800)
|
|
|
|
|
|
# ── Team serializers ──
|
|
|
|
class TeamCreateSerializer(serializers.Serializer):
|
|
name = serializers.CharField(max_length=100)
|
|
monthly_seconds_limit = serializers.IntegerField(min_value=0, required=False, default=6000)
|
|
daily_member_limit_default = serializers.IntegerField(min_value=0, required=False, default=600)
|
|
markup_percentage = serializers.DecimalField(max_digits=5, decimal_places=2, min_value=0, required=True)
|
|
monthly_spending_limit = serializers.DecimalField(max_digits=12, decimal_places=2, required=False, default=-1)
|
|
daily_member_spending_default = serializers.DecimalField(max_digits=12, decimal_places=2, required=False, default=50)
|
|
expected_regions = serializers.CharField(max_length=500, required=True)
|
|
|
|
|
|
class TeamUpdateSerializer(serializers.Serializer):
|
|
name = serializers.CharField(max_length=100, required=False)
|
|
monthly_seconds_limit = serializers.IntegerField(min_value=0, required=False)
|
|
daily_member_limit_default = serializers.IntegerField(min_value=0, required=False)
|
|
markup_percentage = serializers.DecimalField(max_digits=5, decimal_places=2, min_value=0, required=False)
|
|
monthly_spending_limit = serializers.DecimalField(max_digits=12, decimal_places=2, required=False)
|
|
daily_member_spending_default = serializers.DecimalField(max_digits=12, decimal_places=2, required=False)
|
|
is_active = serializers.BooleanField(required=False)
|
|
expected_regions = serializers.CharField(max_length=500, required=False, allow_blank=True)
|
|
|
|
|
|
class TeamAnomalyConfigSerializer(serializers.Serializer):
|
|
r1_enabled = serializers.BooleanField(required=False, allow_null=True, default=None)
|
|
r2_enabled = serializers.BooleanField(required=False, allow_null=True, default=None)
|
|
r2_window_seconds = serializers.IntegerField(min_value=60, required=False, allow_null=True, default=None)
|
|
r3_enabled = serializers.BooleanField(required=False, allow_null=True, default=None)
|
|
r3_window_seconds = serializers.IntegerField(min_value=60, required=False, allow_null=True, default=None)
|
|
r3_max_count = serializers.IntegerField(min_value=1, required=False, allow_null=True, default=None)
|
|
r4_enabled = serializers.BooleanField(required=False, allow_null=True, default=None)
|
|
r4_window_seconds = serializers.IntegerField(min_value=60, required=False, allow_null=True, default=None)
|
|
r4_city_count = serializers.IntegerField(min_value=1, required=False, allow_null=True, default=None)
|
|
r5_enabled = serializers.BooleanField(required=False, allow_null=True, default=None)
|
|
r5_days = serializers.IntegerField(min_value=1, required=False, allow_null=True, default=None)
|
|
r5_country_count = serializers.IntegerField(min_value=1, required=False, allow_null=True, default=None)
|
|
|
|
|
|
class TeamTopUpSerializer(serializers.Serializer):
|
|
amount = serializers.DecimalField(max_digits=12, decimal_places=2, min_value=0.01)
|
|
|
|
|
|
class TeamAdminCreateSerializer(serializers.Serializer):
|
|
"""Create a team admin account for a specific team."""
|
|
username = serializers.CharField(max_length=150)
|
|
email = serializers.EmailField()
|
|
password = serializers.CharField(min_length=6)
|
|
|
|
|
|
class TeamMemberCreateSerializer(serializers.Serializer):
|
|
"""Team admin creates a member."""
|
|
username = serializers.CharField(max_length=150)
|
|
password = serializers.CharField(min_length=6)
|
|
daily_seconds_limit = serializers.IntegerField(min_value=-1, required=False)
|
|
monthly_seconds_limit = serializers.IntegerField(min_value=-1, required=False)
|
|
daily_generation_limit = serializers.IntegerField(min_value=-1, required=False)
|
|
monthly_generation_limit = serializers.IntegerField(min_value=-1, required=False)
|
|
|
|
|
|
class MemberQuotaSerializer(serializers.Serializer):
|
|
daily_generation_limit = serializers.IntegerField(min_value=-1)
|
|
monthly_generation_limit = serializers.IntegerField(min_value=-1)
|