feat: 中文化 django-admin 字段标签 (T-603)
This commit is contained in:
+11
-11
@@ -8,10 +8,10 @@ from .security import AiKeyEncryptionError, encrypt_api_key
|
||||
|
||||
class AiModelAdminForm(forms.ModelForm):
|
||||
api_key = forms.CharField(
|
||||
label="API key",
|
||||
label="API Key",
|
||||
required=False,
|
||||
widget=forms.PasswordInput(render_value=False),
|
||||
help_text="Leave blank to keep the existing encrypted key.",
|
||||
help_text="留空表示保留已有加密密钥。",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -33,7 +33,7 @@ class AiModelAdminForm(forms.ModelForm):
|
||||
cleaned_data = super().clean()
|
||||
api_key = cleaned_data.get("api_key")
|
||||
if not self.instance.pk and not api_key:
|
||||
raise forms.ValidationError("API key is required when creating an AI model.")
|
||||
raise forms.ValidationError("创建 AI 模型时必须填写 API Key。")
|
||||
if api_key:
|
||||
try:
|
||||
self._api_key_encrypted = encrypt_api_key(api_key)
|
||||
@@ -71,14 +71,14 @@ class AiModelAdmin(admin.ModelAdmin):
|
||||
fieldsets = (
|
||||
(None, {"fields": ("name", "url", "model", "api_type", "is_active")}),
|
||||
(
|
||||
"Credentials",
|
||||
"凭证",
|
||||
{
|
||||
"fields": ("api_key", "api_key_status"),
|
||||
"description": "The stored key is encrypted and never displayed.",
|
||||
"description": "已保存的密钥会加密存储,后台不会回显明文。",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Capabilities and request defaults",
|
||||
"能力与请求默认值",
|
||||
{
|
||||
"fields": (
|
||||
"capabilities",
|
||||
@@ -88,16 +88,16 @@ class AiModelAdmin(admin.ModelAdmin):
|
||||
)
|
||||
},
|
||||
),
|
||||
("Timestamps", {"fields": ("created_at", "updated_at")}),
|
||||
("时间信息", {"fields": ("created_at", "updated_at")}),
|
||||
)
|
||||
|
||||
@admin.display(description="capabilities")
|
||||
@admin.display(description="能力")
|
||||
def capabilities_display(self, obj):
|
||||
return ", ".join(sorted(obj.capabilities_set()))
|
||||
|
||||
@admin.display(description="API key")
|
||||
@admin.display(description="API Key")
|
||||
def api_key_status(self, obj):
|
||||
return obj.api_key_masked or "not set"
|
||||
return obj.api_key_masked or "未设置"
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
before = _snapshot_existing(obj) if change else None
|
||||
@@ -241,7 +241,7 @@ class AiConfigAuditLogAdmin(admin.ModelAdmin):
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
@admin.display(description="changed fields")
|
||||
@admin.display(description="变更字段")
|
||||
def changed_fields_display(self, obj):
|
||||
return ", ".join(obj.changed_fields)
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-04 08:02
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ai', '0003_alter_aiconfigauditlog_options_alter_aimodel_options_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='action',
|
||||
field=models.CharField(choices=[('create', '创建'), ('update', '更新'), ('delete', '删除')], max_length=16, verbose_name='动作'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='actor',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='操作人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='changed_fields',
|
||||
field=models.JSONField(blank=True, default=list, verbose_name='变更字段'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='changes',
|
||||
field=models.JSONField(blank=True, default=dict, verbose_name='变更内容'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='target_id',
|
||||
field=models.PositiveBigIntegerField(blank=True, null=True, verbose_name='目标 ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='target_repr',
|
||||
field=models.CharField(max_length=255, verbose_name='目标描述'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aiconfigauditlog',
|
||||
name='target_type',
|
||||
field=models.CharField(choices=[('ai_model', 'AI 模型'), ('model_alias', '能力别名')], max_length=32, verbose_name='目标类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='api_key_encrypted',
|
||||
field=models.TextField(blank=True, editable=False, verbose_name='加密 API Key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='api_type',
|
||||
field=models.CharField(choices=[('auto', '自动'), ('chat', '聊天补全'), ('gemini', 'Gemini'), ('images', '图片生成'), ('images_edits', '图片编辑')], default='auto', max_length=32, verbose_name='API 类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='capabilities',
|
||||
field=models.JSONField(blank=True, default=list, verbose_name='能力'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='connect_timeout_seconds',
|
||||
field=models.PositiveIntegerField(default=30, verbose_name='连接超时秒数'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='extra_body',
|
||||
field=models.JSONField(blank=True, default=dict, verbose_name='额外请求体'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='is_active',
|
||||
field=models.BooleanField(default=True, verbose_name='启用'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='model',
|
||||
field=models.CharField(max_length=128, verbose_name='模型标识'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='name',
|
||||
field=models.CharField(max_length=128, unique=True, verbose_name='名称'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='timeout_seconds',
|
||||
field=models.PositiveIntegerField(default=0, help_text='0 表示使用 provider 默认值。', verbose_name='请求超时秒数'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aimodel',
|
||||
name='url',
|
||||
field=models.URLField(max_length=500, verbose_name='接口 URL'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modelalias',
|
||||
name='ai_model',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='aliases', to='ai.aimodel', verbose_name='AI 模型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modelalias',
|
||||
name='alias',
|
||||
field=models.SlugField(max_length=64, verbose_name='能力别名'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modelalias',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modelalias',
|
||||
name='is_active',
|
||||
field=models.BooleanField(default=True, verbose_name='启用'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modelalias',
|
||||
name='is_default',
|
||||
field=models.BooleanField(default=False, verbose_name='默认别名'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modelalias',
|
||||
name='operation_type',
|
||||
field=models.CharField(choices=[('title', '生成标题'), ('image', '生成图片')], max_length=32, verbose_name='操作类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modelalias',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
]
|
||||
+39
-35
@@ -11,31 +11,33 @@ from .security import decrypt_api_key, encrypt_api_key, mask_secret
|
||||
|
||||
class AiModel(models.Model):
|
||||
class ApiType(models.TextChoices):
|
||||
AUTO = "auto", "Auto"
|
||||
CHAT = "chat", "Chat completions"
|
||||
AUTO = "auto", "自动"
|
||||
CHAT = "chat", "聊天补全"
|
||||
GEMINI = "gemini", "Gemini"
|
||||
IMAGES = "images", "Images generations"
|
||||
IMAGES_EDITS = "images_edits", "Images edits"
|
||||
IMAGES = "images", "图片生成"
|
||||
IMAGES_EDITS = "images_edits", "图片编辑"
|
||||
|
||||
name = models.CharField(max_length=128, unique=True)
|
||||
url = models.URLField(max_length=500)
|
||||
model = models.CharField(max_length=128)
|
||||
name = models.CharField("名称", max_length=128, unique=True)
|
||||
url = models.URLField("接口 URL", max_length=500)
|
||||
model = models.CharField("模型标识", max_length=128)
|
||||
api_type = models.CharField(
|
||||
"API 类型",
|
||||
max_length=32,
|
||||
choices=ApiType.choices,
|
||||
default=ApiType.AUTO,
|
||||
)
|
||||
api_key_encrypted = models.TextField(blank=True, editable=False)
|
||||
capabilities = models.JSONField(default=list, blank=True)
|
||||
api_key_encrypted = models.TextField("加密 API Key", blank=True, editable=False)
|
||||
capabilities = models.JSONField("能力", default=list, blank=True)
|
||||
timeout_seconds = models.PositiveIntegerField(
|
||||
"请求超时秒数",
|
||||
default=0,
|
||||
help_text="0 means use provider resolution defaults.",
|
||||
help_text="0 表示使用 provider 默认值。",
|
||||
)
|
||||
connect_timeout_seconds = models.PositiveIntegerField(default=30)
|
||||
extra_body = models.JSONField(default=dict, blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
connect_timeout_seconds = models.PositiveIntegerField("连接超时秒数", default=30)
|
||||
extra_body = models.JSONField("额外请求体", default=dict, blank=True)
|
||||
is_active = models.BooleanField("启用", default=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "ai_model"
|
||||
@@ -88,20 +90,21 @@ class AiModel(models.Model):
|
||||
|
||||
class ModelAlias(models.Model):
|
||||
class OperationType(models.TextChoices):
|
||||
TITLE = "title", "Generate title"
|
||||
IMAGE = "image", "Generate image"
|
||||
TITLE = "title", "生成标题"
|
||||
IMAGE = "image", "生成图片"
|
||||
|
||||
alias = models.SlugField(max_length=64)
|
||||
operation_type = models.CharField(max_length=32, choices=OperationType.choices)
|
||||
alias = models.SlugField("能力别名", max_length=64)
|
||||
operation_type = models.CharField("操作类型", max_length=32, choices=OperationType.choices)
|
||||
ai_model = models.ForeignKey(
|
||||
AiModel,
|
||||
verbose_name="AI 模型",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="aliases",
|
||||
)
|
||||
is_default = models.BooleanField(default=False)
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
is_default = models.BooleanField("默认别名", default=False)
|
||||
is_active = models.BooleanField("启用", default=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "model_alias"
|
||||
@@ -142,28 +145,29 @@ class ModelAlias(models.Model):
|
||||
|
||||
class AiConfigAuditLog(models.Model):
|
||||
class TargetType(models.TextChoices):
|
||||
AI_MODEL = "ai_model", "AI model"
|
||||
MODEL_ALIAS = "model_alias", "Model alias"
|
||||
AI_MODEL = "ai_model", "AI 模型"
|
||||
MODEL_ALIAS = "model_alias", "能力别名"
|
||||
|
||||
class Action(models.TextChoices):
|
||||
CREATE = "create", "Create"
|
||||
UPDATE = "update", "Update"
|
||||
DELETE = "delete", "Delete"
|
||||
CREATE = "create", "创建"
|
||||
UPDATE = "update", "更新"
|
||||
DELETE = "delete", "删除"
|
||||
|
||||
actor = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name="操作人",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
)
|
||||
action = models.CharField(max_length=16, choices=Action.choices)
|
||||
target_type = models.CharField(max_length=32, choices=TargetType.choices)
|
||||
target_id = models.PositiveBigIntegerField(null=True, blank=True)
|
||||
target_repr = models.CharField(max_length=255)
|
||||
changed_fields = models.JSONField(default=list, blank=True)
|
||||
changes = models.JSONField(default=dict, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
action = models.CharField("动作", max_length=16, choices=Action.choices)
|
||||
target_type = models.CharField("目标类型", max_length=32, choices=TargetType.choices)
|
||||
target_id = models.PositiveBigIntegerField("目标 ID", null=True, blank=True)
|
||||
target_repr = models.CharField("目标描述", max_length=255)
|
||||
changed_fields = models.JSONField("变更字段", default=list, blank=True)
|
||||
changes = models.JSONField("变更内容", default=dict, blank=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "ai_config_audit_log"
|
||||
|
||||
@@ -51,7 +51,7 @@ class PricingRuleAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ("created_at", "updated_at")
|
||||
list_editable = ("points_cost", "is_active")
|
||||
|
||||
@admin.display(description="resolution")
|
||||
@admin.display(description="分辨率")
|
||||
def resolution_display(self, obj):
|
||||
return obj.resolution or "*"
|
||||
|
||||
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-04 08:02
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0005_alter_callrecord_options_alter_exchangerate_options_and_more'),
|
||||
('users', '0005_alter_apikey_created_at_alter_apikey_key_hash_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='alias',
|
||||
field=models.CharField(blank=True, max_length=64, verbose_name='能力别名'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='api_key',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='call_records', to='users.apikey', verbose_name='API 密钥'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='error_message',
|
||||
field=models.TextField(blank=True, verbose_name='错误信息'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='model_used',
|
||||
field=models.CharField(blank=True, max_length=128, verbose_name='实际模型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='operation_type',
|
||||
field=models.CharField(choices=[('title', '生成标题'), ('image', '生成图片')], max_length=32, verbose_name='操作类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='points_cost',
|
||||
field=models.BigIntegerField(default=0, verbose_name='扣费点数'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='prompt',
|
||||
field=models.TextField(blank=True, verbose_name='提示词'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='resolution',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='分辨率'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='result_ref',
|
||||
field=models.TextField(blank=True, verbose_name='结果引用'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='result_summary',
|
||||
field=models.TextField(blank=True, verbose_name='结果摘要'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('pending', '待处理'), ('success', '成功'), ('failed', '失败')], default='pending', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='upstream_latency_ms',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='上游耗时毫秒'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='callrecord',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='call_records', to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exchangerate',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exchangerate',
|
||||
name='currency',
|
||||
field=models.CharField(default='CNY', max_length=10, verbose_name='币种'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exchangerate',
|
||||
name='effective_from',
|
||||
field=models.DateTimeField(verbose_name='生效时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exchangerate',
|
||||
name='is_active',
|
||||
field=models.BooleanField(default=True, verbose_name='启用'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exchangerate',
|
||||
name='note',
|
||||
field=models.CharField(blank=True, max_length=255, verbose_name='备注'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exchangerate',
|
||||
name='points_per_unit',
|
||||
field=models.DecimalField(decimal_places=4, max_digits=12, verbose_name='每单位金额点数'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exchangerate',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='balance_after',
|
||||
field=models.BigIntegerField(verbose_name='变动后余额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='change_type',
|
||||
field=models.CharField(choices=[('recharge', '充值'), ('consume', '消费'), ('adjust', '调整'), ('refund', '退款')], max_length=20, verbose_name='变动类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='points_delta',
|
||||
field=models.BigIntegerField(verbose_name='点数变动'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='reason',
|
||||
field=models.TextField(blank=True, verbose_name='原因'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='ref_call',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ledger_entries', to='billing.callrecord', verbose_name='关联调用'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='ref_order_id',
|
||||
field=models.PositiveBigIntegerField(blank=True, null=True, verbose_name='关联订单 ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pointsledger',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='points_ledger_entries', to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pricingrule',
|
||||
name='alias',
|
||||
field=models.CharField(max_length=64, verbose_name='能力别名'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pricingrule',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pricingrule',
|
||||
name='is_active',
|
||||
field=models.BooleanField(default=True, verbose_name='启用'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pricingrule',
|
||||
name='operation_type',
|
||||
field=models.CharField(choices=[('title', '生成标题'), ('image', '生成图片')], max_length=32, verbose_name='操作类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pricingrule',
|
||||
name='points_cost',
|
||||
field=models.BigIntegerField(verbose_name='点数单价'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pricingrule',
|
||||
name='resolution',
|
||||
field=models.CharField(blank=True, default='', help_text='留空表示该别名所有分辨率通用。', max_length=32, verbose_name='分辨率'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pricingrule',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='amount_money',
|
||||
field=models.DecimalField(decimal_places=2, max_digits=12, verbose_name='金额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='code_url',
|
||||
field=models.TextField(blank=True, verbose_name='二维码票据'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='currency',
|
||||
field=models.CharField(default='CNY', max_length=10, verbose_name='币种'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='exchange_rate',
|
||||
field=models.DecimalField(decimal_places=4, max_digits=12, verbose_name='锁定汇率'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='expires_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='过期时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='order_no',
|
||||
field=models.CharField(max_length=64, unique=True, verbose_name='订单号'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='paid_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='支付时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='pay_method',
|
||||
field=models.CharField(choices=[('weixin', '微信'), ('alipay', '支付宝')], max_length=20, verbose_name='支付方式'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='payment_txn_no',
|
||||
field=models.CharField(blank=True, max_length=128, verbose_name='支付交易号'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='points_granted',
|
||||
field=models.BigIntegerField(verbose_name='到账点数'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('pending', '待支付'), ('paid', '已支付'), ('failed', '失败'), ('expired', '已过期')], default='pending', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rechargeorder',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='recharge_orders', to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||
),
|
||||
]
|
||||
+67
-59
@@ -12,43 +12,46 @@ def normalize_resolution(value: str | None) -> str:
|
||||
|
||||
class CallRecord(models.Model):
|
||||
class OperationType(models.TextChoices):
|
||||
TITLE = "title", "Generate title"
|
||||
IMAGE = "image", "Generate image"
|
||||
TITLE = "title", "生成标题"
|
||||
IMAGE = "image", "生成图片"
|
||||
|
||||
class Status(models.TextChoices):
|
||||
PENDING = "pending", "Pending"
|
||||
SUCCESS = "success", "Success"
|
||||
FAILED = "failed", "Failed"
|
||||
PENDING = "pending", "待处理"
|
||||
SUCCESS = "success", "成功"
|
||||
FAILED = "failed", "失败"
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name="用户",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="call_records",
|
||||
)
|
||||
api_key = models.ForeignKey(
|
||||
ApiKey,
|
||||
verbose_name="API 密钥",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="call_records",
|
||||
)
|
||||
operation_type = models.CharField(max_length=32, choices=OperationType.choices)
|
||||
alias = models.CharField(max_length=64, blank=True)
|
||||
model_used = models.CharField(max_length=128, blank=True)
|
||||
resolution = models.CharField(max_length=32, blank=True)
|
||||
prompt = models.TextField(blank=True)
|
||||
points_cost = models.BigIntegerField(default=0)
|
||||
operation_type = models.CharField("操作类型", max_length=32, choices=OperationType.choices)
|
||||
alias = models.CharField("能力别名", max_length=64, blank=True)
|
||||
model_used = models.CharField("实际模型", max_length=128, blank=True)
|
||||
resolution = models.CharField("分辨率", max_length=32, blank=True)
|
||||
prompt = models.TextField("提示词", blank=True)
|
||||
points_cost = models.BigIntegerField("扣费点数", default=0)
|
||||
status = models.CharField(
|
||||
"状态",
|
||||
max_length=20,
|
||||
choices=Status.choices,
|
||||
default=Status.PENDING,
|
||||
)
|
||||
upstream_latency_ms = models.PositiveIntegerField(null=True, blank=True)
|
||||
error_message = models.TextField(blank=True)
|
||||
result_ref = models.TextField(blank=True)
|
||||
result_summary = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
upstream_latency_ms = models.PositiveIntegerField("上游耗时毫秒", null=True, blank=True)
|
||||
error_message = models.TextField("错误信息", blank=True)
|
||||
result_ref = models.TextField("结果引用", blank=True)
|
||||
result_summary = models.TextField("结果摘要", blank=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "call_record"
|
||||
@@ -73,18 +76,19 @@ class CallRecord(models.Model):
|
||||
|
||||
|
||||
class PricingRule(models.Model):
|
||||
operation_type = models.CharField(max_length=32, choices=CallRecord.OperationType.choices)
|
||||
alias = models.CharField(max_length=64)
|
||||
operation_type = models.CharField("操作类型", max_length=32, choices=CallRecord.OperationType.choices)
|
||||
alias = models.CharField("能力别名", max_length=64)
|
||||
resolution = models.CharField(
|
||||
"分辨率",
|
||||
max_length=32,
|
||||
blank=True,
|
||||
default="",
|
||||
help_text="Leave blank to apply to every resolution for this alias.",
|
||||
help_text="留空表示该别名所有分辨率通用。",
|
||||
)
|
||||
points_cost = models.BigIntegerField()
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
points_cost = models.BigIntegerField("点数单价")
|
||||
is_active = models.BooleanField("启用", default=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "pricing_rule"
|
||||
@@ -123,13 +127,13 @@ class PricingRule(models.Model):
|
||||
|
||||
|
||||
class ExchangeRate(models.Model):
|
||||
currency = models.CharField(max_length=10, default="CNY")
|
||||
points_per_unit = models.DecimalField(max_digits=12, decimal_places=4)
|
||||
is_active = models.BooleanField(default=True)
|
||||
effective_from = models.DateTimeField()
|
||||
note = models.CharField(max_length=255, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
currency = models.CharField("币种", max_length=10, default="CNY")
|
||||
points_per_unit = models.DecimalField("每单位金额点数", max_digits=12, decimal_places=4)
|
||||
is_active = models.BooleanField("启用", default=True)
|
||||
effective_from = models.DateTimeField("生效时间")
|
||||
note = models.CharField("备注", max_length=255, blank=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "exchange_rate"
|
||||
@@ -162,37 +166,39 @@ class ExchangeRate(models.Model):
|
||||
|
||||
class RechargeOrder(models.Model):
|
||||
class PayMethod(models.TextChoices):
|
||||
WEIXIN = "weixin", "Weixin"
|
||||
ALIPAY = "alipay", "Alipay"
|
||||
WEIXIN = "weixin", "微信"
|
||||
ALIPAY = "alipay", "支付宝"
|
||||
|
||||
class Status(models.TextChoices):
|
||||
PENDING = "pending", "Pending"
|
||||
PAID = "paid", "Paid"
|
||||
FAILED = "failed", "Failed"
|
||||
EXPIRED = "expired", "Expired"
|
||||
PENDING = "pending", "待支付"
|
||||
PAID = "paid", "已支付"
|
||||
FAILED = "failed", "失败"
|
||||
EXPIRED = "expired", "已过期"
|
||||
|
||||
order_no = models.CharField(max_length=64, unique=True)
|
||||
order_no = models.CharField("订单号", max_length=64, unique=True)
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name="用户",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="recharge_orders",
|
||||
)
|
||||
amount_money = models.DecimalField(max_digits=12, decimal_places=2)
|
||||
currency = models.CharField(max_length=10, default="CNY")
|
||||
pay_method = models.CharField(max_length=20, choices=PayMethod.choices)
|
||||
exchange_rate = models.DecimalField(max_digits=12, decimal_places=4)
|
||||
points_granted = models.BigIntegerField()
|
||||
amount_money = models.DecimalField("金额", max_digits=12, decimal_places=2)
|
||||
currency = models.CharField("币种", max_length=10, default="CNY")
|
||||
pay_method = models.CharField("支付方式", max_length=20, choices=PayMethod.choices)
|
||||
exchange_rate = models.DecimalField("锁定汇率", max_digits=12, decimal_places=4)
|
||||
points_granted = models.BigIntegerField("到账点数")
|
||||
status = models.CharField(
|
||||
"状态",
|
||||
max_length=20,
|
||||
choices=Status.choices,
|
||||
default=Status.PENDING,
|
||||
)
|
||||
code_url = models.TextField(blank=True)
|
||||
expires_at = models.DateTimeField(null=True, blank=True)
|
||||
payment_txn_no = models.CharField(max_length=128, blank=True)
|
||||
paid_at = models.DateTimeField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
code_url = models.TextField("二维码票据", blank=True)
|
||||
expires_at = models.DateTimeField("过期时间", null=True, blank=True)
|
||||
payment_txn_no = models.CharField("支付交易号", max_length=128, blank=True)
|
||||
paid_at = models.DateTimeField("支付时间", null=True, blank=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "recharge_order"
|
||||
@@ -238,29 +244,31 @@ class RechargeOrder(models.Model):
|
||||
|
||||
class PointsLedger(models.Model):
|
||||
class ChangeType(models.TextChoices):
|
||||
RECHARGE = "recharge", "Recharge"
|
||||
CONSUME = "consume", "Consume"
|
||||
ADJUST = "adjust", "Adjust"
|
||||
REFUND = "refund", "Refund"
|
||||
RECHARGE = "recharge", "充值"
|
||||
CONSUME = "consume", "消费"
|
||||
ADJUST = "adjust", "调整"
|
||||
REFUND = "refund", "退款"
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name="用户",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="points_ledger_entries",
|
||||
)
|
||||
change_type = models.CharField(max_length=20, choices=ChangeType.choices)
|
||||
points_delta = models.BigIntegerField()
|
||||
balance_after = models.BigIntegerField()
|
||||
ref_order_id = models.PositiveBigIntegerField(null=True, blank=True)
|
||||
change_type = models.CharField("变动类型", max_length=20, choices=ChangeType.choices)
|
||||
points_delta = models.BigIntegerField("点数变动")
|
||||
balance_after = models.BigIntegerField("变动后余额")
|
||||
ref_order_id = models.PositiveBigIntegerField("关联订单 ID", null=True, blank=True)
|
||||
ref_call = models.ForeignKey(
|
||||
CallRecord,
|
||||
verbose_name="关联调用",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="ledger_entries",
|
||||
)
|
||||
reason = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
reason = models.TextField("原因", blank=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "points_ledger"
|
||||
|
||||
+3
-3
@@ -40,7 +40,7 @@ class WalletAdjustmentForm(forms.Form):
|
||||
@admin.register(User)
|
||||
class UserAdmin(DjangoUserAdmin):
|
||||
fieldsets = DjangoUserAdmin.fieldsets + (
|
||||
("Cmhub", {"fields": ("payment_user_id", "status", "created_at")}),
|
||||
("业务信息", {"fields": ("payment_user_id", "status", "created_at")}),
|
||||
)
|
||||
readonly_fields = ("created_at",)
|
||||
list_display = (
|
||||
@@ -58,7 +58,7 @@ class UserAdmin(DjangoUserAdmin):
|
||||
def get_queryset(self, request):
|
||||
return super().get_queryset(request).select_related("wallet")
|
||||
|
||||
@admin.display(description="points")
|
||||
@admin.display(description="点数余额")
|
||||
def wallet_points_balance(self, obj):
|
||||
wallet = getattr(obj, "wallet", None)
|
||||
return wallet.points_balance if wallet else 0
|
||||
@@ -191,7 +191,7 @@ class ApiKeyAdmin(admin.ModelAdmin):
|
||||
)
|
||||
ordering = ("-created_at", "-id")
|
||||
|
||||
@admin.display(description="key hash")
|
||||
@admin.display(description="Key 哈希")
|
||||
def key_hash_masked(self, obj):
|
||||
if not obj.key_hash:
|
||||
return ""
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-04 08:02
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0004_alter_apikey_options_alter_user_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='key_hash',
|
||||
field=models.CharField(editable=False, max_length=64, unique=True, verbose_name='API Key 哈希'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='key_prefix',
|
||||
field=models.CharField(editable=False, max_length=32, verbose_name='Key 前缀'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='last_used_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='最后使用时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, max_length=80, verbose_name='名称'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('active', '有效'), ('revoked', '已吊销')], default='active', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='apikey',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='api_keys', to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, unique=True, verbose_name='邮箱'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='payment_user_id',
|
||||
field=models.CharField(blank=True, help_text='外部支付系统用户标识,可为空。', max_length=128, verbose_name='支付系统用户 ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('active', '正常'), ('disabled', '禁用')], default='active', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userwallet',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userwallet',
|
||||
name='points_balance',
|
||||
field=models.BigIntegerField(default=0, verbose_name='点数余额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userwallet',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userwallet',
|
||||
name='user',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='wallet', to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||
),
|
||||
]
|
||||
+21
-16
@@ -12,21 +12,23 @@ from django.db.models import Q
|
||||
|
||||
class User(AbstractUser):
|
||||
class Status(models.TextChoices):
|
||||
ACTIVE = "active", "Active"
|
||||
DISABLED = "disabled", "Disabled"
|
||||
ACTIVE = "active", "正常"
|
||||
DISABLED = "disabled", "禁用"
|
||||
|
||||
email = models.EmailField("email address", unique=True)
|
||||
email = models.EmailField("邮箱", unique=True)
|
||||
payment_user_id = models.CharField(
|
||||
"支付系统用户 ID",
|
||||
max_length=128,
|
||||
blank=True,
|
||||
help_text="External payment system user identifier, if available.",
|
||||
help_text="外部支付系统用户标识,可为空。",
|
||||
)
|
||||
status = models.CharField(
|
||||
"状态",
|
||||
max_length=20,
|
||||
choices=Status.choices,
|
||||
default=Status.ACTIVE,
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
|
||||
@property
|
||||
def is_business_active(self) -> bool:
|
||||
@@ -41,12 +43,13 @@ class User(AbstractUser):
|
||||
class UserWallet(models.Model):
|
||||
user = models.OneToOneField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name="用户",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="wallet",
|
||||
)
|
||||
points_balance = models.BigIntegerField(default=0)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
points_balance = models.BigIntegerField("点数余额", default=0)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "user_wallet"
|
||||
@@ -68,25 +71,27 @@ class ApiKey(models.Model):
|
||||
KEY_PREFIX_LENGTH = 16
|
||||
|
||||
class Status(models.TextChoices):
|
||||
ACTIVE = "active", "Active"
|
||||
REVOKED = "revoked", "Revoked"
|
||||
ACTIVE = "active", "有效"
|
||||
REVOKED = "revoked", "已吊销"
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name="用户",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="api_keys",
|
||||
)
|
||||
name = models.CharField(max_length=80, blank=True)
|
||||
key_hash = models.CharField(max_length=64, unique=True, editable=False)
|
||||
key_prefix = models.CharField(max_length=32, editable=False)
|
||||
name = models.CharField("名称", max_length=80, blank=True)
|
||||
key_hash = models.CharField("API Key 哈希", max_length=64, unique=True, editable=False)
|
||||
key_prefix = models.CharField("Key 前缀", max_length=32, editable=False)
|
||||
status = models.CharField(
|
||||
"状态",
|
||||
max_length=20,
|
||||
choices=Status.choices,
|
||||
default=Status.ACTIVE,
|
||||
)
|
||||
last_used_at = models.DateTimeField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
last_used_at = models.DateTimeField("最后使用时间", null=True, blank=True)
|
||||
created_at = models.DateTimeField("创建时间", auto_now_add=True)
|
||||
updated_at = models.DateTimeField("更新时间", auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "api_key"
|
||||
|
||||
Reference in New Issue
Block a user