Skip to content

Commit

Permalink
新增用户管理页面
Browse files Browse the repository at this point in the history
  • Loading branch information
wuranxu committed Dec 28, 2021
1 parent bf374ac commit 3dcf1eb
Show file tree
Hide file tree
Showing 23 changed files with 465 additions and 166 deletions.
1 change: 0 additions & 1 deletion app/core/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,6 @@ def replace_cls(self, params: dict, cls, *fields: Any):
def replace_args(self, params, data: TestCase, constructors: List[Constructor], asserts: List[TestCaseAsserts]):
self.replace_testcase(params, data)
self.replace_constructors(params, constructors)
# TODO 替换后置条件变量
self.replace_asserts(params, asserts)

def replace_testcase(self, params: dict, data: TestCase):
Expand Down
56 changes: 52 additions & 4 deletions app/crud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,22 @@ def query_wrapper(cls, condition=None, **kwargs):
conditions = condition if condition else list()
if getattr(cls.model, "deleted_at", None):
conditions.append(getattr(cls.model, "deleted_at") == 0)
desc = kwargs.get("desc")
if desc:
# 需要去掉desc,不然会影响之前的sql执行
kwargs.pop("desc")
# 遍历参数,当参数不为None的时候传递
for k, v in kwargs.items():
# 判断是否是like的情况
like = isinstance(v, str) and len(v) > 2 and (v.startswith("%") or v.endswith("%"))
# 如果是like模式,则使用Model.字段.like 否则用 Model.字段 等于
DatabaseHelper.where(v, getattr(cls.model, k).like(v) if like else getattr(cls.model, k) == v,
conditions)
return select(cls.model).where(*conditions)
sql = select(cls.model).where(*conditions)
if desc and isinstance(desc, list):
for d in desc:
sql = getattr(sql, "order_by")(d)
return sql

