This commit is contained in:
xie7654
2025-06-29 21:45:27 +08:00
commit f6e68e37c8
1539 changed files with 129319 additions and 0 deletions

View File

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

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

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

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

View File

@@ -0,0 +1,990 @@
# Generated by Django 5.2.1 on 2025-06-29 13:08
import django.contrib.auth.models
import django.contrib.auth.validators
import django.db.models.deletion
import django.utils.timezone
import utils.utils
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
]
operations = [
migrations.CreateModel(
name="DictType",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"remark",
models.CharField(
blank=True,
help_text="备注",
max_length=256,
null=True,
verbose_name="备注",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"create_time",
models.DateTimeField(
auto_now_add=True,
help_text="创建时间",
null=True,
verbose_name="创建时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
(
"name",
models.CharField(
default="", max_length=100, verbose_name="字典名称"
),
),
(
"type",
models.CharField(
db_index=True,
default="",
max_length=100,
verbose_name="字典类型",
),
),
("status", models.BooleanField(default=True)),
(
"deleted_time",
models.DateTimeField(
blank=True, null=True, verbose_name="删除时间"
),
),
],
options={
"verbose_name": "字典类型",
"verbose_name_plural": "字典类型",
"db_table": "system_dict_type",
"ordering": ["-id"],
},
),
migrations.CreateModel(
name="MenuMeta",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"remark",
models.CharField(
blank=True,
help_text="备注",
max_length=256,
null=True,
verbose_name="备注",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"create_time",
models.DateTimeField(
auto_now_add=True,
help_text="创建时间",
null=True,
verbose_name="创建时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
("title", models.CharField(max_length=200, verbose_name="标题")),
(
"icon",
models.CharField(blank=True, max_length=100, verbose_name="图标"),
),
("order", models.IntegerField(default=0, verbose_name="排序")),
(
"affix_tab",
models.BooleanField(default=False, verbose_name="固定标签页"),
),
(
"badge",
models.CharField(
blank=True, max_length=50, verbose_name="徽章文本"
),
),
(
"badge_type",
models.CharField(
blank=True, max_length=20, verbose_name="徽章类型"
),
),
(
"badge_variants",
models.CharField(
blank=True, max_length=20, verbose_name="徽章样式"
),
),
("iframe_src", models.URLField(blank=True, verbose_name="内嵌页面URL")),
("link", models.URLField(blank=True, verbose_name="外部链接")),
],
options={
"verbose_name": "菜单元数据",
"verbose_name_plural": "菜单元数据",
"db_table": "system_menu_meta",
},
),
migrations.CreateModel(
name="Role",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"create_time",
models.DateTimeField(
auto_now_add=True,
help_text="创建时间",
null=True,
verbose_name="创建时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
("name", models.CharField(max_length=100, verbose_name="角色名称")),
(
"status",
models.IntegerField(
choices=[(1, "启用"), (0, "禁用")],
default=1,
verbose_name="角色状态",
),
),
(
"sort",
models.IntegerField(
default=0, help_text="数值越小越靠前", verbose_name="显示排序"
),
),
("remark", models.TextField(blank=True, verbose_name="备注")),
],
options={
"verbose_name": "角色管理",
"verbose_name_plural": "角色管理",
"ordering": ["-create_time"],
},
),
migrations.CreateModel(
name="Dept",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
("name", models.CharField(max_length=100, verbose_name="部门名称")),
(
"status",
models.SmallIntegerField(
choices=[(0, "禁用"), (1, "启用")],
default=0,
verbose_name="部门状态",
),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"sort",
models.IntegerField(
default=0, help_text="数值越小越靠前", verbose_name="显示排序"
),
),
(
"leader",
models.CharField(
blank=True, max_length=20, null=True, verbose_name="负责人"
),
),
(
"phone",
models.CharField(
blank=True, max_length=20, null=True, verbose_name="联系电话"
),
),
(
"email",
models.EmailField(
blank=True, max_length=254, null=True, verbose_name="邮箱"
),
),
("remark", models.TextField(blank=True, verbose_name="备注")),
(
"pid",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="children",
to="system.dept",
verbose_name="父部门 ID",
),
),
],
options={
"verbose_name": "部门管理",
"verbose_name_plural": "部门管理",
"ordering": ["-create_time"],
},
),
migrations.CreateModel(
name="User",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"username",
models.CharField(
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",
),
),
(
"first_name",
models.CharField(
blank=True, max_length=150, verbose_name="first name"
),
),
(
"last_name",
models.CharField(
blank=True, max_length=150, verbose_name="last name"
),
),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="email address"
),
),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
),
),
(
"remark",
models.CharField(
blank=True,
help_text="备注",
max_length=256,
null=True,
verbose_name="备注",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"create_time",
models.DateTimeField(
auto_now_add=True,
help_text="创建时间",
null=True,
verbose_name="创建时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
(
"mobile",
models.CharField(
db_comment="手机号",
max_length=11,
null=True,
validators=[utils.utils.validate_mobile],
),
),
(
"nickname",
models.CharField(
blank=True, db_comment="昵称", max_length=50, null=True
),
),
(
"gender",
models.SmallIntegerField(
blank=True, db_comment="性别", default=0, null=True
),
),
(
"language",
models.CharField(
blank=True,
db_comment="语言",
max_length=20,
null=True,
verbose_name="语言",
),
),
(
"city",
models.CharField(
blank=True,
db_comment="城市",
max_length=20,
null=True,
verbose_name="城市",
),
),
(
"province",
models.CharField(
blank=True,
db_comment="省份",
max_length=50,
null=True,
verbose_name="省份",
),
),
(
"country",
models.CharField(
blank=True,
db_comment="国家",
max_length=50,
null=True,
verbose_name="国家",
),
),
(
"avatarUrl",
models.URLField(
blank=True, db_comment="头像", null=True, verbose_name="头像"
),
),
(
"status",
models.BooleanField(
db_comment="帐号状态",
default=False,
verbose_name="<帐号状态>1正常 0停用",
),
),
(
"login_date",
models.DateTimeField(
blank=True,
db_comment="最后登录时间",
null=True,
verbose_name="<最后登录时间>",
),
),
(
"login_ip",
models.GenericIPAddressField(
blank=True, db_comment="最后登录IP", null=True
),
),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
(
"dept",
models.ManyToManyField(
blank=True,
db_constraint=False,
related_name="users",
to="system.dept",
verbose_name="部门",
),
),
],
options={
"verbose_name": "用户数据",
"verbose_name_plural": "用户数据",
"db_table": "system_users",
},
managers=[
("objects", django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name="DictData",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"remark",
models.CharField(
blank=True,
help_text="备注",
max_length=256,
null=True,
verbose_name="备注",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"create_time",
models.DateTimeField(
auto_now_add=True,
help_text="创建时间",
null=True,
verbose_name="创建时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
("sort", models.IntegerField(default=0, verbose_name="字典排序")),
(
"label",
models.CharField(
default="", max_length=100, verbose_name="字典标签"
),
),
(
"value",
models.CharField(
default="", max_length=100, verbose_name="字典键值"
),
),
("status", models.BooleanField(default=True)),
(
"color_type",
models.CharField(
blank=True, default="", max_length=100, verbose_name="颜色类型"
),
),
(
"css_class",
models.CharField(
blank=True, default="", max_length=100, verbose_name="css 样式"
),
),
(
"dict_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="dict_data",
to="system.dicttype",
verbose_name="字典类型",
),
),
],
options={
"verbose_name": "字典数据",
"verbose_name_plural": "字典数据",
"db_table": "system_dict_data",
"ordering": ["sort", "id"],
},
),
migrations.CreateModel(
name="Menu",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"remark",
models.CharField(
blank=True,
help_text="备注",
max_length=256,
null=True,
verbose_name="备注",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"create_time",
models.DateTimeField(
auto_now_add=True,
help_text="创建时间",
null=True,
verbose_name="创建时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
("name", models.CharField(max_length=100, verbose_name="菜单名称")),
(
"status",
models.IntegerField(
choices=[(1, "启用"), (0, "禁用")],
default=1,
verbose_name="状态",
),
),
(
"type",
models.CharField(
choices=[
("catalog", "目录"),
("menu", "菜单"),
("button", "按钮"),
("embedded", "内嵌页面"),
("link", "外部链接"),
],
max_length=20,
verbose_name="菜单类型",
),
),
(
"path",
models.CharField(
blank=True, max_length=200, verbose_name="路由路径"
),
),
(
"component",
models.CharField(
blank=True, max_length=200, verbose_name="组件路径"
),
),
(
"auth_code",
models.CharField(
blank=True, max_length=100, verbose_name="权限编码"
),
),
(
"pid",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="children",
to="system.menu",
verbose_name="父菜单",
),
),
(
"meta",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="system.menumeta",
verbose_name="元数据",
),
),
],
options={
"verbose_name": "菜单",
"verbose_name_plural": "菜单管理",
"ordering": ["meta__order", "id"],
},
),
migrations.CreateModel(
name="RolePermission",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"remark",
models.CharField(
blank=True,
help_text="备注",
max_length=256,
null=True,
verbose_name="备注",
),
),
(
"creator",
models.CharField(
blank=True,
help_text="创建人",
max_length=64,
null=True,
verbose_name="创建人",
),
),
(
"modifier",
models.CharField(
blank=True,
help_text="修改人",
max_length=64,
null=True,
verbose_name="修改人",
),
),
(
"update_time",
models.DateTimeField(
auto_now=True,
help_text="修改时间",
null=True,
verbose_name="修改时间",
),
),
(
"is_deleted",
models.BooleanField(default=False, verbose_name="是否软删除"),
),
(
"create_time",
models.DateTimeField(
auto_now_add=True, verbose_name="权限关联时间"
),
),
(
"menu",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="system.menu",
verbose_name="菜单/权限",
),
),
(
"role",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="system.role",
verbose_name="角色",
),
),
],
options={
"verbose_name": "角色权限关联",
"verbose_name_plural": "角色权限关联",
"db_table": "system_role_permission",
},
),
migrations.AddField(
model_name="role",
name="permissions",
field=models.ManyToManyField(
through="system.RolePermission",
to="system.menu",
verbose_name="关联权限",
),
),
]

