init ai api key

This commit is contained in:
xie7654
2025-07-10 15:32:00 +08:00
parent bf07ff5744
commit 65bdda6377
26 changed files with 1825 additions and 6 deletions

0
backend/ai/__init__.py Normal file
View File

3
backend/ai/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
backend/ai/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class AiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'ai'

21
backend/ai/choices.py Normal file
View File

@@ -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', '百川智能'

View File

@@ -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",
},
),
]

View File

@@ -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="修改时间",
),
),
]

View File

61
backend/ai/models.py Normal file
View File

@@ -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

3
backend/ai/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
backend/ai/urls.py Normal file
View File

@@ -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)),
]

3
backend/ai/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

@@ -0,0 +1,5 @@
__all__ = [
'AIApiKeyViewSet'
]
from ai.views.ai_api_key import AIApiKeyViewSet

View File

@@ -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中
#

View File

@@ -54,6 +54,7 @@ INSTALLED_APPS = [
'corsheaders', 'corsheaders',
'rest_framework.authtoken', 'rest_framework.authtoken',
"system", "system",
"ai",
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@@ -20,6 +20,7 @@ from django.conf import settings
urlpatterns = [ urlpatterns = [
path('api/system/', include('system.urls')), path('api/system/', include('system.urls')),
path('api/ai/', include('ai.urls')),
] ]
# 演示环境下禁用 admin 路由 # 演示环境下禁用 admin 路由

View File

@@ -7,6 +7,7 @@ import type { ${app_name_camel}${model_name}Api } from '#/models/${app_name}/${m
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { $$t } from '#/locales'; import { $$t } from '#/locales';
import { format_datetime } from '#/utils/date'; import { format_datetime } from '#/utils/date';
import { op } from '#/utils/permission';
/** /**
* 获取编辑表单的字段配置 * 获取编辑表单的字段配置

View File

@@ -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="修改时间",
),
),
]

View File

@@ -10,13 +10,17 @@ class CommonStatus(models.IntegerChoices):
ENABLED = 1, '启用' ENABLED = 1, '启用'
class CoreModel(models.Model): class CoreModel(models.Model):
remark = models.CharField(max_length=256, db_comment="备注", null=True, blank=True, help_text="备注") 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="创建人") verbose_name="备注")
modifier = models.CharField(max_length=64, null=True, blank=True, help_text="修改", db_comment="修改") creator = 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="修改时间") 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="创建时间", create_time = models.DateTimeField(auto_now_add=True, null=True, blank=True, help_text="创建时间",
db_comment="创建时间") db_comment="创建时间", verbose_name="创建时间")
is_deleted = models.BooleanField(default=False, db_comment='是否软删除') is_deleted = models.BooleanField(default=False, db_comment='是否软删除', verbose_name="是否软删除")
class Meta: class Meta:
abstract = True abstract = True

View File

@@ -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
```
---
## 三、注意事项
- 模块命名规范统一,避免重复。
- 建议优先使用代码生成器自动生成基础代码,减少重复劳动。
- 新模块需补充单元测试和接口文档。
- 前后端接口字段需保持一致,类型安全。
- 权限点、菜单、路由需同步配置。
- 代码提交前请自查并走团队代码评审流程。
---
如有疑问请联系项目负责人或查阅项目主文档。

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -0,0 +1,7 @@
{
"title": "AI Management",
"ai_api_key": {
"title": "API KEY",
"name": "API KEY"
}
}

View File

@@ -0,0 +1,7 @@
{
"title": "AI大模型",
"ai_api_key": {
"title": "API 密钥",
"name": "API 密钥"
}
}

View File

@@ -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<AiAIApiKeyApi.AiAIApiKey> {
constructor() {
super('/ai/ai_api_key/');
}
}

View File

@@ -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<AiAIApiKeyApi.AiAIApiKey>,
): VxeTableGridOptions<AiAIApiKeyApi.AiAIApiKey>['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,
},
];
}

View File

@@ -0,0 +1,141 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { AiAIApiKeyApi } from '#/models/ai/ai_api_key';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { $t } from '#/locales';
import { AiAIApiKeyModel } from '#/models/ai/ai_api_key';
import { useColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
const formModel = new AiAIApiKeyModel();
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
destroyOnClose: true,
});
/**
* 编辑AI API 密钥
*/
function onEdit(row: AiAIApiKeyApi.AiAIApiKey) {
formModalApi.setData(row).open();
}
/**
* 创建新AI API 密钥
*/
function onCreate() {
formModalApi.setData(null).open();
}
/**
* 删除AI API 密钥
*/
function onDelete(row: AiAIApiKeyApi.AiAIApiKey) {
const hideLoading = message.loading({
content: '删除AI API 密钥',
duration: 0,
key: 'action_process_msg',
});
formModel
.delete(row.id)
.then(() => {
message.success({
content: '删除成功',
key: 'action_process_msg',
});
refreshGrid();
})
.catch(() => {
hideLoading();
});
}
/**
* 表格操作按钮的回调函数
*/
function onActionClick({
code,
row,
}: OnActionClickParams<AiAIApiKeyApi.AiAIApiKey>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
submitOnChange: true,
},
gridEvents: {},
gridOptions: {
columns: useColumns(onActionClick),
height: 'auto',
keepSource: true,
pagerConfig: {
enabled: true,
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await formModel.list({
page: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
toolbarConfig: {
custom: true,
export: false,
refresh: { code: 'query' },
zoom: true,
search: true,
},
} as VxeTableGridOptions,
});
/**
* 刷新表格
*/
function refreshGrid() {
gridApi.query();
}
</script>
<template>
<Page auto-content-height>
<FormModal @success="refreshGrid" />
<Grid table-title="AI API 密钥">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-permission="'ai:ai_api_key:create'"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', [$t('ai.ai_api_key.name')]) }}
</Button>
</template>
</Grid>
</Page>
</template>

View File

@@ -0,0 +1,79 @@
<script lang="ts" setup>
import type { AiAIApiKeyApi } from '#/models/ai/ai_api_key';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Button } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { $t } from '#/locales';
import { AiAIApiKeyModel } from '#/models/ai/ai_api_key';
import { useSchema } from '../data';
const emit = defineEmits(['success']);
const formModel = new AiAIApiKeyModel();
const formData = ref<AiAIApiKeyApi.AiAIApiKey>();
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', [$t('ai.aiapikey.name')])
: $t('ui.actionTitle.create', [$t('ai.aiapikey.name')]);
});
const [Form, formApi] = useVbenForm({
layout: 'horizontal',
schema: useSchema(),
showDefaultActions: false,
});
function resetForm() {
formApi.resetForm();
formApi.setValues(formData.value || {});
}
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (valid) {
modalApi.lock();
const data = await formApi.getValues();
try {
await (formData.value?.id
? formModel.update(formData.value.id, data)
: formModel.create(data));
await modalApi.close();
emit('success');
} finally {
modalApi.lock(false);
}
}
},
onOpenChange(isOpen) {
if (isOpen) {
const data = modalApi.getData<AiAIApiKeyApi.AiAIApiKey>();
if (data) {
formData.value = data;
formApi.setValues(formData.value);
}
}
},
});
</script>
<template>
<Modal :title="getTitle">
<Form />
<template #prepend-footer>
<div class="flex-auto">
<Button type="primary" danger @click="resetForm">
{{ $t('common.reset') }}
</Button>
</div>
</template>
</Modal>
</template>
<style lang="css" scoped></style>