@classmethod
async def query_record(cls, session=None, **kwargs):
Expand Down Expand Up @@ -180,7 +188,17 @@ async def insert_log(cls, session, user, mode, now, old=None, key=None, changed=

@classmethod
async def get_diff(cls, session, mode, now, old, changed):
"""
根据新旧model获取2者的diff
:param session:
:param mode:
:param now:
:param old:
:param changed:
:return:
"""
fields = getattr(now, Config.FIELD, None)
# 根据要展示的字段数量(__show__)获取title数据
fields_number = getattr(now, Config.SHOW_FIELD, 1)
if fields:
# 必须要展示至少1个字段
Expand All @@ -205,6 +223,15 @@ async def get_diff(cls, session, mode, now, old, changed):

@classmethod
async def fetch_id_with_name(cls, session, id_field, name_field, old_id, new_id):
"""
通用方法,通过id查询name等字段数据
:param session:
:param id_field:
:param name_field:
:param old_id:
:param new_id:
:return:
"""
cls_ = id_field.parent.class_
if old_id is None:
data = await session.execute(select(cls_).where(getattr(cls_, id_field.name) == new_id))
Expand All @@ -216,19 +243,33 @@ async def fetch_id_with_name(cls, session, id_field, name_field, old_id, new_id)
old_value, new_value = old_id, new_id
for d in data.scalars():
if getattr(d, id_field.name, None) == old_id:
old_value = getattr(d, name_field.name, old_value)
new_value = getattr(d, name_field.name, old_value)
else:
new_value = getattr(d, name_field.name, new_value)
old_value = getattr(d, name_field.name, new_value)
return new_value, old_value

@classmethod
def get_json_field(cls, field):
"""
遇到datetime等类型,进行转换
:param field:
:return:
"""
if isinstance(field, datetime):
return field.strftime("%Y-%m-%d %H:%M:%S")
return field

@classmethod
async def get_field_alias(cls, session, relation: Tuple[PityRelationField], name, now, old=None):
"""
获取别名操作,如果字段是别的表的主键,则还需要根据此字段查询别的表的对应字段
:param session:
:param relation: relation有2个值,第一个值是别的表对应的主键,第二个值是要显示的字段
:param name:
:param now:
:param old:
:return:
"""
alias = getattr(now, Config.ALIAS, {})
current_value = getattr(now, name, None)
current_value = cls.get_json_field(current_value)
Expand All @@ -241,17 +282,24 @@ async def get_field_alias(cls, session, relation: Tuple[PityRelationField], name
if r.foreign is None:
return dict(name=alias.get(name, name), old=old_value, now=current_value)
if callable(r.foreign):
# 说明是function
# foreign支持方法和数据库其他表,如果callable为True 说明是function
# 参考 ProjectRoleEnum.name方法 里面将int转为具体角色的方法
real_value = r.foreign(current_value)
real_old_value = r.foreign(old_value)
return dict(name=alias.get(name, name), old=real_old_value, now=real_value)
# 更新字段
id_field, name_field = r.foreign
current, old = await cls.fetch_id_with_name(session, id_field, name_field, old_value, current_value)
return dict(name=alias.get(name, name), old=old, now=current)
return dict(name=alias.get(name, name), old=old_value, now=current_value)

@classmethod
async def get_fields(cls, model):
"""
遍历字段,排除掉被忽略的字段
:param model:
:return:
"""
ans = []
fields = getattr(model, Config.FIELD, None)
fields = [x.name for x in fields] if fields else list()
Expand Down
124 changes: 96 additions & 28 deletions app/crud/auth/UserDao.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,86 @@

from app.middleware.Jwt import UserToken
from app.middleware.RedisManager import RedisHelper
from app.models import Session, async_session
from app.models import Session, async_session, DatabaseHelper
from app.models.schema.user import UserUpdateForm
from app.models.user import User
from app.utils.logger import Log
from config import Config


class UserDao(object):
log = Log("UserDao")

@staticmethod
def register_for_github(username, name, email, avatar):
@RedisHelper.up_cache("user_list")
async def update_user(user_info: UserUpdateForm, user_id: int):
"""
变更用户的接口,主要用于用户管理页面(为管理员提供)
:param user_id:
:param user_info:
:return:
"""
try:
with Session() as session:
user = session.query(User).filter(or_(User.username == username, User.email == email)).first()
if user:
# 如果存在,则给用户更新信息
user.last_login_at = datetime.now()
user.name = name
user.avatar = avatar
else:
random_pwd = random.randint(100000, 999999)
user = User(username, name, UserToken.add_salt(str(random_pwd)), email, avatar)
session.add(user)
session.commit()
session.refresh(user)
return user
async with async_session() as session:
async with session.begin():
query = await session.execute(select(User).where(User.id == user_info.id))
user = query.scalars().first()
if not user:
raise Exception("该用户不存在, 请检查")
# 开启not_null,这样只有非空字段才修改
DatabaseHelper.update_model(user, user_info, user_id, True)
await session.flush()
session.expunge(user)
return user
except Exception as e:
UserDao.log.error(f"修改用户信息失败: {str(e)}")
raise Exception(e)

@staticmethod
@RedisHelper.up_cache("user_list")
async def delete_user(id: int, user_id: int):
"""
变更用户的接口,主要用于用户管理页面(为管理员提供)
:param id: 被删除用户id
:param user_id: 操作人id
:return:
"""
try:
async with async_session() as session:
async with session.begin():
query = await session.execute(select(User).where(User.id == id))
user = query.scalars().first()
if not user:
raise Exception("该用户不存在, 请检查")
if user.role == Config.ADMIN:
raise Exception("你不能删除超级管理员")
user.update_user = user_id
user.deleted_at = datetime.now()
except Exception as e:
UserDao.log.error(f"修改用户信息失败: {str(e)}")
raise Exception(e)

@staticmethod
async def register_for_github(username, name, email, avatar):
try:
async with async_session() as session:
async with session.begin():
# 异步session只需要 session.begin,下面的commit可以去掉 语法也有一些区别
query = await session.execute(
select(User).where(or_(User.username == username, User.email == email)))
user = query.scalars().first()
if user:
# 如果存在,则给用户更新信息
user.last_login_at = datetime.now()
user.name = name
user.avatar = avatar
else:
random_pwd = random.randint(100000, 999999)
user = User(username, name, UserToken.add_salt(str(random_pwd)), email, avatar)
session.add(user)
await session.flush()
session.expunge(user)
return user
except Exception as e:
UserDao.log.error(f"Github用户登录失败: {str(e)}")
raise Exception("登录失败")
Expand Down Expand Up @@ -60,25 +115,38 @@ def register_user(username, name, password, email):
return None

@staticmethod
def login(username, password):
async def login(username, password):
"""
这里要改成异步了,原来的go写法要废弃
:param username:
:param password:
:return:
"""
try:
pwd = UserToken.add_salt(password)
with Session() as session:
# 查询用户名/密码匹配且没有被删除的用户
user = session.query(User).filter_by(username=username, password=pwd, deleted_at=None).first()
if user is None:
return None, "用户名或密码错误"
# 更新用户的最后登录时间
user.last_login_at = datetime.now()
session.commit()
session.refresh(user)
return user, None
async with async_session() as session:
async with session.begin():
# 查询用户名/密码匹配且没有被删除的用户
query = await session.execute(
select(User).where(User.username == username, User.password == pwd,
User.deleted_at == None))
user = query.scalars().first()
if user is None:
raise Exception("用户名或密码错误")
if not user.is_valid:
# 说明用户被禁用
raise Exception("您的账号已被封禁, 请联系管理员")
user.last_login_at = datetime.now()
await session.flush()
session.expunge(user)
return user
except Exception as e:
UserDao.log.error(f"用户{username}登录失败: {str(e)}")
return None, str(e)
raise e

@staticmethod
@RedisHelper.cache("user_list", 3 * 3600, True)
# TODO 先不改,里面有redis相关内容
def list_users():
try:
with Session() as session:
Expand Down
2 changes: 1 addition & 1 deletion app/crud/config/RedisConfigDao.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async def execute_command(command: str, **kwargs):
redis_config.password, redis_config.db)
else:
client = PityRedisManager.get_cluster_client(redis_config.id, redis_config.addr)
return RedisHelper.execute_command(client, command)
return await RedisHelper.execute_command(client, command)
except Exception as e:
raise Exception(f"执行redis命令出错: {e}")

Expand Down
30 changes: 30 additions & 0 deletions app/crud/operation/PityOperationDao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from sqlalchemy import func, select

from app.crud import Mapper
from app.models import async_session
from app.models.operation_log import PityOperationLog
from app.utils.decorator import dao
from app.utils.logger import Log


@dao(PityOperationLog, Log("PityOperationDao"))
class PityOperationDao(Mapper):

@classmethod
async def count_user_activities(cls, user_id, start_time: str, end_time: str):
"""
根据开始/结束时间 获取用户的活动日历(操作记录的数量)
:param user_id:
:param start_time:
:param end_time:
:return:
"""
async with async_session() as session:
async with session.begin():
format_date = func.date_format(PityOperationLog.operate_time, "%Y-%m-%d")
sql = select(format_date, func.count(PityOperationLog.id)).where(
PityOperationLog.operate_time.between(start_time, end_time),
PityOperationLog.user_id == user_id) \
.group_by(format_date).order_by(format_date)
data = await session.execute(sql)
return data.all()
Loading

0 comments on commit 3dcf1eb

Please sign in to comment.