View File

248
backend/system/models.py Normal file
View File

@@ -0,0 +1,248 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from backend import settings
from utils.models import CoreModel
from utils.utils import validate_mobile
# 定义状态枚举(可根据实际业务扩展)
class DepartmentStatus(models.IntegerChoices):
DISABLED = 0, "禁用" # 对应数据中的 status: 0
ENABLED = 1, "启用" # 对应数据中的 status: 1
class Dept(CoreModel):
pid = models.ForeignKey(
"self",
on_delete=models.CASCADE,
null=True,
blank=True,
related_name="children",
verbose_name="父部门 ID"
)
name = models.CharField(max_length=100, verbose_name="部门名称")
status = models.SmallIntegerField(
choices=DepartmentStatus.choices,
default=DepartmentStatus.DISABLED,
verbose_name="部门状态"
)
create_time = models.DateTimeField(
auto_now_add=True,
verbose_name="创建时间",
# 若数据中时间需自动解析,可在保存时处理:
# default=timezone.now # 或通过数据导入时赋值
)
sort = models.IntegerField(
default=0,
verbose_name="显示排序",
help_text="数值越小越靠前"
)
leader = models.CharField(
null=True,
blank=True,
max_length=20,
verbose_name="负责人"
)
phone = models.CharField(
max_length=20,
blank=True,
null=True,
verbose_name="联系电话"
)
email = models.EmailField(
blank=True,
null=True,
verbose_name="邮箱"
)
remark = models.TextField(blank=True, verbose_name="备注")
class Meta:
verbose_name = "部门管理"
verbose_name_plural = verbose_name
ordering = ["-create_time"] # 按创建时间倒序排列
# 菜单类型枚举
class MenuType(models.TextChoices):
CATALOG = 'catalog', '目录'
MENU = 'menu', '菜单'
BUTTON = 'button', '按钮'
EMBEDDED = 'embedded', '内嵌页面'
LINK = 'link', '外部链接'
# 菜单状态枚举
class MenuStatus(models.IntegerChoices):
ENABLED = 1, '启用'
DISABLED = 0, '禁用'
# 菜单元数据模型(单独存储元数据,避免 JSONField
class MenuMeta(CoreModel):
title = models.CharField(max_length=200, verbose_name='标题')
icon = models.CharField(max_length=100, blank=True, verbose_name='图标')
order = models.IntegerField(default=0, verbose_name='排序')
affix_tab = models.BooleanField(default=False, verbose_name='固定标签页')
badge = models.CharField(max_length=50, blank=True, verbose_name='徽章文本')
badge_type = models.CharField(max_length=20, blank=True, verbose_name='徽章类型')
badge_variants = models.CharField(max_length=20, blank=True, verbose_name='徽章样式')
iframe_src = models.URLField(blank=True, verbose_name='内嵌页面URL')
link = models.URLField(blank=True, verbose_name='外部链接')
def __str__(self):
return self.title
class Meta:
db_table = 'system_menu_meta'
verbose_name = '菜单元数据'
verbose_name_plural = '菜单元数据'
# 主菜单模型
class Menu(CoreModel):
pid = models.ForeignKey(
'self',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='children',
verbose_name='父菜单'
)
name = models.CharField(max_length=100, verbose_name='菜单名称')
status = models.IntegerField(choices=MenuStatus.choices, default=MenuStatus.ENABLED, verbose_name='状态')
type = models.CharField(choices=MenuType.choices, max_length=20, verbose_name='菜单类型')
path = models.CharField(max_length=200, blank=True, verbose_name='路由路径')
component = models.CharField(max_length=200, blank=True, verbose_name='组件路径')
auth_code = models.CharField(max_length=100, blank=True, verbose_name='权限编码')
meta = models.OneToOneField(MenuMeta, on_delete=models.CASCADE, verbose_name='元数据')
def __str__(self):
return self.name
class Meta:
verbose_name = '菜单'
verbose_name_plural = '菜单管理'
ordering = ['meta__order', 'id']
# 角色状态枚举
class RoleStatus(models.IntegerChoices):
ENABLED = 1, '启用'
DISABLED = 0, '禁用'
class Role(CoreModel):
name = models.CharField(
max_length=100,
verbose_name='角色名称'
)
status = models.IntegerField(
choices=RoleStatus.choices,
default=RoleStatus.ENABLED,
verbose_name='角色状态'
)
sort = models.IntegerField(
default=0,
verbose_name="显示排序",
help_text="数值越小越靠前"
)
remark = models.TextField(
blank=True,
verbose_name='备注'
)
# 与菜单权限的多对多关联(假设菜单模型为 Menu权限字段为 auth_code
permissions = models.ManyToManyField(
'Menu', # 引用之前设计的 Menu 模型
through='RolePermission',
verbose_name='关联权限'
)
class Meta:
verbose_name = '角色管理'
verbose_name_plural = verbose_name
ordering = ["-create_time"] # 按创建时间倒序排列
def __str__(self):
return self.name
# 中间表:角色与权限的关联(可扩展字段如权限生效时间)
class RolePermission(CoreModel):
role = models.ForeignKey(
Role,
on_delete=models.CASCADE,
verbose_name='角色'
)
menu = models.ForeignKey(
'Menu',
on_delete=models.CASCADE,
verbose_name='菜单/权限'
)
# 可选:记录权限关联时间
create_time = models.DateTimeField(
auto_now_add=True,
verbose_name='权限关联时间'
)
class Meta:
db_table = 'system_role_permission'
verbose_name = '角色权限关联'
verbose_name_plural = verbose_name
class DictType(CoreModel):
"""字典类型表"""
name = models.CharField(max_length=100, default='', verbose_name='字典名称')
type = models.CharField(max_length=100, default='', verbose_name='字典类型', db_index=True)
status = models.BooleanField(default=True)
deleted_time = models.DateTimeField(null=True, blank=True, verbose_name='删除时间')
class Meta:
verbose_name = '字典类型'
verbose_name_plural = '字典类型'
db_table = 'system_dict_type'
ordering = ['-id']
def __str__(self):
return self.name
class DictData(CoreModel):
"""字典数据表"""
sort = models.IntegerField(default=0, verbose_name='字典排序')
label = models.CharField(max_length=100, default='', verbose_name='字典标签')
value = models.CharField(max_length=100, default='', verbose_name='字典键值')
dict_type = models.ForeignKey(
DictType,
on_delete=models.CASCADE,
related_name='dict_data',
verbose_name='字典类型'
)
status = models.BooleanField(default=True)
color_type = models.CharField(max_length=100, blank=True, default='', verbose_name='颜色类型')
css_class = models.CharField(max_length=100, blank=True, default='', verbose_name='css 样式')
class Meta:
verbose_name = '字典数据'
verbose_name_plural = '字典数据'
db_table = 'system_dict_data'
ordering = ['sort', 'id']
def __str__(self):
return self.label
class User(AbstractUser, CoreModel):
mobile = models.CharField(max_length=11, null=True, validators=[validate_mobile], db_comment="手机号")
nickname = models.CharField(max_length=50, blank=True, null=True, db_comment="昵称")
gender = models.SmallIntegerField(blank=True, null=True, default=0, db_comment='性别')
language = models.CharField('语言', max_length=20, blank=True, null=True, db_comment="语言")
city = models.CharField('城市', max_length=20, blank=True, null=True, db_comment="城市")
province = models.CharField('省份', max_length=50, blank=True, null=True, db_comment="省份")
country = models.CharField('国家', max_length=50, blank=True, null=True, db_comment="国家")
avatarUrl = models.URLField('头像', blank=True, null=True, db_comment="头像")
dept = models.ManyToManyField(
'Dept', blank=True, verbose_name='部门', db_constraint=False,
related_name='users'
)
status = models.BooleanField(default=False, verbose_name='<帐号状态>1正常 0停用', db_comment="帐号状态")
login_date = models.DateTimeField("<最后登录时间>", blank=True, null=True, db_comment="最后登录时间")
login_ip = models.GenericIPAddressField(blank=True, null=True, db_comment="最后登录IP")
class Meta:
verbose_name = '用户数据'
verbose_name_plural = verbose_name
db_table = 'system_users'

