diff --git a/README.md b/README.md index 1563a66..e7b8730 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ - admin/admin123 - chenze/admin123 -演示地址:https://demo.ywwuzi.cn +体验地址:https://demo.ywwuzi.cn 文档地址:https://docs.ywwuzi.cn diff --git a/backend/ai/choices.py b/backend/ai/choices.py index d0d8dd3..07eb425 100644 --- a/backend/ai/choices.py +++ b/backend/ai/choices.py @@ -18,4 +18,9 @@ class PlatformChoices(models.TextChoices): ZHIPU = 'ZhiPu', '智谱' MINIMAX = 'MiniMax', 'MiniMax' MOONSHOT = 'Moonshot', '月之暗灭' - BAICHUAN = 'BaiChuan', '百川智能' \ No newline at end of file + BAICHUAN = 'BaiChuan', '百川智能' + + +class MessageType(models.TextChoices): + USER = 'user', '用户发送' + ASSISTANT = 'assistant', '模型回复' \ No newline at end of file diff --git a/backend/ai/migrations/0001_initial.py b/backend/ai/migrations/0001_initial.py index f1bcd6c..dc69a27 100644 --- a/backend/ai/migrations/0001_initial.py +++ b/backend/ai/migrations/0001_initial.py @@ -1,6 +1,7 @@ -# Generated by Django 5.2.1 on 2025-07-10 03:03 +# Generated by Django 5.2.1 on 2025-07-11 07:44 import django.db.models.deletion +from django.conf import settings from django.db import migrations, models @@ -8,7 +9,9 @@ class Migration(migrations.Migration): initial = True - dependencies = [] + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] operations = [ migrations.CreateModel( @@ -31,6 +34,7 @@ class Migration(migrations.Migration): help_text="备注", max_length=256, null=True, + verbose_name="备注", ), ), ( @@ -41,6 +45,7 @@ class Migration(migrations.Migration): help_text="创建人", max_length=64, null=True, + verbose_name="创建人", ), ), ( @@ -51,6 +56,7 @@ class Migration(migrations.Migration): help_text="修改人", max_length=64, null=True, + verbose_name="修改人", ), ), ( @@ -60,6 +66,7 @@ class Migration(migrations.Migration): db_comment="修改时间", help_text="修改时间", null=True, + verbose_name="修改时间", ), ), ( @@ -69,15 +76,56 @@ class Migration(migrations.Migration): db_comment="创建时间", help_text="创建时间", null=True, + verbose_name="创建时间", ), ), ( "is_deleted", - models.BooleanField(db_comment="是否软删除", default=False), + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "name", + models.CharField( + db_comment="名称", max_length=255, verbose_name="名称" + ), + ), + ( + "platform", + models.CharField( + choices=[ + ("AzureOpenAI", "OpenAI 微软"), + ("OpenAI", "OpenAI"), + ("Ollama", "Ollama"), + ("YiYan", "文心一言"), + ("XingHuo", "讯飞星火"), + ("TongYi", "通义千问"), + ("StableDiffusion", "StableDiffusion"), + ("Midjourney", "Midjourney"), + ("Suno", "Suno"), + ("DeepSeek", "DeepSeek"), + ("DouBao", "字节豆包"), + ("HunYuan", "腾讯混元"), + ("SiliconFlow", "硅基流动"), + ("ZhiPu", "智谱"), + ("MiniMax", "MiniMax"), + ("Moonshot", "月之暗灭"), + ("BaiChuan", "百川智能"), + ], + db_comment="平台", + max_length=100, + verbose_name="平台", + ), + ), + ( + "api_key", + models.CharField( + db_comment="密钥", max_length=255, verbose_name="密钥" + ), ), - ("name", models.CharField(db_comment="名称", max_length=255)), - ("platform", models.CharField(db_comment="平台", max_length=255)), - ("api_key", models.CharField(db_comment="密钥", max_length=255)), ( "url", models.CharField( @@ -85,6 +133,7 @@ class Migration(migrations.Migration): db_comment="自定义 API 地址", max_length=255, null=True, + verbose_name="自定义 API 地址", ), ), ( @@ -103,6 +152,108 @@ class Migration(migrations.Migration): "db_table": "ai_api_key", }, ), + migrations.CreateModel( + name="Tool", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "remark", + models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + ( + "is_deleted", + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "name", + models.CharField( + db_comment="工具名称", max_length=128, verbose_name="工具名称" + ), + ), + ( + "description", + models.CharField( + blank=True, + db_comment="工具描述", + max_length=256, + null=True, + verbose_name="工具描述", + ), + ), + ( + "status", + models.SmallIntegerField( + db_comment="状态", default=0, verbose_name="状态" + ), + ), + ], + options={ + "verbose_name": "AI 工具", + "verbose_name_plural": "AI 工具", + "db_table": "ai_tool", + }, + ), migrations.CreateModel( name="AIModel", fields=[ @@ -123,6 +274,7 @@ class Migration(migrations.Migration): help_text="备注", max_length=256, null=True, + verbose_name="备注", ), ), ( @@ -133,6 +285,7 @@ class Migration(migrations.Migration): help_text="创建人", max_length=64, null=True, + verbose_name="创建人", ), ), ( @@ -143,6 +296,7 @@ class Migration(migrations.Migration): help_text="修改人", max_length=64, null=True, + verbose_name="修改人", ), ), ( @@ -152,6 +306,7 @@ class Migration(migrations.Migration): db_comment="修改时间", help_text="修改时间", null=True, + verbose_name="修改时间", ), ), ( @@ -161,14 +316,29 @@ class Migration(migrations.Migration): db_comment="创建时间", help_text="创建时间", null=True, + verbose_name="创建时间", ), ), ( "is_deleted", - models.BooleanField(db_comment="是否软删除", default=False), + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "name", + models.CharField( + db_comment="模型名字", max_length=64, verbose_name="模型名字" + ), + ), + ( + "sort", + models.IntegerField( + db_comment="排序", default=0, verbose_name="排序" + ), ), - ("name", models.CharField(db_comment="模型名字", max_length=64)), - ("sort", models.IntegerField(db_comment="排序", default=0)), ( "status", models.SmallIntegerField( @@ -178,22 +348,43 @@ class Migration(migrations.Migration): verbose_name="状态", ), ), - ("platform", models.CharField(db_comment="模型平台", max_length=32)), - ("model", models.CharField(db_comment="模型标识", max_length=64)), + ( + "platform", + models.CharField( + db_comment="模型平台", max_length=32, verbose_name="模型平台" + ), + ), + ( + "model", + models.CharField( + db_comment="模型标识", max_length=64, verbose_name="模型标识" + ), + ), ( "temperature", - models.FloatField(blank=True, db_comment="温度参数", null=True), + models.FloatField( + blank=True, + db_comment="温度参数", + null=True, + verbose_name="温度参数", + ), ), ( "max_tokens", models.IntegerField( - blank=True, db_comment="单条回复的最大 Token 数量", null=True + blank=True, + db_comment="单条回复的最大 Token 数量", + null=True, + verbose_name="单条回复的最大 Token 数量", ), ), ( "max_contexts", models.IntegerField( - blank=True, db_comment="上下文的最大 Message 数量", null=True + blank=True, + db_comment="上下文的最大 Message 数量", + null=True, + verbose_name="上下文的最大 Message 数量", ), ), ( @@ -202,13 +393,938 @@ class Migration(migrations.Migration): db_comment="API 秘钥编号", on_delete=django.db.models.deletion.CASCADE, to="ai.aiapikey", + verbose_name="API 秘钥编号", ), ), ], options={ - "verbose_name": "AI 模型", - "verbose_name_plural": "AI 模型", + "verbose_name": "模型配置", + "verbose_name_plural": "模型配置", "db_table": "ai_model", }, ), + migrations.CreateModel( + name="ChatRole", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "remark", + models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + ( + "is_deleted", + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "name", + models.CharField( + db_comment="角色名称", max_length=128, verbose_name="角色名称" + ), + ), + ( + "avatar", + models.CharField( + db_comment="头像", max_length=256, verbose_name="头像" + ), + ), + ( + "description", + models.CharField( + db_comment="角色描述", max_length=256, verbose_name="角色描述" + ), + ), + ( + "status", + models.SmallIntegerField( + choices=[(0, "禁用"), (1, "启用")], + db_comment="状态", + default=0, + verbose_name="状态", + ), + ), + ( + "sort", + models.IntegerField( + db_comment="角色排序", default=0, verbose_name="角色排序" + ), + ), + ( + "public_status", + models.BooleanField( + db_comment="是否公开", default=False, verbose_name="是否公开" + ), + ), + ( + "category", + models.CharField( + blank=True, + db_comment="角色类别", + max_length=32, + null=True, + verbose_name="角色类别", + ), + ), + ( + "system_message", + models.CharField( + blank=True, + db_comment="角色上下文", + max_length=1024, + null=True, + verbose_name="角色上下文", + ), + ), + ( + "model_id", + models.ForeignKey( + db_column="model_id", + db_comment="向量模型编号", + on_delete=django.db.models.deletion.CASCADE, + to="ai.aimodel", + verbose_name="向量模型编号", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + db_comment="用户编号", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="用户", + ), + ), + ], + options={ + "verbose_name": "AI 聊天角色", + "verbose_name_plural": "AI 聊天角色", + "db_table": "ai_chat_role", + }, + ), + migrations.CreateModel( + name="ChatMessage", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "remark", + models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + ( + "is_deleted", + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "conversation_id", + models.BigIntegerField( + db_comment="对话编号", verbose_name="对话编号" + ), + ), + ( + "model", + models.CharField( + db_comment="模型标识", max_length=32, verbose_name="模型标识" + ), + ), + ( + "type", + models.CharField( + choices=[("user", "用户发送"), ("assistant", "模型回复")], + db_comment="消息类型", + max_length=16, + verbose_name="消息类型", + ), + ), + ( + "reply_id", + models.BigIntegerField( + blank=True, + db_comment="回复编号", + null=True, + verbose_name="回复编号", + ), + ), + ( + "content", + models.CharField( + db_comment="消息内容", max_length=2048, verbose_name="消息内容" + ), + ), + ( + "use_context", + models.BooleanField( + db_comment="是否携带上下文", + default=False, + verbose_name="是否携带上下文", + ), + ), + ( + "segment_ids", + models.CharField( + blank=True, + db_comment="段落编号数组", + max_length=2048, + null=True, + verbose_name="段落编号数组", + ), + ), + ( + "model_id", + models.ForeignKey( + db_column="model_id", + db_comment="向量模型编号", + on_delete=django.db.models.deletion.CASCADE, + to="ai.aimodel", + verbose_name="向量模型编号", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + db_comment="用户编号", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="用户", + ), + ), + ( + "role", + models.ForeignKey( + blank=True, + db_comment="聊天角色", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="ai.chatrole", + verbose_name="聊天角色", + ), + ), + ], + options={ + "verbose_name": "AI 聊天消息", + "verbose_name_plural": "AI 聊天消息", + "db_table": "ai_chat_message", + }, + ), + migrations.CreateModel( + name="ChatConversation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "remark", + models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + ( + "is_deleted", + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "title", + models.CharField( + db_comment="对话标题", max_length=256, verbose_name="对话标题" + ), + ), + ( + "pinned", + models.BooleanField( + db_comment="是否置顶", default=False, verbose_name="是否置顶" + ), + ), + ( + "pinned_time", + models.DateTimeField( + blank=True, + db_comment="置顶时间", + null=True, + verbose_name="置顶时间", + ), + ), + ( + "model", + models.CharField( + db_comment="模型标识", max_length=32, verbose_name="模型标识" + ), + ), + ( + "system_message", + models.CharField( + blank=True, + db_comment="角色设定", + max_length=1024, + null=True, + verbose_name="角色设定", + ), + ), + ( + "temperature", + models.FloatField(db_comment="温度参数", verbose_name="温度参数"), + ), + ( + "max_tokens", + models.IntegerField( + db_comment="单条回复的最大 Token 数量", + verbose_name="单条回复的最大 Token 数量", + ), + ), + ( + "max_contexts", + models.IntegerField( + db_comment="上下文的最大 Message 数量", + verbose_name="上下文的最大 Message 数量", + ), + ), + ( + "model_id", + models.ForeignKey( + db_column="model_id", + db_comment="向量模型编号", + on_delete=django.db.models.deletion.CASCADE, + to="ai.aimodel", + verbose_name="向量模型编号", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + db_comment="用户编号", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="用户", + ), + ), + ( + "role", + models.ForeignKey( + blank=True, + db_comment="聊天角色", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="ai.chatrole", + verbose_name="聊天角色", + ), + ), + ], + options={ + "verbose_name": "AI 聊天对话", + "verbose_name_plural": "AI 聊天对话", + "db_table": "ai_chat_conversation", + }, + ), + migrations.CreateModel( + name="Knowledge", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "remark", + models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + ( + "is_deleted", + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "name", + models.CharField( + db_comment="知识库名称", + max_length=255, + verbose_name="知识库名称", + ), + ), + ( + "description", + models.TextField( + blank=True, + db_comment="知识库描述", + null=True, + verbose_name="知识库描述", + ), + ), + ( + "embedding_model", + models.CharField( + db_comment="向量模型标识", + max_length=32, + verbose_name="向量模型标识", + ), + ), + ( + "top_k", + models.IntegerField( + db_comment="topK", default=0, verbose_name="topK" + ), + ), + ( + "similarity_threshold", + models.FloatField( + db_comment="相似度阈值", verbose_name="相似度阈值" + ), + ), + ( + "status", + models.SmallIntegerField( + choices=[(0, "禁用"), (1, "启用")], + db_comment="状态", + default=0, + verbose_name="状态", + ), + ), + ( + "embedding_model_id", + models.ForeignKey( + db_column="embedding_model_id", + db_comment="向量模型编号", + on_delete=django.db.models.deletion.CASCADE, + to="ai.aimodel", + verbose_name="向量模型编号", + ), + ), + ], + options={ + "verbose_name": "AI 知识库", + "verbose_name_plural": "AI 知识库", + "db_table": "ai_knowledge", + }, + ), + migrations.AddField( + model_name="chatrole", + name="knowledge", + field=models.ManyToManyField( + blank=True, + db_comment="关联的知识库", + related_name="roles", + to="ai.knowledge", + verbose_name="关联的知识库", + ), + ), + migrations.CreateModel( + name="KnowledgeDocument", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "remark", + models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + ( + "is_deleted", + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "name", + models.CharField( + db_comment="文档名称", max_length=255, verbose_name="文档名称" + ), + ), + ( + "url", + models.CharField( + db_comment="文件 URL", max_length=1024, verbose_name="文件 URL" + ), + ), + ("content", models.TextField(db_comment="内容", verbose_name="内容")), + ( + "content_length", + models.IntegerField(db_comment="字符数", verbose_name="字符数"), + ), + ( + "tokens", + models.IntegerField( + db_comment="token 数量", verbose_name="token 数量" + ), + ), + ( + "segment_max_tokens", + models.IntegerField( + db_comment="分片最大 Token 数", verbose_name="分片最大 Token 数" + ), + ), + ( + "retrieval_count", + models.IntegerField( + db_comment="召回次数", default=0, verbose_name="召回次数" + ), + ), + ( + "status", + models.SmallIntegerField( + choices=[(0, "禁用"), (1, "启用")], + db_comment="状态", + default=0, + verbose_name="状态", + ), + ), + ( + "knowledge", + models.ForeignKey( + db_comment="知识库", + on_delete=django.db.models.deletion.CASCADE, + related_name="documents", + to="ai.knowledge", + verbose_name="知识库", + ), + ), + ], + options={ + "verbose_name": "AI 知识库文档", + "verbose_name_plural": "AI 知识库文档", + "db_table": "ai_knowledge_document", + }, + ), + migrations.CreateModel( + name="KnowledgeSegment", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "remark", + models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + ( + "is_deleted", + models.BooleanField( + db_comment="是否软删除", + default=False, + verbose_name="是否软删除", + ), + ), + ( + "content", + models.TextField(db_comment="分段内容", verbose_name="分段内容"), + ), + ( + "content_length", + models.IntegerField(db_comment="字符数", verbose_name="字符数"), + ), + ( + "tokens", + models.IntegerField( + db_comment="token 数量", verbose_name="token 数量" + ), + ), + ( + "vector_id", + models.CharField( + blank=True, + db_comment="向量库的编号", + max_length=100, + null=True, + verbose_name="向量库的编号", + ), + ), + ( + "retrieval_count", + models.IntegerField( + db_comment="召回次数", default=0, verbose_name="召回次数" + ), + ), + ( + "status", + models.SmallIntegerField( + choices=[(0, "禁用"), (1, "启用")], + db_comment="状态", + default=0, + verbose_name="状态", + ), + ), + ( + "document", + models.ForeignKey( + db_comment="文档", + on_delete=django.db.models.deletion.CASCADE, + related_name="segments", + to="ai.knowledgedocument", + verbose_name="文档", + ), + ), + ( + "knowledge", + models.ForeignKey( + db_comment="知识库", + on_delete=django.db.models.deletion.CASCADE, + related_name="segments", + to="ai.knowledge", + verbose_name="知识库", + ), + ), + ], + options={ + "verbose_name": "AI 知识库分段", + "verbose_name_plural": "AI 知识库分段", + "db_table": "ai_knowledge_segment", + }, + ), + migrations.AddField( + model_name="chatrole", + name="tools", + field=models.ManyToManyField( + blank=True, + db_comment="关联的工具", + related_name="roles", + to="ai.tool", + verbose_name="关联的工具", + ), + ), ] diff --git a/backend/ai/migrations/0002_alter_aiapikey_api_key_alter_aiapikey_create_time_and_more.py b/backend/ai/migrations/0002_alter_aiapikey_api_key_alter_aiapikey_create_time_and_more.py deleted file mode 100644 index 7333bd2..0000000 --- a/backend/ai/migrations/0002_alter_aiapikey_api_key_alter_aiapikey_create_time_and_more.py +++ /dev/null @@ -1,262 +0,0 @@ -# Generated by Django 5.2.1 on 2025-07-10 04:07 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("ai", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="aiapikey", - name="api_key", - field=models.CharField( - db_comment="密钥", max_length=255, verbose_name="密钥" - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="create_time", - field=models.DateTimeField( - auto_now_add=True, - db_comment="创建时间", - help_text="创建时间", - null=True, - verbose_name="创建时间", - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="creator", - field=models.CharField( - blank=True, - db_comment="创建人", - help_text="创建人", - max_length=64, - null=True, - verbose_name="创建人", - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="is_deleted", - field=models.BooleanField( - db_comment="是否软删除", default=False, verbose_name="是否软删除" - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="modifier", - field=models.CharField( - blank=True, - db_comment="修改人", - help_text="修改人", - max_length=64, - null=True, - verbose_name="修改人", - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="name", - field=models.CharField( - db_comment="名称", max_length=255, verbose_name="名称" - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="platform", - field=models.CharField( - choices=[ - ("AzureOpenAI", "OpenAI 微软"), - ("OpenAI", "OpenAI"), - ("Ollama", "Ollama"), - ("YiYan", "文心一言"), - ("XingHuo", "讯飞星火"), - ("TongYi", "通义千问"), - ("StableDiffusion", "StableDiffusion"), - ("Midjourney", "Midjourney"), - ("Suno", "Suno"), - ("DeepSeek", "DeepSeek"), - ("DouBao", "字节豆包"), - ("HunYuan", "腾讯混元"), - ("SiliconFlow", "硅基流动"), - ("ZhiPu", "智谱"), - ("MiniMax", "MiniMax"), - ("Moonshot", "月之暗灭"), - ("BaiChuan", "百川智能"), - ], - db_comment="平台", - max_length=100, - verbose_name="平台", - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="remark", - field=models.CharField( - blank=True, - db_comment="备注", - help_text="备注", - max_length=256, - null=True, - verbose_name="备注", - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="update_time", - field=models.DateTimeField( - auto_now=True, - db_comment="修改时间", - help_text="修改时间", - null=True, - verbose_name="修改时间", - ), - ), - migrations.AlterField( - model_name="aiapikey", - name="url", - field=models.CharField( - blank=True, - db_comment="自定义 API 地址", - max_length=255, - null=True, - verbose_name="自定义 API 地址", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="create_time", - field=models.DateTimeField( - auto_now_add=True, - db_comment="创建时间", - help_text="创建时间", - null=True, - verbose_name="创建时间", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="creator", - field=models.CharField( - blank=True, - db_comment="创建人", - help_text="创建人", - max_length=64, - null=True, - verbose_name="创建人", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="is_deleted", - field=models.BooleanField( - db_comment="是否软删除", default=False, verbose_name="是否软删除" - ), - ), - migrations.AlterField( - model_name="aimodel", - name="key", - field=models.ForeignKey( - db_comment="API 秘钥编号", - on_delete=django.db.models.deletion.CASCADE, - to="ai.aiapikey", - verbose_name="API 秘钥编号", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="max_contexts", - field=models.IntegerField( - blank=True, - db_comment="上下文的最大 Message 数量", - null=True, - verbose_name="上下文的最大 Message 数量", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="max_tokens", - field=models.IntegerField( - blank=True, - db_comment="单条回复的最大 Token 数量", - null=True, - verbose_name="单条回复的最大 Token 数量", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="model", - field=models.CharField( - db_comment="模型标识", max_length=64, verbose_name="模型标识" - ), - ), - migrations.AlterField( - model_name="aimodel", - name="modifier", - field=models.CharField( - blank=True, - db_comment="修改人", - help_text="修改人", - max_length=64, - null=True, - verbose_name="修改人", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="name", - field=models.CharField( - db_comment="模型名字", max_length=64, verbose_name="模型名字" - ), - ), - migrations.AlterField( - model_name="aimodel", - name="platform", - field=models.CharField( - db_comment="模型平台", max_length=32, verbose_name="模型平台" - ), - ), - migrations.AlterField( - model_name="aimodel", - name="remark", - field=models.CharField( - blank=True, - db_comment="备注", - help_text="备注", - max_length=256, - null=True, - verbose_name="备注", - ), - ), - migrations.AlterField( - model_name="aimodel", - name="sort", - field=models.IntegerField( - db_comment="排序", default=0, verbose_name="排序" - ), - ), - migrations.AlterField( - model_name="aimodel", - name="temperature", - field=models.FloatField( - blank=True, db_comment="温度参数", null=True, verbose_name="温度参数" - ), - ), - migrations.AlterField( - model_name="aimodel", - name="update_time", - field=models.DateTimeField( - auto_now=True, - db_comment="修改时间", - help_text="修改时间", - null=True, - verbose_name="修改时间", - ), - ), - ] diff --git a/backend/ai/migrations/0003_tool_alter_aimodel_options.py b/backend/ai/migrations/0003_tool_alter_aimodel_options.py deleted file mode 100644 index 6ab662c..0000000 --- a/backend/ai/migrations/0003_tool_alter_aimodel_options.py +++ /dev/null @@ -1,105 +0,0 @@ -# Generated by Django 5.2.1 on 2025-07-11 02:56 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("ai", "0002_alter_aiapikey_api_key_alter_aiapikey_create_time_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="Tool", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "remark", - models.CharField( - blank=True, - db_comment="备注", - help_text="备注", - max_length=256, - null=True, - verbose_name="备注", - ), - ), - ( - "creator", - models.CharField( - blank=True, - db_comment="创建人", - help_text="创建人", - max_length=64, - null=True, - verbose_name="创建人", - ), - ), - ( - "modifier", - models.CharField( - blank=True, - db_comment="修改人", - help_text="修改人", - max_length=64, - null=True, - verbose_name="修改人", - ), - ), - ( - "update_time", - models.DateTimeField( - auto_now=True, - db_comment="修改时间", - help_text="修改时间", - null=True, - verbose_name="修改时间", - ), - ), - ( - "create_time", - models.DateTimeField( - auto_now_add=True, - db_comment="创建时间", - help_text="创建时间", - null=True, - verbose_name="创建时间", - ), - ), - ( - "is_deleted", - models.BooleanField( - db_comment="是否软删除", - default=False, - verbose_name="是否软删除", - ), - ), - ("name", models.CharField(max_length=128, verbose_name="工具名称")), - ( - "description", - models.CharField( - blank=True, max_length=256, null=True, verbose_name="工具描述" - ), - ), - ("status", models.SmallIntegerField(verbose_name="状态")), - ], - options={ - "verbose_name": "AI 工具", - "verbose_name_plural": "AI 工具", - "db_table": "ai_tool", - }, - ), - migrations.AlterModelOptions( - name="aimodel", - options={"verbose_name": "模型配置", "verbose_name_plural": "模型配置"}, - ), - ] diff --git a/backend/ai/models.py b/backend/ai/models.py index 2278402..26c487f 100644 --- a/backend/ai/models.py +++ b/backend/ai/models.py @@ -1,6 +1,7 @@ from django.db import models -from ai.choices import PlatformChoices +from ai.choices import PlatformChoices, MessageType +from backend import settings from utils.models import CommonStatus, CoreModel class AIApiKey(CoreModel): @@ -63,9 +64,9 @@ class AIModel(CoreModel): class Tool(CoreModel): """ AI 工具表 """ - name = models.CharField(max_length=128, verbose_name="工具名称") - description = models.CharField(max_length=256, null=True, blank=True, verbose_name="工具描述") - status = models.SmallIntegerField(verbose_name="状态") + name = models.CharField(max_length=128, verbose_name="工具名称", db_comment="工具名称") + description = models.CharField(max_length=256, null=True, blank=True, verbose_name="工具描述", db_comment="工具描述") + status = models.SmallIntegerField(verbose_name="状态", db_comment="状态", default=0) class Meta: db_table = "ai_tool" @@ -73,4 +74,258 @@ class Tool(CoreModel): verbose_name_plural = verbose_name def __str__(self): - return self.name \ No newline at end of file + return self.name + + +class Knowledge(CoreModel): + """ AI 知识库表 """ + name = models.CharField(max_length=255, verbose_name="知识库名称", db_comment="知识库名称") + description = models.TextField(null=True, blank=True, verbose_name="知识库描述", db_comment="知识库描述") + embedding_model_id = models.ForeignKey( + 'AIModel', + on_delete=models.CASCADE, + db_column='embedding_model_id', + db_comment='向量模型编号', verbose_name="向量模型编号" + ) + embedding_model = models.CharField(max_length=32, verbose_name="向量模型标识", db_comment="向量模型标识") + top_k = models.IntegerField(verbose_name="topK", db_comment="topK", default=0) + similarity_threshold = models.FloatField(verbose_name="相似度阈值", db_comment="相似度阈值") + status = models.SmallIntegerField( + choices=CommonStatus.choices, + default=CommonStatus.DISABLED, + verbose_name="状态", + db_comment="状态", + ) + + class Meta: + db_table = "ai_knowledge" + verbose_name = "AI 知识库" + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + +class KnowledgeDocument(CoreModel): + """ AI 知识库文档表 """ + knowledge = models.ForeignKey( + Knowledge, + on_delete=models.CASCADE, + related_name="documents", + verbose_name="知识库", + db_comment="知识库" + ) + name = models.CharField(max_length=255, verbose_name="文档名称", db_comment="文档名称") + url = models.CharField(max_length=1024, verbose_name="文件 URL", db_comment="文件 URL") + content = models.TextField(verbose_name="内容", db_comment="内容") + content_length = models.IntegerField(verbose_name="字符数", db_comment="字符数") + tokens = models.IntegerField(verbose_name="token 数量", db_comment="token 数量") + segment_max_tokens = models.IntegerField(verbose_name="分片最大 Token 数", db_comment="分片最大 Token 数") + retrieval_count = models.IntegerField(default=0, verbose_name="召回次数", db_comment="召回次数") + status = models.SmallIntegerField( + choices=CommonStatus.choices, + default=CommonStatus.DISABLED, + verbose_name="状态", + db_comment="状态", + ) + + class Meta: + db_table = "ai_knowledge_document" + verbose_name = "AI 知识库文档" + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + +class KnowledgeSegment(CoreModel): + """ AI 知识库分段表 """ + knowledge = models.ForeignKey( + Knowledge, + on_delete=models.CASCADE, + related_name="segments", + verbose_name="知识库", + db_comment="知识库" + ) + document = models.ForeignKey( + KnowledgeDocument, + on_delete=models.CASCADE, + related_name="segments", + verbose_name="文档", + db_comment="文档" + ) + content = models.TextField(verbose_name="分段内容", db_comment="分段内容") + content_length = models.IntegerField(verbose_name="字符数", db_comment="字符数") + tokens = models.IntegerField(verbose_name="token 数量", db_comment="token 数量") + vector_id = models.CharField( + max_length=100, + null=True, blank=True, + verbose_name="向量库的编号", + db_comment="向量库的编号" + ) + retrieval_count = models.IntegerField(default=0, verbose_name="召回次数", db_comment="召回次数") + status = models.SmallIntegerField( + choices=CommonStatus.choices, + default=CommonStatus.DISABLED, + verbose_name="状态", + db_comment="状态" + ) + + class Meta: + db_table = "ai_knowledge_segment" + verbose_name = "AI 知识库分段" + verbose_name_plural = verbose_name + + def __str__(self): + return f"Segment {self.id}" + + +class ChatRole(CoreModel): + """ AI 聊天角色表 """ + name = models.CharField(max_length=128, verbose_name="角色名称", db_comment="角色名称") + avatar = models.CharField(max_length=256, verbose_name="头像", db_comment="头像") + description = models.CharField(max_length=256, verbose_name="角色描述", db_comment="角色描述") + status = models.SmallIntegerField( + choices=CommonStatus.choices, + default=CommonStatus.DISABLED, + verbose_name="状态", + db_comment="状态" + ) + sort = models.IntegerField(default=0, verbose_name="角色排序", db_comment="角色排序") + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, blank=True, + on_delete=models.SET_NULL, + verbose_name="用户", + db_comment="用户编号" + ) + public_status = models.BooleanField(default=False, verbose_name="是否公开", db_comment="是否公开") + category = models.CharField(max_length=32, null=True, blank=True, verbose_name="角色类别", db_comment="角色类别") + model_id = models.ForeignKey( + 'AIModel', + on_delete=models.CASCADE, + db_column='model_id', + verbose_name="向量模型编号", + db_comment='向量模型编号' + ) + system_message = models.CharField( + max_length=1024, null=True, blank=True, + verbose_name="角色上下文", + db_comment="角色上下文" + ) + knowledge = models.ManyToManyField( + 'Knowledge', + blank=True, + related_name="roles", + verbose_name="关联的知识库", + db_comment="关联的知识库" + ) + tools = models.ManyToManyField( + 'Tool', + blank=True, + related_name="roles", + verbose_name="关联的工具", + db_comment="关联的工具" + ) + + class Meta: + db_table = "ai_chat_role" + verbose_name = "AI 聊天角色" + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + +class ChatConversation(CoreModel): + """ AI 聊天对话表 """ + title = models.CharField(max_length=256, verbose_name="对话标题", db_comment="对话标题") + pinned = models.BooleanField(default=False, verbose_name="是否置顶", db_comment="是否置顶") + pinned_time = models.DateTimeField(null=True, blank=True, verbose_name="置顶时间", db_comment="置顶时间") + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, blank=True, + on_delete=models.SET_NULL, + verbose_name="用户", + db_comment="用户编号" + ) + role = models.ForeignKey( + 'ChatRole', + null=True, blank=True, + on_delete=models.SET_NULL, + verbose_name="聊天角色", + db_comment="聊天角色" + ) + model_id = models.ForeignKey( + 'AIModel', + on_delete=models.CASCADE, + db_column='model_id', + verbose_name="向量模型编号", + db_comment='向量模型编号' + ) + model = models.CharField(max_length=32, verbose_name="模型标识", db_comment="模型标识") + system_message = models.CharField( + max_length=1024, null=True, blank=True, + verbose_name="角色设定", + db_comment="角色设定" + ) + temperature = models.FloatField(verbose_name="温度参数", db_comment="温度参数") + max_tokens = models.IntegerField(verbose_name="单条回复的最大 Token 数量", db_comment="单条回复的最大 Token 数量") + max_contexts = models.IntegerField(verbose_name="上下文的最大 Message 数量", db_comment="上下文的最大 Message 数量") + + class Meta: + db_table = "ai_chat_conversation" + verbose_name = "AI 聊天对话" + verbose_name_plural = verbose_name + + def __str__(self): + return self.title + + +class ChatMessage(CoreModel): + """ AI 聊天消息表 """ + conversation_id = models.BigIntegerField(verbose_name="对话编号", db_comment="对话编号") + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, blank=True, + on_delete=models.SET_NULL, + verbose_name="用户", + db_comment="用户编号" + ) + role = models.ForeignKey( + 'ChatRole', + null=True, blank=True, + on_delete=models.SET_NULL, + verbose_name="聊天角色", + db_comment="聊天角色" + ) + model = models.CharField(max_length=32, verbose_name="模型标识", db_comment="模型标识") + model_id = models.ForeignKey( + 'AIModel', + on_delete=models.CASCADE, + db_column='model_id', + verbose_name="向量模型编号", + db_comment='向量模型编号' + ) + type = models.CharField( + max_length=16, + choices=MessageType.choices, + verbose_name="消息类型", + db_comment="消息类型", + ) + reply_id = models.BigIntegerField(null=True, blank=True, verbose_name="回复编号", db_comment="回复编号") + content = models.CharField(max_length=2048, verbose_name="消息内容", db_comment="消息内容") + use_context = models.BooleanField(default=False, verbose_name="是否携带上下文", db_comment="是否携带上下文") + segment_ids = models.CharField( + max_length=2048, null=True, blank=True, + verbose_name="段落编号数组", + db_comment="段落编号数组" + ) + + class Meta: + db_table = "ai_chat_message" + verbose_name = "AI 聊天消息" + verbose_name_plural = verbose_name + + def __str__(self): + return self.content[:30] diff --git a/backend/system/management/commands/generate_crud.py b/backend/system/management/commands/generate_crud.py index 65948f8..50e6ec4 100644 --- a/backend/system/management/commands/generate_crud.py +++ b/backend/system/management/commands/generate_crud.py @@ -173,11 +173,9 @@ class Command(BaseCommand): field_config = self.generate_grid_form_field(field) if field_config: grid_form_fields.append(field_config) - + # 生成 useColumns - columns = [] - for field in business_fields + core_fields: - columns.append(f" {{\n field: '{field.name}',\n title: '{getattr(field, 'verbose_name', field.name)}',\n }},") + columns = self.get_columns_code(business_fields + core_fields) context = get_context(app_name, model_name, model, model_name_snake) context['form_fields'] = '\n'.join(form_fields) context['grid_form_fields'] = '\n'.join(grid_form_fields) @@ -218,28 +216,41 @@ class Command(BaseCommand): return 'any' def generate_form_field(self, field): - """生成表单字段配置""" field_name = field.name field_label = getattr(field, 'verbose_name', field_name) + if field_name == 'status': + return "{ component: 'RadioGroup', componentProps: { buttonStyle: 'solid', options: [{ label: $t('common.enabled'), value: 1 }, { label: $t('common.disabled'), value: 0 }], optionType: 'button' }, defaultValue: 1, fieldName: 'status', label: $t('system.status') }," if isinstance(field, models.CharField): - return f''' {{\n component: 'Input',\n fieldName: '{field_name}',\n label: '{field_label}',\n rules: z\n .string()\n .min(1, $t('ui.formRules.required', ['{field_label}']))\n .max(100, $t('ui.formRules.maxLength', ['{field_label}', 100])),\n }},''' + return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}', rules: z.string().min(1, $t('ui.formRules.required', ['{field_label}'])).max(100, $t('ui.formRules.maxLength', ['{field_label}', 100])) }}," elif isinstance(field, models.TextField): - return f''' {{\n component: 'Input',\n componentProps: {{\n rows: 3,\n showCount: true,\n }},\n fieldName: '{field_name}',\n label: '{field_label}',\n rules: z\n .string()\n .max(500, $t('ui.formRules.maxLength', ['{field_label}', 500]))\n .optional(),\n }},''' + return f"{{ component: 'Input', componentProps: {{ rows: 3, showCount: true }}, fieldName: '{field_name}', label: '{field_label}', rules: z.string().max(500, $t('ui.formRules.maxLength', ['{field_label}', 500])).optional() }}," elif isinstance(field, models.IntegerField): - return f''' {{\n component: 'InputNumber',\n fieldName: '{field_name}',\n label: '{field_label}',\n }},''' + return f"{{ component: 'InputNumber', fieldName: '{field_name}', label: '{field_label}' }}," elif isinstance(field, models.BooleanField): - return f''' {{\n component: 'RadioGroup',\n componentProps: {{\n buttonStyle: 'solid',\n options: [\n {{ label: '开启', value: 1 }},\n {{ label: '关闭', value: 0 }},\n ],\n optionType: 'button',\n }},\n defaultValue: 1,\n fieldName: '{field_name}',\n label: '{field_label}',\n }},''' + return f"{{ component: 'RadioGroup', componentProps: {{ buttonStyle: 'solid', options: [{{ label: '开启', value: 1 }}, {{ label: '关闭', value: 0 }}], optionType: 'button' }}, defaultValue: 1, fieldName: '{field_name}', label: '{field_label}' }}," else: - return f''' {{\n component: 'Input',\n fieldName: '{field_name}',\n label: '{field_label}',\n }},''' + return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}' }}," def generate_grid_form_field(self, field): field_name = field.name field_label = getattr(field, 'verbose_name', field_name) - # 查询表单一般只需要 component/fieldName/label,不需要 rules + if field_name == 'status': + return "{ component: 'Select', fieldName: 'status', label: '状态', componentProps: { allowClear: true, options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }] } }," if isinstance(field, models.CharField): - return f''' {{\n component: 'Input',\n fieldName: '{field_name}',\n label: '{field_label}',\n }},''' + return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}' }}," elif isinstance(field, models.IntegerField): - return f''' {{\n component: 'InputNumber',\n fieldName: '{field_name}',\n label: '{field_label}',\n }},''' - # 其他类型同理 + return f"{{ component: 'InputNumber', fieldName: '{field_name}', label: '{field_label}' }}," else: - return f''' {{\n component: 'Input',\n fieldName: '{field_name}',\n label: '{field_label}',\n }},''' \ No newline at end of file + return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}' }}," + + def get_columns_code(self, fields): + columns = [] + for field in fields: + if field.name == 'status': + columns.append("{ field: 'status', title: '状态', cellRender: { name: 'CellTag' } },") + continue + if isinstance(field, (models.DateField, models.DateTimeField)): + columns.append(f"{{ field: '{field.name}', title: '{getattr(field, 'verbose_name', field.name)}', width: 150, formatter: ({{ cellValue }}) => format_datetime(cellValue) }},") + continue + columns.append(f"{{ field: '{field.name}', title: '{getattr(field, 'verbose_name', field.name)}' }},") + return columns \ No newline at end of file diff --git a/backend/system/migrations/0002_alter_dept_creator_alter_dept_is_deleted_and_more.py b/backend/system/migrations/0002_alter_dept_creator_alter_dept_is_deleted_and_more.py index 4af477c..15e8505 100644 --- a/backend/system/migrations/0002_alter_dept_creator_alter_dept_is_deleted_and_more.py +++ b/backend/system/migrations/0002_alter_dept_creator_alter_dept_is_deleted_and_more.py @@ -1,5 +1,7 @@ -# Generated by Django 5.2.1 on 2025-07-10 04:07 +# Generated by Django 5.2.1 on 2025-07-11 07:44 +import django.contrib.auth.validators +import django.utils.timezone from django.db import migrations, models @@ -572,6 +574,45 @@ class Migration(migrations.Migration): verbose_name="创建人", ), ), + migrations.AlterField( + model_name="user", + name="date_joined", + field=models.DateTimeField( + db_comment="date joined", + default=django.utils.timezone.now, + verbose_name="date joined", + ), + ), + migrations.AlterField( + model_name="user", + name="email", + field=models.EmailField( + blank=True, + db_comment="email address", + max_length=254, + verbose_name="email address", + ), + ), + migrations.AlterField( + model_name="user", + name="first_name", + field=models.CharField( + blank=True, + db_comment="first name", + max_length=150, + verbose_name="first name", + ), + ), + migrations.AlterField( + model_name="user", + name="is_active", + field=models.BooleanField( + db_comment="active", + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), migrations.AlterField( model_name="user", name="is_deleted", @@ -579,6 +620,46 @@ class Migration(migrations.Migration): db_comment="是否软删除", default=False, verbose_name="是否软删除" ), ), + migrations.AlterField( + model_name="user", + name="is_staff", + field=models.BooleanField( + db_comment="staff status", + default=False, + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", + ), + ), + migrations.AlterField( + model_name="user", + name="is_superuser", + field=models.BooleanField( + db_comment="superuser status", + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + migrations.AlterField( + model_name="user", + name="last_login", + field=models.DateTimeField( + blank=True, + db_comment="last login", + null=True, + verbose_name="last login", + ), + ), + migrations.AlterField( + model_name="user", + name="last_name", + field=models.CharField( + blank=True, + db_comment="last name", + max_length=150, + verbose_name="last name", + ), + ), migrations.AlterField( model_name="user", name="modifier", @@ -591,6 +672,13 @@ class Migration(migrations.Migration): verbose_name="修改人", ), ), + migrations.AlterField( + model_name="user", + name="password", + field=models.CharField( + db_comment="password", max_length=128, verbose_name="password" + ), + ), migrations.AlterField( model_name="user", name="remark", @@ -614,4 +702,17 @@ class Migration(migrations.Migration): verbose_name="修改时间", ), ), + migrations.AlterField( + model_name="user", + name="username", + field=models.CharField( + db_comment="username", + error_messages={"unique": "A user with that username already exists."}, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], + verbose_name="username", + ), + ), ] diff --git a/backend/utils/models.py b/backend/utils/models.py index 60cb7ac..ecd5569 100644 --- a/backend/utils/models.py +++ b/backend/utils/models.py @@ -9,7 +9,20 @@ class CommonStatus(models.IntegerChoices): DISABLED = 0, '禁用' ENABLED = 1, '启用' -class CoreModel(models.Model): + +class AutoCommentModel(models.Model): + class Meta: + abstract = True + + # 给字段自动添加db_comment + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + for field in cls._meta.fields: + if getattr(field, 'verbose_name', None) and not getattr(field, 'db_comment', None): + field.db_comment = field.verbose_name + + +class CoreModel(AutoCommentModel): remark = models.CharField(max_length=256, db_comment="备注", null=True, blank=True, help_text="备注", verbose_name="备注") creator = models.CharField(max_length=64, null=True, blank=True, help_text="创建人", db_comment="创建人",