feat(01-01): aiapp 新增 CredentialSlot 单例模型
- 在 aiapp/models.py 末尾追加 CredentialSlot(不动 Bot / ChatMessage) - 字段:app_id CharField(128) / access_token CharField(512) / updated_at auto_now - 单例三件套:pk=1 + save() 钩子重定向 + get_solo() 类方法(1:1 复刻 AffinitySetting) - 不引入 gettext_lazy / created_at,沿用仓库中文 verbose_name 实操约定 - 覆盖需求 CRED-01 模型层
This commit is contained in:
parent
a9c25eb2ac
commit
30c7caff41
@ -50,3 +50,44 @@ class ChatMessage(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.sender}: {self.message[:50]} ({self.timestamp})"
|
||||
|
||||
|
||||
class CredentialSlot(models.Model):
|
||||
"""通用凭据槽位(单例)— Milestone v1.0 / Phase 1
|
||||
|
||||
全局唯一一条记录,存第三方服务(Kimi / 阿里云 / 火山等)的 APP ID + Access Token。
|
||||
通过 pk=1 + get_or_create + save() 钩子三件套保证单例:
|
||||
- 任何 .save() 在已有记录时把新对象 pk 改成现有那条
|
||||
- get_solo() 是单一访问入口(Phase 2/3 视图统一调用)
|
||||
- DB 层无额外约束,绕过 ORM 的 bulk_create / 原始 SQL 不在保护范围
|
||||
"""
|
||||
|
||||
app_id = models.CharField(
|
||||
'APP ID', max_length=128, blank=True, default='',
|
||||
help_text='第三方服务商分配的 APP ID;运营在 Admin 录入'
|
||||
)
|
||||
access_token = models.CharField(
|
||||
'Access Token', max_length=512, blank=True, default='',
|
||||
help_text='第三方服务商访问令牌;DB 明文存储,Admin 列表/查看态末 4 位脱敏'
|
||||
)
|
||||
updated_at = models.DateTimeField('更新时间', auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '凭据槽位'
|
||||
verbose_name_plural = '凭据槽位'
|
||||
|
||||
def __str__(self):
|
||||
return f"凭据槽位 (updated {self.updated_at:%Y-%m-%d %H:%M})"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# 强制单例:新增时如果已有记录则覆盖到现有 pk
|
||||
if not self.pk and CredentialSlot.objects.exists():
|
||||
existing = CredentialSlot.objects.first()
|
||||
self.pk = existing.pk
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_solo(cls):
|
||||
"""获取单例实例,不存在则用默认值创建(pk=1)"""
|
||||
instance, _ = cls.objects.get_or_create(pk=1)
|
||||
return instance
|
||||
Loading…
x
Reference in New Issue
Block a user