View File

@@ -0,0 +1,5 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers
from .models import Department, Menu, MenuMeta, Role

14
backend/system/tasks.py Normal file
View File

@@ -0,0 +1,14 @@
# 某个 app 目录下的 tasks.py
from celery import shared_task
@shared_task
def add(x, y):
return x + y
@shared_task
def sync_temu_order():
pass
@shared_task
def sync_temu_shipping():
pass

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

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

19
backend/system/urls.py Normal file
View File

@@ -0,0 +1,19 @@
from django.urls import include, path
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register(r'dept', views.DeptViewSet)
router.register(r'menu-meta', views.MenuMetaViewSet)
router.register(r'menu', views.MenuViewSet)
router.register(r'role', views.RoleViewSet)
router.register(r'dict_data', views.DictDataViewSet)
router.register(r'dict_type', views.DictTypeViewSet)
urlpatterns = [
path('', include(router.urls)),
path('login/', views.user.UserLogin.as_view()),
path('info/', views.user.UserInfo.as_view()),
path('codes/', views.user.Codes.as_view()),
]

View File

@@ -0,0 +1,16 @@
__all__ = [
'DeptViewSet',
'MenuViewSet',
'MenuMetaViewSet',
'RoleViewSet',
'DictDataViewSet',
'DictTypeViewSet',
]
from system.views.dict_data import DictDataViewSet
from system.views.dict_type import DictTypeViewSet
from system.views.menu import MenuViewSet, MenuMetaViewSet
from system.views.role import RoleViewSet
from system.views.dept import DeptViewSet
from system.views.user import *

