initial commit
This commit is contained in:
0
apps/user/__init__.py
Normal file
0
apps/user/__init__.py
Normal file
BIN
apps/user/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
apps/user/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
apps/user/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/admin.cpython-313.pyc
Normal file
BIN
apps/user/__pycache__/admin.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/admin.cpython-38.pyc
Normal file
BIN
apps/user/__pycache__/admin.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/apps.cpython-313.pyc
Normal file
BIN
apps/user/__pycache__/apps.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/apps.cpython-38.pyc
Normal file
BIN
apps/user/__pycache__/apps.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/controllers.cpython-313.pyc
Normal file
BIN
apps/user/__pycache__/controllers.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/controllers.cpython-38.pyc
Normal file
BIN
apps/user/__pycache__/controllers.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/models.cpython-313.pyc
Normal file
BIN
apps/user/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/models.cpython-38.pyc
Normal file
BIN
apps/user/__pycache__/models.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/schema.cpython-313.pyc
Normal file
BIN
apps/user/__pycache__/schema.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/__pycache__/schema.cpython-38.pyc
Normal file
BIN
apps/user/__pycache__/schema.cpython-38.pyc
Normal file
Binary file not shown.
6
apps/user/admin.py
Normal file
6
apps/user/admin.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from apps.user.models import Users
|
||||
|
||||
# 在这里将数据库表注册到admin中
|
||||
admin.site.register([Users])
|
||||
|
||||
5
apps/user/apps.py
Normal file
5
apps/user/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class UserConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'apps.user'
|
||||
182
apps/user/controllers.py
Normal file
182
apps/user/controllers.py
Normal file
@@ -0,0 +1,182 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja.pagination import paginate
|
||||
from utils.chen_pagination import MyPagination
|
||||
from ninja_extra.permissions import IsAuthenticated, IsAdminUser
|
||||
from ninja import Query
|
||||
from django.db import transaction
|
||||
from django.contrib.auth import authenticate
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja_jwt.tokens import RefreshToken
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_jwt.controller import TokenObtainPairController
|
||||
from ninja_jwt import schema
|
||||
from typing import List
|
||||
from utils.chen_response import ChenResponse
|
||||
from apps.user.schema import UserInfoOutSchema, CreateUserSchema, CreateUserOutSchema, UserRetrieveInputSchema, \
|
||||
UserRetrieveOutSchema, UpdateDeleteUserSchema, UpdateDeleteUserOutSchema, DeleteUserSchema, LogOutSchema, \
|
||||
LogInputSchema, LogDeleteInSchema, AdminModifyPasswordSchema
|
||||
from apps.user.models import TableOperationLog, Users as UserClass
|
||||
from apps.project.models import Project
|
||||
# 工具函数
|
||||
from utils.chen_crud import update, multi_delete
|
||||
from apps.user.tools.ldap_tools import load_ldap_users
|
||||
# 导入登录日志函数
|
||||
from utils.log_util.request_util import save_login_log
|
||||
|
||||
Users: UserClass = get_user_model() # type:ignore
|
||||
|
||||
# 定义用户登录接口,包含token刷新和生成
|
||||
@api_controller("/system", tags=['用户token控制和登录接口'])
|
||||
class UserTokenController(TokenObtainPairController):
|
||||
auto_import = True
|
||||
|
||||
@route.post("/login", url_name='login')
|
||||
def obtain_token(self, user_token: schema.TokenObtainPairSerializer):
|
||||
"""新版本有特性,后期修改"""
|
||||
# 注意TokenObtainPairSerializer是老版本,所以兼容,本质是TokenObtainPairInputSchema
|
||||
user: UserClass = user_token._user
|
||||
if user:
|
||||
# 判断是否为启用状态
|
||||
if user.status == '2':
|
||||
return ChenResponse(status=500, code=500, message='账号已被禁用,请联系管理员...')
|
||||
save_login_log(request=self.context.request, user=user) # 保存登录日志
|
||||
refresh = RefreshToken.for_user(user)
|
||||
token = refresh.access_token # type:ignore
|
||||
return ChenResponse(code=200,
|
||||
data={'token': str(token), 'refresh': str(refresh),
|
||||
'token_exp_data': datetime.fromtimestamp(token["exp"], tz=timezone.utc)})
|
||||
|
||||
@route.get("/getInfo", response=UserInfoOutSchema, url_name="get_info", auth=JWTAuth())
|
||||
def get_user_info(self):
|
||||
# 直接按照Schema返回
|
||||
return self.context.request.auth
|
||||
|
||||
@route.post("/logout", url_name="logout", auth=JWTAuth())
|
||||
def logout(self):
|
||||
return ChenResponse(code=200, message='退出登录成功')
|
||||
|
||||
# 定义system/user用户管理接口
|
||||
@api_controller("/system/user", tags=['用户管理'], auth=JWTAuth())
|
||||
class UserManageController(ControllerBase):
|
||||
# 用户创建接口
|
||||
@route.post("/save", response=CreateUserOutSchema, url_name="user_create", auth=JWTAuth(),
|
||||
permissions=[IsAuthenticated, IsAdminUser])
|
||||
def create_user(self, user_schema: CreateUserSchema):
|
||||
user = user_schema.create()
|
||||
return user
|
||||
|
||||
# 给前端传所有用户当做字典
|
||||
@route.get('/list', response=List[UserRetrieveOutSchema], url_name="user_list", auth=None)
|
||||
@transaction.atomic
|
||||
def list_user(self, project_id: int = None):
|
||||
"""如果传了project_id则返回项目中的成员而非全部用户"""
|
||||
qs = Users.objects.all()
|
||||
if project_id is not None:
|
||||
project_obj = get_object_or_404(Project, id=project_id)
|
||||
all_member: list = project_obj.member
|
||||
# 将member和duty_person联合
|
||||
if project_obj.duty_person not in project_obj.member:
|
||||
all_member.append(project_obj.duty_person)
|
||||
qs = qs.filter(name__in=all_member)
|
||||
return qs
|
||||
|
||||
# 用户检索接口
|
||||
@route.get("/index", response=List[UserRetrieveOutSchema])
|
||||
@paginate(MyPagination)
|
||||
def index_user(self, filters: UserRetrieveInputSchema = Query(...)):
|
||||
# 重要,处理前端不传值为None的情况
|
||||
for attr, value in filters.__dict__.items():
|
||||
if getattr(filters, attr) is None:
|
||||
setattr(filters, attr, '')
|
||||
start_time = self.context.request.GET.get('create_datetime[0]')
|
||||
if start_time is None:
|
||||
start_time = "2000-01-01"
|
||||
end_time = self.context.request.GET.get('create_datetime[1]')
|
||||
if end_time is None:
|
||||
end_time = '8000-01-01'
|
||||
date_list = [start_time, end_time]
|
||||
qs = Users.objects.filter(name__icontains=filters.name, username__icontains=filters.username,
|
||||
phone__icontains=filters.phone, status__contains=filters.status,
|
||||
create_datetime__range=date_list).order_by('-create_datetime')
|
||||
return qs
|
||||
|
||||
@route.put("/update/{user_id}", response=UpdateDeleteUserOutSchema, permissions=[IsAuthenticated, IsAdminUser],
|
||||
url_name="user-update")
|
||||
def update_user(self, user_id: int, payload: UpdateDeleteUserSchema):
|
||||
if payload.username == "superAdmin":
|
||||
return ChenResponse(code=400, status=400, message="无法编辑,唯一管理员账号")
|
||||
payload.validate_unique_username(user_id)
|
||||
update_user = update(self.context.request, user_id, payload, Users)
|
||||
return {"message": "用户更新成功"}
|
||||
|
||||
@route.delete("/delete", permissions=[IsAuthenticated, IsAdminUser], url_name="user-delete")
|
||||
def delete_user(self, data: DeleteUserSchema):
|
||||
ids = data.ids
|
||||
# 去掉删除创始人
|
||||
for item in ids:
|
||||
if item == 1:
|
||||
ids.pop(item)
|
||||
multi_delete(ids, Users)
|
||||
return ChenResponse(code=200, status=200, message="删除成功")
|
||||
|
||||
# 管理员改变用户状态是否停用/启用
|
||||
@route.get('/change_status', auth=JWTAuth(), permissions=[IsAuthenticated, IsAdminUser], url_name='user-change')
|
||||
def change_user_status(self, user_status: str, userId: int):
|
||||
user = Users.objects.filter(id=userId).first()
|
||||
if not user:
|
||||
return ChenResponse(status=400, code=400, message='用户未找到')
|
||||
if user.id == 1:
|
||||
return ChenResponse(status=400, code=400, message='管理员不能被禁用,此操作无效')
|
||||
user.status = user_status
|
||||
user.save()
|
||||
return user.status
|
||||
|
||||
@route.post("/modifyPassword", auth=JWTAuth(), permissions=[IsAuthenticated, IsAdminUser])
|
||||
def modify_password(self, payload: AdminModifyPasswordSchema):
|
||||
user: UserClass = self.context.request.user # type:ignore
|
||||
if user:
|
||||
# 判断就密码是否正确
|
||||
user_old = authenticate(username=user.username, password=payload.oldPassword)
|
||||
if not user_old:
|
||||
return ChenResponse(status=500, code=500, message='旧密码错误,请检查')
|
||||
user.set_password(payload.newPassword)
|
||||
user.save()
|
||||
return ChenResponse(status=200, code=200, message='管理员修改密码成功')
|
||||
|
||||
# 用户登录后动态读取LDAP用户录入数据
|
||||
@route.get("/ldap", url_name='user-ldap')
|
||||
def load_ldap(self):
|
||||
try:
|
||||
load_ldap_users()
|
||||
return ChenResponse(status=200, code=200, message='连接LDAP服务器成功,同步用户数据')
|
||||
except Exception as exc:
|
||||
print(exc)
|
||||
return ChenResponse(status=200, code=200, message='欢迎您,正在外网访问')
|
||||
|
||||
# 操作日志接口
|
||||
@api_controller("/system/log", tags=['日志记录'], auth=JWTAuth())
|
||||
class LogController(ControllerBase):
|
||||
@route.get("/operation_list", url_name="log_list", response=List[LogOutSchema], auth=None)
|
||||
@paginate(MyPagination)
|
||||
def log_list(self, data: Query[LogInputSchema]):
|
||||
for attr, value in data.model_dump().items():
|
||||
if getattr(data, attr) is None:
|
||||
setattr(data, attr, '')
|
||||
logs = TableOperationLog.objects.values('id', 'user__username', 'operate_obj', 'create_datetime',
|
||||
'operate_des').order_by(
|
||||
'-create_datetime')
|
||||
# 根据条件搜索
|
||||
logs = logs.filter(user__username__icontains=data.user, create_datetime__range=data.create_datetime)
|
||||
return logs
|
||||
|
||||
@route.get('/operation_delete', url_name='log_delete', permissions=[IsAuthenticated, IsAdminUser], auth=JWTAuth())
|
||||
def log_delete(self, data: LogDeleteInSchema = Query(...)):
|
||||
time = datetime.now() - timedelta(days=data.day)
|
||||
log_qs = TableOperationLog.objects.filter(create_datetime__lt=time)
|
||||
log_qs.delete()
|
||||
if data.day > 0:
|
||||
return ChenResponse(message=f'删除{data.day}天前数据成功')
|
||||
else:
|
||||
return ChenResponse(message='全部日志删除成功')
|
||||
79
apps/user/migrations/0001_initial.py
Normal file
79
apps/user/migrations/0001_initial.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-03 10:30
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.models
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Users',
|
||||
fields=[
|
||||
('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')),
|
||||
('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')),
|
||||
('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')),
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('remark', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
|
||||
('update_datetime', models.DateField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_datetime', models.DateField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('sort', models.IntegerField(blank=True, default=1, help_text='显示排序', null=True, verbose_name='显示排序')),
|
||||
('username', models.CharField(db_index=True, help_text='用户账号', max_length=150, unique=True, verbose_name='用户账号')),
|
||||
('name', models.CharField(help_text='姓名', max_length=40, verbose_name='姓名')),
|
||||
('avatar', models.TextField(blank=True, help_text='头像', null=True, verbose_name='头像')),
|
||||
('email', models.EmailField(blank=True, help_text='邮箱', max_length=255, null=True, verbose_name='邮箱')),
|
||||
('status', models.CharField(default='1', help_text='status', max_length=15, verbose_name='启用状态')),
|
||||
('job', models.CharField(blank=True, help_text='工作', max_length=255, null=True, verbose_name='工作')),
|
||||
('jobName', models.CharField(blank=True, help_text='工作名称', max_length=255, null=True, verbose_name='工作名称')),
|
||||
('organization', models.CharField(blank=True, help_text='工作组织', max_length=255, null=True, verbose_name='工作组织')),
|
||||
('location', models.CharField(blank=True, help_text='住地', max_length=255, null=True, verbose_name='住地')),
|
||||
('locationName', models.CharField(blank=True, help_text='住地名称', max_length=255, null=True, verbose_name='住地名称')),
|
||||
('introduction', models.CharField(blank=True, help_text='自我介绍', max_length=255, null=True, verbose_name='自我介绍')),
|
||||
('personalWebsite', models.CharField(blank=True, help_text='个人网站', max_length=255, null=True, verbose_name='个人网站')),
|
||||
('phone', models.CharField(blank=True, default='18888888888', help_text='电话', max_length=255, null=True, verbose_name='电话')),
|
||||
('accountId', models.CharField(blank=True, default='1', help_text='用户标识', max_length=255, null=True, verbose_name='用户标识')),
|
||||
('role', models.CharField(blank=True, default='user', help_text='角色', max_length=64, null=True, verbose_name='角色')),
|
||||
('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')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户表',
|
||||
'verbose_name_plural': '用户表',
|
||||
'db_table': 'user_user',
|
||||
'ordering': ('-create_datetime',),
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TableOperationLog',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('operate_obj', models.CharField(default='未关联对象', help_text='操作对象', max_length=256, verbose_name='操作对象')),
|
||||
('operate_des', models.CharField(default='未有操作详情', help_text='操作详情', max_length=1024, verbose_name='操作详情')),
|
||||
('user', models.ForeignKey(blank=True, db_constraint=False, help_text='操作人员', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ruser', related_query_name='quser', to=settings.AUTH_USER_MODEL, verbose_name='操作人员')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户操作日志表',
|
||||
'verbose_name_plural': '用户操作日志表',
|
||||
'db_table': 'operation_log',
|
||||
'ordering': ('-create_datetime',),
|
||||
},
|
||||
),
|
||||
]
|
||||
19
apps/user/migrations/0002_alter_users_email.py
Normal file
19
apps/user/migrations/0002_alter_users_email.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-15 13:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='users',
|
||||
name='email',
|
||||
field=models.EmailField(blank=True, default=1, max_length=254, verbose_name='email address'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
17
apps/user/migrations/0003_remove_users_email.py
Normal file
17
apps/user/migrations/0003_remove_users_email.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-15 13:38
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user', '0002_alter_users_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='users',
|
||||
name='email',
|
||||
),
|
||||
]
|
||||
18
apps/user/migrations/0004_users_email.py
Normal file
18
apps/user/migrations/0004_users_email.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-15 13:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user', '0003_remove_users_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='users',
|
||||
name='email',
|
||||
field=models.EmailField(blank=True, max_length=254, verbose_name='email address'),
|
||||
),
|
||||
]
|
||||
0
apps/user/migrations/__init__.py
Normal file
0
apps/user/migrations/__init__.py
Normal file
BIN
apps/user/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
BIN
apps/user/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/migrations/__pycache__/0001_initial.cpython-38.pyc
Normal file
BIN
apps/user/migrations/__pycache__/0001_initial.cpython-38.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
apps/user/migrations/__pycache__/0004_users_email.cpython-38.pyc
Normal file
BIN
apps/user/migrations/__pycache__/0004_users_email.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/user/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
apps/user/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/migrations/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
apps/user/migrations/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
56
apps/user/models.py
Normal file
56
apps/user/models.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from django.contrib.auth.models import AbstractUser, Group
|
||||
from django.db import models
|
||||
from utils.models import CoreModel
|
||||
|
||||
STATUS_CHOICES = (
|
||||
(0, '禁用'),
|
||||
(1, '启用')
|
||||
)
|
||||
|
||||
class Users(AbstractUser, CoreModel):
|
||||
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name='用户账号',
|
||||
help_text="用户账号")
|
||||
name = models.CharField(max_length=40, verbose_name="姓名", help_text="姓名")
|
||||
avatar = models.TextField(verbose_name="头像", null=True, blank=True, help_text="头像")
|
||||
status = models.CharField(max_length=15, verbose_name='启用状态', help_text="status", default='1')
|
||||
job = models.CharField(max_length=255, verbose_name='工作', null=True, blank=True, help_text='工作')
|
||||
jobName = models.CharField(max_length=255, verbose_name='工作名称', null=True, blank=True, help_text='工作名称')
|
||||
organization = models.CharField(max_length=255, verbose_name='工作组织', null=True, blank=True,
|
||||
help_text='工作组织')
|
||||
location = models.CharField(max_length=255, verbose_name='住地', null=True, blank=True, help_text='住地')
|
||||
locationName = models.CharField(max_length=255, verbose_name='住地名称', null=True, blank=True,
|
||||
help_text='住地名称')
|
||||
introduction = models.CharField(max_length=255, verbose_name='自我介绍', null=True, blank=True,
|
||||
help_text='自我介绍')
|
||||
personalWebsite = models.CharField(max_length=255, verbose_name='个人网站', null=True, blank=True,
|
||||
help_text='个人网站')
|
||||
phone = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话", default='18888888888')
|
||||
accountId = models.CharField(max_length=255, verbose_name="用户标识", null=True, blank=True, help_text="用户标识", default='1')
|
||||
role = models.CharField(max_length=64, verbose_name="角色", null=True, blank=True, help_text="角色", default='user')
|
||||
|
||||
def __str__(self):
|
||||
return f'用户账号:{self.username}-用户名:{self.name}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'user_user'
|
||||
verbose_name = '用户表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
class TableOperationLog(models.Model):
|
||||
create_datetime = models.DateTimeField(auto_now_add=True, null=True, blank=True, help_text="创建时间",
|
||||
verbose_name="创建时间")
|
||||
user = models.ForeignKey(to="Users", db_constraint=False, related_name="ruser", on_delete=models.CASCADE,
|
||||
verbose_name='操作人员', help_text='操作人员', related_query_name='quser', null=True,
|
||||
blank=True)
|
||||
# 2.操作的对象
|
||||
operate_obj = models.CharField(max_length=256, verbose_name='操作对象', default='未关联对象', help_text='操作对象')
|
||||
# 3.操作详情
|
||||
operate_des = models.CharField(max_length=1024, verbose_name='操作详情', default='未有操作详情',
|
||||
help_text='操作详情')
|
||||
|
||||
class Meta:
|
||||
db_table = 'operation_log'
|
||||
verbose_name = '用户操作日志表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
115
apps/user/schema.py
Normal file
115
apps/user/schema.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from apps.user.models import Users
|
||||
from django.contrib.auth.models import Group
|
||||
from ninja_schema import ModelSchema, model_validator, Schema
|
||||
from ninja_extra.exceptions import APIException
|
||||
from ninja_extra import status
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from ninja import Field
|
||||
|
||||
UserModel = Users
|
||||
|
||||
# 定义用户名异常
|
||||
class UsernameException(APIException):
|
||||
status_code = status.HTTP_400_BAD_REQUEST
|
||||
default_detail = "用户名已存在,注册失败"
|
||||
default_code = 400
|
||||
|
||||
class GroupSchema(ModelSchema):
|
||||
class Config:
|
||||
# 因为保证唯一性,所以ninja_schema使用set集合
|
||||
model = Group
|
||||
# 注意ninja_schema(include)和ninja(model_fields)
|
||||
include = ("name",)
|
||||
|
||||
# schema:作用于创建用户请求
|
||||
class CreateUserSchema(ModelSchema):
|
||||
class Config:
|
||||
model = UserModel
|
||||
include = ('username', 'name', 'password', 'phone', 'status',)
|
||||
|
||||
# username判重
|
||||
@model_validator("username")
|
||||
@classmethod
|
||||
def unique_username(cls, value):
|
||||
if UserModel.objects.filter(username__icontains=value).exists():
|
||||
raise UsernameException()
|
||||
return value
|
||||
|
||||
def create(self):
|
||||
# 注意这里使用exclude_none,dict()方式属于pydantic
|
||||
return UserModel.objects.create_user(**self.dict(exclude_none=True), email='xxx@qq.com')
|
||||
|
||||
# schema:作用于创建用户后response
|
||||
class CreateUserOutSchema(ModelSchema):
|
||||
class Config:
|
||||
model = UserModel
|
||||
exclude = ('password',)
|
||||
|
||||
# schema:定义前端查询用户信息的输出
|
||||
class UserInfoOutSchema(ModelSchema):
|
||||
class Config:
|
||||
model = UserModel
|
||||
exclude = ("password",)
|
||||
|
||||
# schema:作用于用户检索以及其他
|
||||
class UserRetrieveInputSchema(ModelSchema):
|
||||
class Config:
|
||||
model = UserModel
|
||||
include = ("name", "username", "phone", "status",)
|
||||
# ninja_schema的可选字段
|
||||
optional = ("name", "username", "phone", "status",)
|
||||
|
||||
# schema:作用于检索后的输出定义
|
||||
class UserRetrieveOutSchema(ModelSchema):
|
||||
class Config:
|
||||
model = UserModel
|
||||
exclude = ("password",)
|
||||
|
||||
# 删除和更新用户
|
||||
class UpdateDeleteUserSchema(ModelSchema):
|
||||
class Config:
|
||||
model = UserModel
|
||||
include = ("name", "username", "phone", "status")
|
||||
|
||||
def validate_unique_username(self, id: int):
|
||||
user_filters = UserModel.objects.filter(username=self.username)
|
||||
if len(user_filters) > 1:
|
||||
raise UsernameException()
|
||||
elif len(user_filters) == 1:
|
||||
if user_filters[0].id == id:
|
||||
return
|
||||
else:
|
||||
raise UsernameException()
|
||||
else:
|
||||
return
|
||||
|
||||
class UpdateDeleteUserOutSchema(Schema):
|
||||
message: str
|
||||
|
||||
class DeleteUserSchema(Schema):
|
||||
ids: List[int]
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~日志schema~~~~~~~~~~~~~~~~~~~~
|
||||
# 操作日志的schema
|
||||
class LogOutSchema(Schema):
|
||||
id: int
|
||||
user: str = Field(..., alias='user__username')
|
||||
operate_obj: str
|
||||
create_datetime: datetime
|
||||
operate_des: str
|
||||
|
||||
# 操作日志的查询
|
||||
class LogInputSchema(Schema):
|
||||
user: str = Field("", alias='user')
|
||||
create_datetime: List = ['2000-01-01', '9999-01-01']
|
||||
|
||||
# 操作日志的删除输入
|
||||
class LogDeleteInSchema(Schema):
|
||||
day: int = Field(7, ge=0, description='删除多少天前的数据')
|
||||
|
||||
# 管理员修改密码
|
||||
class AdminModifyPasswordSchema(Schema):
|
||||
newPassword: str
|
||||
newPassword_confirmation: str
|
||||
oldPassword: str
|
||||
3
apps/user/tests.py
Normal file
3
apps/user/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
BIN
apps/user/tools/__pycache__/ldap_tools.cpython-313.pyc
Normal file
BIN
apps/user/tools/__pycache__/ldap_tools.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/user/tools/__pycache__/ldap_tools.cpython-38.pyc
Normal file
BIN
apps/user/tools/__pycache__/ldap_tools.cpython-38.pyc
Normal file
Binary file not shown.
56
apps/user/tools/ldap_tools.py
Normal file
56
apps/user/tools/ldap_tools.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import ldap
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
def load_ldap_users(url='ldap://dns.paisat.cn:389',
|
||||
dn="CN=Administrator,CN=Users,DC=sstc,DC=ctu",
|
||||
pwd="WXWX2019!!!!!!",
|
||||
search_dn="OU=ALL,DC=sstc,DC=ctu",
|
||||
search_filter='(&(sAMAccountName=*))'):
|
||||
Users = get_user_model()
|
||||
|
||||
ldap_server = ldap.initialize(url)
|
||||
ldap_server.simple_bind_s(dn, pwd)
|
||||
ldap_users = ldap_server.search_ext_s(search_dn,
|
||||
ldap.SCOPE_SUBTREE,
|
||||
search_filter)
|
||||
|
||||
temp_users = []
|
||||
for user in ldap_users:
|
||||
username_field = user[-1]['sAMAccountName'][0]
|
||||
email_field = user[-1].get('mail', username_field + b'@sstc.ctu')[0]
|
||||
if isinstance(email_field, int):
|
||||
email_field = username_field + b'@sstc.ctu'
|
||||
user_dict = {
|
||||
'username': username_field.decode(),
|
||||
'name': user[-1]['name'][0].decode(),
|
||||
'email': email_field.decode(),
|
||||
}
|
||||
temp_users.append(user_dict)
|
||||
db_user = Users.objects.filter(username=user_dict['username'])
|
||||
exsits = db_user.exists()
|
||||
if exsits:
|
||||
# 如果存在则更新
|
||||
update_flag = False
|
||||
c_user = db_user.first()
|
||||
if c_user != user_dict['username']:
|
||||
c_user.username = user_dict['username']
|
||||
update_flag = True
|
||||
if c_user.name != user_dict['name']:
|
||||
c_user.name = user_dict['name']
|
||||
update_flag = True
|
||||
if c_user.email != user_dict['email']:
|
||||
c_user.email = user_dict['email']
|
||||
update_flag = True
|
||||
if update_flag:
|
||||
c_user.save()
|
||||
else:
|
||||
user_dict['remark'] = '自动同步LDAP数据用户'
|
||||
user_dict['status'] = '1'
|
||||
user_dict['phone'] = '18888888888'
|
||||
user_dict['role'] = 'user'
|
||||
user_dict['accountId'] = 'user'
|
||||
user_single = Users.objects.create(**user_dict)
|
||||
user_single.set_password('wxwx2018!!!')
|
||||
user_single.save()
|
||||
# 6月3日新增组别
|
||||
|
||||
3
apps/user/views.py
Normal file
3
apps/user/views.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
Reference in New Issue
Block a user