diff --git a/backend/ai/__init__.py b/backend/ai/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/ai/admin.py b/backend/ai/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/backend/ai/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/backend/ai/apps.py b/backend/ai/apps.py new file mode 100644 index 0000000..ad0ae5f --- /dev/null +++ b/backend/ai/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'ai' diff --git a/backend/ai/choices.py b/backend/ai/choices.py new file mode 100644 index 0000000..d0d8dd3 --- /dev/null +++ b/backend/ai/choices.py @@ -0,0 +1,21 @@ +from django.db import models + + +class PlatformChoices(models.TextChoices): + AZURE_OPENAI = 'AzureOpenAI', 'OpenAI 微软' + OPENAI = 'OpenAI', 'OpenAI' + OLLAMA = 'Ollama', 'Ollama' + YIYAN = 'YiYan', '文心一言' + XINGHUO = 'XingHuo', '讯飞星火' + TONGYI = 'TongYi', '通义千问' + STABLE_DIFFUSION = 'StableDiffusion', 'StableDiffusion' + MIDJOURNEY = 'Midjourney', 'Midjourney' + SUNO = 'Suno', 'Suno' + DEEPSEEK = 'DeepSeek', 'DeepSeek' + DOUBAO = 'DouBao', '字节豆包' + HUNYUAN = 'HunYuan', '腾讯混元' + SILICON_FLOW = 'SiliconFlow', '硅基流动' + ZHIPU = 'ZhiPu', '智谱' + MINIMAX = 'MiniMax', 'MiniMax' + MOONSHOT = 'Moonshot', '月之暗灭' + BAICHUAN = 'BaiChuan', '百川智能' \ No newline at end of file diff --git a/backend/ai/migrations/0001_initial.py b/backend/ai/migrations/0001_initial.py new file mode 100644 index 0000000..f1bcd6c --- /dev/null +++ b/backend/ai/migrations/0001_initial.py @@ -0,0 +1,214 @@ +# Generated by Django 5.2.1 on 2025-07-10 03:03 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="AIApiKey", + 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, + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + ), + ), + ( + "is_deleted", + models.BooleanField(db_comment="是否软删除", default=False), + ), + ("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( + blank=True, + db_comment="自定义 API 地址", + max_length=255, + null=True, + ), + ), + ( + "status", + models.SmallIntegerField( + choices=[(0, "禁用"), (1, "启用")], + db_comment="状态", + default=0, + verbose_name="状态", + ), + ), + ], + options={ + "verbose_name": "AI API 密钥", + "verbose_name_plural": "AI API 密钥", + "db_table": "ai_api_key", + }, + ), + migrations.CreateModel( + name="AIModel", + 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, + ), + ), + ( + "creator", + models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + ), + ), + ( + "modifier", + models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + ), + ), + ( + "update_time", + models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + ), + ), + ( + "create_time", + models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + ), + ), + ( + "is_deleted", + models.BooleanField(db_comment="是否软删除", default=False), + ), + ("name", models.CharField(db_comment="模型名字", max_length=64)), + ("sort", models.IntegerField(db_comment="排序", default=0)), + ( + "status", + models.SmallIntegerField( + choices=[(0, "禁用"), (1, "启用")], + db_comment="状态", + default=0, + verbose_name="状态", + ), + ), + ("platform", models.CharField(db_comment="模型平台", max_length=32)), + ("model", models.CharField(db_comment="模型标识", max_length=64)), + ( + "temperature", + models.FloatField(blank=True, db_comment="温度参数", null=True), + ), + ( + "max_tokens", + models.IntegerField( + blank=True, db_comment="单条回复的最大 Token 数量", null=True + ), + ), + ( + "max_contexts", + models.IntegerField( + blank=True, db_comment="上下文的最大 Message 数量", null=True + ), + ), + ( + "key", + models.ForeignKey( + db_comment="API 秘钥编号", + on_delete=django.db.models.deletion.CASCADE, + to="ai.aiapikey", + ), + ), + ], + options={ + "verbose_name": "AI 模型", + "verbose_name_plural": "AI 模型", + "db_table": "ai_model", + }, + ), + ] 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 new file mode 100644 index 0000000..7333bd2 --- /dev/null +++ b/backend/ai/migrations/0002_alter_aiapikey_api_key_alter_aiapikey_create_time_and_more.py @@ -0,0 +1,262 @@ +# 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/__init__.py b/backend/ai/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/ai/models.py b/backend/ai/models.py new file mode 100644 index 0000000..7415569 --- /dev/null +++ b/backend/ai/models.py @@ -0,0 +1,61 @@ +from django.db import models + +from ai.choices import PlatformChoices +from utils.models import CommonStatus, CoreModel + +class AIApiKey(CoreModel): + """ AI API 密钥表 """ + name = models.CharField(max_length=255, db_comment="名称", verbose_name="名称") + platform = models.CharField( + max_length=100, + choices=PlatformChoices.choices, + verbose_name="平台", + db_comment="平台" + ) + api_key = models.CharField(max_length=255, db_comment="密钥", verbose_name="密钥") + url = models.CharField(max_length=255, null=True, blank=True, db_comment="自定义 API 地址", verbose_name="自定义 API 地址") + status = models.SmallIntegerField( + choices=CommonStatus.choices, + default=CommonStatus.DISABLED, + verbose_name="状态", + db_comment="状态", + ) + + class Meta: + db_table = "ai_api_key" + verbose_name = "AI API 密钥" + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + +class AIModel(CoreModel): + """ AI 模型 """ + name = models.CharField(max_length=64, db_comment="模型名字", verbose_name="模型名字") + sort = models.IntegerField(db_comment="排序", default=0, verbose_name="排序") + status = models.SmallIntegerField( + choices=CommonStatus.choices, + default=CommonStatus.DISABLED, + verbose_name="状态", + db_comment="状态", + ) + key = models.ForeignKey( + 'AIApiKey', + on_delete=models.CASCADE, + db_comment='API 秘钥编号', verbose_name="API 秘钥编号" + ) + platform = models.CharField(max_length=32, db_comment="模型平台", verbose_name="模型平台") + model = models.CharField(max_length=64, db_comment="模型标识", verbose_name="模型标识") + + temperature = models.FloatField(null=True, blank=True, db_comment="温度参数", verbose_name="温度参数") + max_tokens = models.IntegerField(null=True, blank=True, db_comment="单条回复的最大 Token 数量", verbose_name="单条回复的最大 Token 数量") + max_contexts = models.IntegerField(null=True, blank=True, db_comment="上下文的最大 Message 数量", verbose_name="上下文的最大 Message 数量") + + class Meta: + db_table = "ai_model" + verbose_name = "AI 模型" + verbose_name_plural = verbose_name + + def __str__(self): + return self.name diff --git a/backend/ai/tests.py b/backend/ai/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/ai/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/ai/urls.py b/backend/ai/urls.py new file mode 100644 index 0000000..08299ad --- /dev/null +++ b/backend/ai/urls.py @@ -0,0 +1,11 @@ +from django.urls import include, path +from rest_framework import routers + +from . import views + +router = routers.DefaultRouter() +router.register(r'ai_api_key', views.AIApiKeyViewSet) +urlpatterns = [ + path('', include(router.urls)), + +] \ No newline at end of file diff --git a/backend/ai/views.py b/backend/ai/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/ai/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/ai/views/__init__.py b/backend/ai/views/__init__.py new file mode 100644 index 0000000..348726c --- /dev/null +++ b/backend/ai/views/__init__.py @@ -0,0 +1,5 @@ +__all__ = [ + 'AIApiKeyViewSet' +] + +from ai.views.ai_api_key import AIApiKeyViewSet \ No newline at end of file diff --git a/backend/ai/views/ai_api_key.py b/backend/ai/views/ai_api_key.py new file mode 100644 index 0000000..abec12c --- /dev/null +++ b/backend/ai/views/ai_api_key.py @@ -0,0 +1,27 @@ +from ai.models import AIApiKey +from utils.serializers import CustomModelSerializer +from utils.custom_model_viewSet import CustomModelViewSet + +class AIApiKeySerializer(CustomModelSerializer): + """ + AI API 密钥 序列化器 + """ + class Meta: + model = AIApiKey + fields = '__all__' + read_only_fields = ['id', 'create_time', 'update_time'] + + +class AIApiKeyViewSet(CustomModelViewSet): + """ + AI API 密钥 视图集 + """ + queryset = AIApiKey.objects.filter(is_deleted=False).order_by('-id') + serializer_class = AIApiKeySerializer + filterset_fields = ['id', 'remark', 'creator', 'modifier', 'is_deleted', 'name', 'platform', 'api_key', 'url', 'status'] + search_fields = ['name'] # 根据实际字段调整 + ordering_fields = ['create_time', 'id'] + ordering = ['-create_time'] + +# 移入urls中 +# diff --git a/backend/backend/settings.py b/backend/backend/settings.py index 7e8abfa..2a9681c 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -54,6 +54,7 @@ INSTALLED_APPS = [ 'corsheaders', 'rest_framework.authtoken', "system", + "ai", ] MIDDLEWARE = [ diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 04d3763..f6e6303 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -20,6 +20,7 @@ from django.conf import settings urlpatterns = [ path('api/system/', include('system.urls')), + path('api/ai/', include('ai.urls')), ] # 演示环境下禁用 admin 路由 diff --git a/backend/system/management/commands/tpl/frontend_data.ts.tpl b/backend/system/management/commands/tpl/frontend_data.ts.tpl index 4524623..dd69d3f 100644 --- a/backend/system/management/commands/tpl/frontend_data.ts.tpl +++ b/backend/system/management/commands/tpl/frontend_data.ts.tpl @@ -7,6 +7,7 @@ import type { ${app_name_camel}${model_name}Api } from '#/models/${app_name}/${m import { z } from '#/adapter/form'; import { $$t } from '#/locales'; import { format_datetime } from '#/utils/date'; +import { op } from '#/utils/permission'; /** * 获取编辑表单的字段配置 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 new file mode 100644 index 0000000..4af477c --- /dev/null +++ b/backend/system/migrations/0002_alter_dept_creator_alter_dept_is_deleted_and_more.py @@ -0,0 +1,617 @@ +# Generated by Django 5.2.1 on 2025-07-10 04:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("system", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="dept", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="dept", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="dept", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="dept", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="dictdata", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="dictdata", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="dictdata", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="dictdata", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="dictdata", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="dictdata", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="dicttype", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="dicttype", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="dicttype", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="dicttype", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="dicttype", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="dicttype", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="loginlog", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="loginlog", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="loginlog", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="loginlog", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="loginlog", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="loginlog", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="menu", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="menu", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="menu", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="menu", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="menu", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="menu", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="menumeta", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="menumeta", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="menumeta", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="menumeta", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="menumeta", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="menumeta", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="post", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="post", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="post", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="post", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="post", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="post", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="role", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="role", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="role", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="role", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="role", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="rolepermission", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="rolepermission", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="rolepermission", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="rolepermission", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="rolepermission", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + migrations.AlterField( + model_name="user", + name="create_time", + field=models.DateTimeField( + auto_now_add=True, + db_comment="创建时间", + help_text="创建时间", + null=True, + verbose_name="创建时间", + ), + ), + migrations.AlterField( + model_name="user", + name="creator", + field=models.CharField( + blank=True, + db_comment="创建人", + help_text="创建人", + max_length=64, + null=True, + verbose_name="创建人", + ), + ), + migrations.AlterField( + model_name="user", + name="is_deleted", + field=models.BooleanField( + db_comment="是否软删除", default=False, verbose_name="是否软删除" + ), + ), + migrations.AlterField( + model_name="user", + name="modifier", + field=models.CharField( + blank=True, + db_comment="修改人", + help_text="修改人", + max_length=64, + null=True, + verbose_name="修改人", + ), + ), + migrations.AlterField( + model_name="user", + name="remark", + field=models.CharField( + blank=True, + db_comment="备注", + help_text="备注", + max_length=256, + null=True, + verbose_name="备注", + ), + ), + migrations.AlterField( + model_name="user", + name="update_time", + field=models.DateTimeField( + auto_now=True, + db_comment="修改时间", + help_text="修改时间", + null=True, + verbose_name="修改时间", + ), + ), + ] diff --git a/backend/utils/models.py b/backend/utils/models.py index e9619d7..60cb7ac 100644 --- a/backend/utils/models.py +++ b/backend/utils/models.py @@ -10,13 +10,17 @@ class CommonStatus(models.IntegerChoices): ENABLED = 1, '启用' class CoreModel(models.Model): - remark = models.CharField(max_length=256, db_comment="备注", null=True, blank=True, help_text="备注") - creator = models.CharField(max_length=64, null=True, blank=True, help_text="创建人", db_comment="创建人") - modifier = models.CharField(max_length=64, null=True, blank=True, help_text="修改人", db_comment="修改人") - update_time = models.DateTimeField(auto_now=True, null=True, blank=True, help_text="修改时间", db_comment="修改时间") + 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="创建人", + verbose_name="创建人") + modifier = models.CharField(max_length=64, null=True, blank=True, help_text="修改人", db_comment="修改人", + verbose_name="修改人") + update_time = models.DateTimeField(auto_now=True, null=True, blank=True, help_text="修改时间", + db_comment="修改时间", verbose_name="修改时间") create_time = models.DateTimeField(auto_now_add=True, null=True, blank=True, help_text="创建时间", - db_comment="创建时间") - is_deleted = models.BooleanField(default=False, db_comment='是否软删除') + db_comment="创建时间", verbose_name="创建时间") + is_deleted = models.BooleanField(default=False, db_comment='是否软删除', verbose_name="是否软删除") class Meta: abstract = True diff --git a/docs/docs/essential/module_creation_guide.md b/docs/docs/essential/module_creation_guide.md new file mode 100644 index 0000000..0a02b3d --- /dev/null +++ b/docs/docs/essential/module_creation_guide.md @@ -0,0 +1,137 @@ +--- +sidebar_position: 10 +--- + +# 新建模块 + +本文档适用于本项目(Django + Vue3),指导如何规范地新建一个完整的功能模块(含后端与前端)。 + +--- + +## 一、后端模块新建流程(Django) + +1. **新建Django App** + ```bash + cd backend + python manage.py startapp <模块名> + # 如: python manage.py startapp ai + ``` + > 建议模块名使用小写英文,避免与已有模块重名。 + +2. **注册App** + - 在 `backend/backend/settings.py` 的 `INSTALLED_APPS` 中添加新模块。 + ```python + INSTALLED_APPS = [ + "simpleui", + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + "rest_framework", + 'django_filters', + 'corsheaders', + 'rest_framework.authtoken', + "system", + "ai", # 新加 + ] + ``` + +3. **定义模型(models.py)** + - 继承 `CoreModel`,包含必要的审计字段(如 creator、modifier)。 + - 示例: + ```python + from system.models import CoreModel + class AIApiKey(CoreModel): + """ AI API 密钥表 """ + name = models.CharField(max_length=255, db_comment="名称") + platform = models.CharField(max_length=255, db_comment="平台") + ``` + +4. **生成迁移并迁移数据库** + ```bash + python manage.py makemigrations <模块名> + python manage.py migrate + ``` + +5. **注册路由(urls.py)** + - 按照RESTful规范注册路由。 在ai文件夹下新建urls文件,并添加以下内容 + ```python + from django.urls import include, path + from rest_framework import routers + + from . import views + + router = routers.DefaultRouter() + + urlpatterns = [ + path('', include(router.urls)), + + ] + ``` +6. **修改路由(urls.py)** + - 按照RESTful规范注册路由。 在ai文件夹下新建urls文件,并添加以下内容 + ```python + urlpatterns = [ + path('api/system/', include('system.urls')), + path('api/ai/', include('ai.urls')), # 新加 + ] + ``` + +7. **权限与菜单自动生成(推荐)** + - 使用代码生成器: + ```bash + python manage.py generate_crud <模块名> <模型名> --frontend + ``` + +8. ** +```python +# 在ai/views下新建 __init__.py文件 +__all__ = [ + 'AIApiKeyViewSet' +] + +from ai.views.ai_api_key import AIApiKeyViewSet +``` + + +--- + +## 二、前端模块新建流程(Vue3)(代码生成器已经生成) +1. **配置多语言** + 在web/apps/web-antd/src/locales/langs/zh-CN/ai.json 配置多语言 + ```json + { + "title": "AI 大模型" + } + + ``` + +2. **新建菜单配置** + + ![new_module](./new_module.png) + + +3. **生成权限菜单** +```shell + cd backend + python manage.py gen_menu_json --app ai --model AIApiKey --parent AI +``` + + + +--- + +## 三、注意事项 + +- 模块命名规范统一,避免重复。 +- 建议优先使用代码生成器自动生成基础代码,减少重复劳动。 +- 新模块需补充单元测试和接口文档。 +- 前后端接口字段需保持一致,类型安全。 +- 权限点、菜单、路由需同步配置。 +- 代码提交前请自查并走团队代码评审流程。 + +--- + +如有疑问请联系项目负责人或查阅项目主文档。 \ No newline at end of file diff --git a/docs/docs/essential/new_module.png b/docs/docs/essential/new_module.png new file mode 100644 index 0000000..eff17f1 Binary files /dev/null and b/docs/docs/essential/new_module.png differ diff --git a/web/apps/web-antd/src/locales/langs/en-US/ai.json b/web/apps/web-antd/src/locales/langs/en-US/ai.json new file mode 100644 index 0000000..915be92 --- /dev/null +++ b/web/apps/web-antd/src/locales/langs/en-US/ai.json @@ -0,0 +1,7 @@ +{ + "title": "AI Management", + "ai_api_key": { + "title": "API KEY", + "name": "API KEY" + } +} diff --git a/web/apps/web-antd/src/locales/langs/zh-CN/ai.json b/web/apps/web-antd/src/locales/langs/zh-CN/ai.json new file mode 100644 index 0000000..337b444 --- /dev/null +++ b/web/apps/web-antd/src/locales/langs/zh-CN/ai.json @@ -0,0 +1,7 @@ +{ + "title": "AI大模型", + "ai_api_key": { + "title": "API 密钥", + "name": "API 密钥" + } +} diff --git a/web/apps/web-antd/src/models/ai/ai_api_key.ts b/web/apps/web-antd/src/models/ai/ai_api_key.ts new file mode 100644 index 0000000..c29eb6c --- /dev/null +++ b/web/apps/web-antd/src/models/ai/ai_api_key.ts @@ -0,0 +1,24 @@ +import { BaseModel } from '#/models/base'; + +export namespace AiAIApiKeyApi { + export interface AiAIApiKey { + id: number; + remark: string; + creator: string; + modifier: string; + update_time: string; + create_time: string; + is_deleted: boolean; + name: string; + platform: string; + api_key: string; + url: string; + status: number; + } +} + +export class AiAIApiKeyModel extends BaseModel { + constructor() { + super('/ai/ai_api_key/'); + } +} diff --git a/web/apps/web-antd/src/views/ai/ai_api_key/data.ts b/web/apps/web-antd/src/views/ai/ai_api_key/data.ts new file mode 100644 index 0000000..a078c64 --- /dev/null +++ b/web/apps/web-antd/src/views/ai/ai_api_key/data.ts @@ -0,0 +1,184 @@ +import type { VxeTableGridOptions } from '@vben/plugins/vxe-table'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn } from '#/adapter/vxe-table'; +import type { AiAIApiKeyApi } from '#/models/ai/ai_api_key'; + +import { z } from '#/adapter/form'; +import { $t } from '#/locales'; +import { op } from '#/utils/permission'; + +/** + * 获取编辑表单的字段配置 + */ +export function useSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'name', + label: 'name', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['name'])) + .max(100, $t('ui.formRules.maxLength', ['name', 100])), + }, + { + component: 'Input', + fieldName: 'platform', + label: 'platform', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['platform'])) + .max(100, $t('ui.formRules.maxLength', ['platform', 100])), + }, + { + component: 'Input', + fieldName: 'api_key', + label: 'api key', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['api key'])) + .max(100, $t('ui.formRules.maxLength', ['api key', 100])), + }, + { + component: 'Input', + fieldName: 'url', + label: 'url', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['url'])) + .max(100, $t('ui.formRules.maxLength', ['url', 100])), + }, + { + component: 'InputNumber', + fieldName: 'status', + label: '状态', + }, + { + component: 'Input', + fieldName: 'remark', + label: 'remark', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['remark'])) + .max(100, $t('ui.formRules.maxLength', ['remark', 100])), + }, + ]; +} + +/** + * 获取编辑表单的字段配置 + */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'name', + label: 'name', + }, + { + component: 'Input', + fieldName: 'platform', + label: 'platform', + }, + { + component: 'Input', + fieldName: 'api_key', + label: 'api key', + }, + { + component: 'Input', + fieldName: 'url', + label: 'url', + }, + { + component: 'InputNumber', + fieldName: 'status', + label: '状态', + }, + { + component: 'Input', + fieldName: 'remark', + label: 'remark', + }, + ]; +} + +/** + * 获取表格列配置 + * @description 使用函数的形式返回列数据而不是直接export一个Array常量,是为了响应语言切换时重新翻译表头 + * @param onActionClick 表格操作按钮点击事件 + */ +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: 'ID', + }, + { + field: 'name', + title: 'name', + }, + { + field: 'platform', + title: 'platform', + }, + { + field: 'api_key', + title: 'api key', + }, + { + field: 'url', + title: 'url', + }, + { + field: 'status', + title: '状态', + }, + { + field: 'remark', + title: 'remark', + }, + { + field: 'creator', + title: 'creator', + }, + { + field: 'modifier', + title: 'modifier', + }, + { + field: 'update_time', + title: 'update time', + }, + { + field: 'create_time', + title: 'create time', + }, + { + field: 'is_deleted', + title: 'is deleted', + }, + { + align: 'center', + cellRender: { + attrs: { + nameField: 'name', + nameTitle: $t('ai.ai_api_key.name'), + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + op('ai:ai_api_key:edit', 'edit'), + op('ai:ai_api_key:delete', 'delete'), + ], + }, + field: 'action', + fixed: 'right', + title: '操作', + width: 120, + }, + ]; +} diff --git a/web/apps/web-antd/src/views/ai/ai_api_key/list.vue b/web/apps/web-antd/src/views/ai/ai_api_key/list.vue new file mode 100644 index 0000000..2f1bc73 --- /dev/null +++ b/web/apps/web-antd/src/views/ai/ai_api_key/list.vue @@ -0,0 +1,141 @@ + + + diff --git a/web/apps/web-antd/src/views/ai/ai_api_key/modules/form.vue b/web/apps/web-antd/src/views/ai/ai_api_key/modules/form.vue new file mode 100644 index 0000000..dd0f99c --- /dev/null +++ b/web/apps/web-antd/src/views/ai/ai_api_key/modules/form.vue @@ -0,0 +1,79 @@ + + + +