View File

@@ -0,0 +1,84 @@
from datetime import timezone, datetime
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status, serializers
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.response import Response
from system.models import Dept
from utils.custom_model_viewSet import CustomModelViewSet
class DeptSerializer(serializers.ModelSerializer):
"""部门序列化器"""
children = serializers.SerializerMethodField()
status_text = serializers.SerializerMethodField()
class Meta:
model = Dept
fields = '__all__'
read_only_fields = ['id', 'create_time']
def get_children(self, obj):
"""获取子部门"""
children = obj.children.all().order_by('id')
if children:
return DeptSerializer(children, many=True).data
return []
def get_status_text(self, obj):
"""获取状态文本"""
return obj.get_status_display()
class DeptViewSet(CustomModelViewSet):
"""部门管理视图集"""
queryset = Dept.objects.filter(pid__isnull=True).order_by('id', 'status')
serializer_class = DeptSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['status', 'pid']
search_fields = ['name']
ordering_fields = ['create_time', 'name']
def perform_create(self, serializer):
# 自动设置创建时间
if 'create_time' not in serializer.validated_data:
serializer.validated_data['create_time'] = datetime.now()
serializer.save()
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
pk = kwargs['pk']
instance = Dept.objects.get(pk=pk)
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
headers = self.get_success_headers(serializer.data)
return self._build_response(
data=serializer.data,
message="ok",
status=status.HTTP_200_OK,
)
def destroy(self, request, *args, **kwargs):
pk = kwargs['pk']
instance = Dept.objects.get(pk=pk)
self.perform_destroy(instance)
return self._build_response(
message="ok",
status=status.HTTP_200_OK,
)
@action(detail=False, methods=['get'])
def tree(self, request):
"""获取部门树形结构"""
queryset = self.get_queryset().filter(pid__isnull=True)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

View File

@@ -0,0 +1,16 @@
from rest_framework import serializers, viewsets
from system.models import DictData
from utils.custom_model_viewSet import CustomModelViewSet
class DictDataSerializer(serializers.ModelSerializer):
class Meta:
model = DictData
fields = '__all__'
class DictDataViewSet(CustomModelViewSet):
queryset = DictData.objects.filter(is_deleted=False)
serializer_class = DictDataSerializer
filterset_fields = ['dict_type']

View File

@@ -0,0 +1,15 @@
from rest_framework import serializers, viewsets
from system.models import DictType
from utils.custom_model_viewSet import CustomModelViewSet
class DictTypeSerializer(serializers.ModelSerializer):
class Meta:
model = DictType
fields = '__all__'
class DictTypeViewSet(CustomModelViewSet):
queryset = DictType.objects.filter(is_deleted=False)
serializer_class = DictTypeSerializer

View File

@@ -0,0 +1,131 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, serializers, status
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.response import Response
from system.models import Menu, MenuMeta
from utils.custom_model_viewSet import CustomModelViewSet
class MenuMetaSerializer(serializers.ModelSerializer):
"""菜单元数据序列化器"""
class Meta:
model = MenuMeta
fields = '__all__'
class MenuSerializer(serializers.ModelSerializer):
"""菜单序列化器"""
parent = serializers.CharField(source='pid.name', read_only=True)
meta = MenuMetaSerializer()
children = serializers.SerializerMethodField()
status_text = serializers.SerializerMethodField()
type_text = serializers.SerializerMethodField()
class Meta:
model = Menu
fields = '__all__'
read_only_fields = ['id', 'create_time', 'update_time']
def get_children(self, obj):
"""获取子菜单"""
children = obj.children.all()
if children:
return MenuSerializer(children, many=True).data
return []
def get_status_text(self, obj):
"""获取状态文本"""
return obj.get_status_display()
def get_type_text(self, obj):
"""获取菜单类型文本"""
return obj.get_type_display()
def create(self, validated_data):
"""创建菜单及关联的元数据"""
meta_data = validated_data.pop('meta')
meta = MenuMeta.objects.create(**meta_data)
menu = Menu.objects.create(meta=meta, **validated_data)
return menu
def update(self, instance, validated_data):
"""更新菜单及关联的元数据"""
meta_data = validated_data.pop('meta', {})
meta_serializer = self.fields['meta']
meta_serializer.update(instance.meta, meta_data)
return super().update(instance, validated_data)
class MenuMetaViewSet(viewsets.ModelViewSet):
"""菜单元数据视图集"""
queryset = MenuMeta.objects.all()
serializer_class = MenuMetaSerializer
class MenuViewSet(CustomModelViewSet):
"""菜单管理视图集"""
queryset = Menu.objects.filter(pid__isnull=True).order_by('id', 'status')
serializer_class = MenuSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['status', 'type', 'pid', 'name']
search_fields = ['name', 'path', 'auth_code']
ordering_fields = ['meta__order', 'create_time']
@action(detail=False, methods=['get'])
def tree(self, request):
"""获取菜单树形结构"""
queryset = self.get_queryset().filter(pid__isnull=True)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'], url_path='name-exists')
def name_exists(self, request):
return self._build_response()
@action(detail=False, methods=['get'], url_path='name-search')
def name_search(self, request):
name = request.GET.get('name')
pk = request.GET.get('id', None)
queryset = Menu.objects.all()
if pk:
queryset = queryset.exclude(pk=pk)
if name:
queryset = queryset.filter(name=name)
has_menu_name = queryset.exists()
print(has_menu_name, 'has_menu_name')
return self._build_response(data=has_menu_name)
@action(detail=False, methods=['get'], url_path='path-exists')
def path_exists(self, request):
return self._build_response()
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
pk = kwargs['pk']
instance = Menu.objects.get(pk=pk)
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
headers = self.get_success_headers(serializer.data)
return self._build_response(
data=serializer.data,
message="ok",
status=status.HTTP_200_OK,
)
def destroy(self, request, *args, **kwargs):
pk = kwargs['pk']
instance = Menu.objects.get(pk=pk)
self.perform_destroy(instance)
return self._build_response(
message="ok",
status=status.HTTP_200_OK,
)

View File

@@ -0,0 +1,92 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status, serializers
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from system.models import RolePermission, Menu, Role
from utils.custom_model_viewSet import CustomModelViewSet
class RoleSerializer(serializers.ModelSerializer):
"""角色序列化器"""
permissions = serializers.PrimaryKeyRelatedField(
queryset=Menu.objects.all(),
many=True,
required=False
)
status_text = serializers.SerializerMethodField()
class Meta:
model = Role
fields = '__all__'
read_only_fields = ['id', 'create_time']
def get_status_text(self, obj):
"""获取状态文本"""
return obj.get_status_display()
class RoleViewSet(CustomModelViewSet):
"""角色管理视图集"""
queryset = Role.objects.all()
serializer_class = RoleSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['status']
search_fields = ['name']
ordering_fields = ['create_time']
@action(detail=True, methods=['post'])
def assign_permissions(self, request, pk=None):
"""分配角色权限"""
role = self.get_object()
menu_ids = request.data.get('menu_ids', [])
# 清除原有权限
role.permissions.clear()
# 添加新权限
for menu_id in menu_ids:
menu = get_object_or_404(Menu, id=menu_id)
RolePermission.objects.create(role=role, menu=menu)
serializer = self.get_serializer(role)
return Response(serializer.data)
def create(self, request, *args, **kwargs):
# 获取请求数据
data = request.data.copy()
permissions = data.pop('permissions', []) # 提取权限列表
# 创建角色(不包含权限)
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
role = serializer.save()
# 处理权限关联(可根据需求自定义)
if permissions:
try:
# 验证权限ID是否存在
valid_permissions = Menu.objects.filter(id__in=permissions)
# 创建中间表记录(如果需要保存额外字段)
for menu in valid_permissions:
RolePermission.objects.create(
role=role,
menu=menu,
)
except Exception as e:
# 如果关联失败,删除已创建的角色
role.delete()
return Response(
{'error': f'权限关联失败: {str(e)}'},
status=status.HTTP_400_BAD_REQUEST
)
return self._build_response(
data=serializer.data,
message="ok",
status=status.HTTP_200_OK,
)

View File

@@ -0,0 +1,79 @@
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from system.models import User
from utils.custom_model_viewSet import CustomModelViewSet
class UserSerializer(CustomModelViewSet):
class Meta:
model = User
exclude = ('password',)
class UserLogin(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
"code": 0,
"data": {
"id": user.id,
"password": user.password,
"realName": user.nickname,
"roles": [
"super"
],
"username": user.username,
"accessToken": token.key
},
"error": None,
"message": "ok"
})
class UserInfo(APIView):
def get(self, request, *args, **kwargs):
user = self.request.user
return Response({
"code": 0,
"data": {
"id": user.id,
"realName": user.username,
"roles": [
"super"
],
"username": user.username,
},
"error": None,
"message": "ok"
})
class Codes(APIView):
def get(self, request, *args, **kwargs):
return Response({
"code": 0,
"data": [
"AC_100100",
"AC_100110",
"AC_100120",
"AC_100010"
],
"error": None,
"message": "ok"
})
class UserViewSet(ModelViewSet):
queryset = User.objects.all().order_by('id')
serializer_class = UserSerializer