Skip to content

Commit

Permalink
新增oss数据入库🎨 新增修改用户信息功能
Browse files Browse the repository at this point in the history
  • Loading branch information
wuranxu committed Jan 3, 2022
1 parent dab7684 commit 7e578e1
Show file tree
Hide file tree
Showing 17 changed files with 223 additions and 32 deletions.
3 changes: 2 additions & 1 deletion app/core/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,8 @@ async def run_multiple(executor: int, env: int, case_list: List[int], mode=0, pl
return
if executor != 0:
# 说明不是系统执行
name = await UserDao.query_user(executor)
user = await UserDao.query_user(executor)
name = user.name if user is not None else "未知"
else:
name = "CPU"
st = time.perf_counter()
Expand Down
1 change: 1 addition & 0 deletions app/crud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from app.models.test_case import TestCase
from app.models.test_plan import PityTestPlan
from app.models.user import User
from app.models.oss_file import PityOssFile
from config import Config


Expand Down
21 changes: 16 additions & 5 deletions app/crud/auth/UserDao.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime

from sqlalchemy import or_, select, func
from sqlalchemy import update

from app.middleware.Jwt import UserToken
from app.middleware.RedisManager import RedisHelper
Expand All @@ -15,6 +16,18 @@
class UserDao(object):
log = Log("UserDao")

@staticmethod
@RedisHelper.up_cache("user_list")
async def update_avatar(user_id: int, avatar_url: str):
try:
async with async_session() as session:
async with session.begin():
sql = update(User).where(User.id == user_id).values(avatar=avatar_url)
await session.execute(sql)
except Exception as e:
UserDao.log.error(f"修改用户头像失败: {str(e)}")
raise Exception(e)

@staticmethod
@RedisHelper.up_cache("user_list")
async def update_user(user_info: UserUpdateForm, user_id: int):
Expand Down Expand Up @@ -102,7 +115,8 @@ async def register_user(username: str, name: str, password: str, email: str):
try:
async with async_session() as session:
async with session.begin():
users = await session.execute(select(User).where(or_(User.username == username, User.email == email)))
users = await session.execute(
select(User).where(or_(User.username == username, User.email == email)))
counts = await session.execute(select(func.count(User.id)))
if users.scalars().first():
raise Exception("用户名或邮箱已存在")
Expand Down Expand Up @@ -159,14 +173,11 @@ def list_users():
raise Exception("获取用户列表失败")

@staticmethod
@RedisHelper.cache("query_user", 72 * 3600)
async def query_user(id: int):
async with async_session() as session:
query = await session.execute(select(User).where(User.id == id))
result = query.scalars().first()
if result is None:
return "unknown"
return result.name
return result

@staticmethod
@RedisHelper.cache("user_email", 3600)
Expand Down
3 changes: 3 additions & 0 deletions app/crud/config/DbConfigDao.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

from app.crud.config.EnvironmentDao import EnvironmentDao
from app.handler.fatcory import PityResponse
from app.middleware import RedisManager
from app.middleware.RedisManager import RedisHelper
from app.models import async_session, DatabaseHelper, db_helper
from app.models.database import PityDatabase
from app.models.schema.database import DatabaseForm
Expand Down Expand Up @@ -112,6 +114,7 @@ async def query_database_by_env_and_name(env: int, name: str):
raise Exception("获取数据库配置失败")

@staticmethod
@RedisHelper.cache("database:cache", expired_time=3600 * 3, args_key=False)
async def query_database_and_tables():
"""
方法会查询所有数据库表配置的信息
Expand Down
9 changes: 9 additions & 0 deletions app/crud/oss/PityOssDao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from app.crud import Mapper
from app.models.oss_file import PityOssFile
from app.utils.decorator import dao
from app.utils.logger import Log


@dao(PityOssFile, Log("PityOssDao"))
class PityOssDao(Mapper):
pass
13 changes: 13 additions & 0 deletions app/handler/encoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import json
from datetime import datetime
from typing import Any


class JsonEncoder(json.JSONEncoder):

def default(self, o: Any) -> Any:
if isinstance(o, set):
return list(o)
if isinstance(o, datetime):
return o.strftime("%Y-%m-%d %H:%M:%S")
return self.default(o)
24 changes: 24 additions & 0 deletions app/middleware/GrpcClient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
grpc客户端,根据方法/参数请求grpc接口
"""
import asyncio

from grpc_requests import AsyncClient


class GrpcClient(object):
"""
usage:
result = GrpcClient.invoke("localhost:50001", "hello", "greeter", {"name": "woody"})
print(result)
"""

@staticmethod
async def invoke(address: str, iface: str, method: str, request_data=None):
client = AsyncClient(address)
service = await client.service(iface)
return await getattr(service, method)(request_data)


if __name__ == "__main__":
asyncio.run(GrpcClient.invoke("localhost:50052", "Hello", "Edit", {"number": 10}))
16 changes: 11 additions & 5 deletions app/middleware/RedisManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from rediscluster import RedisCluster, ClusterConnectionPool

from app.excpetions.RedisException import RedisException
from app.handler.encoder import JsonEncoder
from app.handler.fatcory import PityResponse
from config import Config

Expand Down Expand Up @@ -122,13 +123,16 @@ def execute_command(client, command, *args, **kwargs):
return client.execute_command(command, *args, **kwargs)

@staticmethod
def get_key(key: str, *args):
return f"{RedisHelper.pity_prefix}:{key}{':'.join(str(a) for a in args)}"
def get_key(key: str, args_key: bool = True, *args):
if args_key:
return f"{RedisHelper.pity_prefix}:{key}{':'.join(str(a) for a in args)}"
return f"{RedisHelper.pity_prefix}:{key}"

@staticmethod
def cache(key: str, expired_time=3 * 60, model=False):
def cache(key: str, expired_time=3 * 60, model=False, args_key=True):
"""
自动缓存装饰器
:param args_key:
:param model:
:param key: 被缓存的key
:param expired_time: 默认key过期时间
Expand All @@ -140,19 +144,21 @@ def decorator(func):
if asyncio.iscoroutinefunction(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
redis_key = RedisHelper.get_key(key, *args)
redis_key = RedisHelper.get_key(key, args_key, *args)
data = RedisHelper.pity_redis_client.get(redis_key)
# 缓存已存在
if data is not None:
return json.loads(data)
# 获取最新数据
new_data = await func(*args, **kwargs)
if len(new_data) > 1:
new_data = list(new_data)
if model:
if isinstance(new_data, list):
new_data = PityResponse.model_to_list(new_data)
else:
new_data = PityResponse.model_to_dict(new_data)
info = json.dumps(new_data, ensure_ascii=False)
info = json.dumps(new_data, cls=JsonEncoder, ensure_ascii=False)
RedisHelper.pity_redis_client.set(redis_key, info, ex=expired_time)
return new_data

Expand Down
2 changes: 1 addition & 1 deletion app/middleware/oss/aliyun.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async def create_file(self, filepath: str, content: bytes):
self.bucket.put_object(filepath, content)

@aioify
async def update_file(self, filepath: str, content: bytes):
async def update_file(self, filepath: str, content: bytes, base_path: str = None):
self.bucket.put_object(filepath, content)

@aioify
Expand Down
9 changes: 6 additions & 3 deletions app/middleware/oss/files.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import random
import time
from abc import ABC, abstractmethod
from typing import ByteString


class OssFile(ABC):
_base_path = "pity"

@abstractmethod
async def create_file(self, filepath: str, content: bytes):
async def create_file(self, filepath: str, content: bytes, base_path: str = None) -> (str, int, str):
pass

@abstractmethod
async def update_file(self, filepath: str, content: bytes):
async def update_file(self, filepath: str, content: bytes, base_path: str = None):
pass

@abstractmethod
Expand All @@ -30,6 +30,9 @@ async def download_file(self, filepath):
async def get_file_object(self, filepath):
pass

async def get_real_path(self, filepath, base_path=None):
return f"{self._base_path if base_path is None else base_path}/{filepath}"

@staticmethod
def get_random_filename(filename):
random_str = list("pity")
Expand Down
26 changes: 21 additions & 5 deletions app/middleware/oss/gitee.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

class GiteeOss(OssFile):
_base_url = "https://gitee.com/api/v5/repos/{}/{}/contents/{}/{}"
_url = "https://gitee.com/{}/{}/raw/master/{}"
_download_url = "https://gitee.com/api/v5/repos/{}/{}/git/blobs/{}"
_base_path = "pity"

Expand All @@ -19,15 +20,20 @@ def __init__(self, user, repo, token):
self.token = token

@aioify
async def create_file(self, filepath: str, content: bytes):
gitee_url = self._base_url.format(self.user, self.repo, self._base_path, filepath)
async def create_file(self, filepath: str, content: bytes, base_path=None) -> (str, int, str):
base_path = base_path if base_path is not None else self._base_path
gitee_url = self._base_url.format(self.user, self.repo, base_path, filepath)
data = base64.b64encode(content).decode()
file_size = len(content)
json_data = {"access_token": self.token, "message": "pity create file", "content": data}
r = requests.post(url=gitee_url, json=json_data)
if r.status_code == 400:
raise Exception("文件重复,请先删除旧文件")
return await self.update_file(filepath, content, base_path)
if r.status_code != 201:
raise Exception("上传文件到gitee失败")
sha = r.json().get("content", {}).get("sha")
view_url = self._url.format(self.user, self.repo, await self.get_real_path(filepath, base_path))
return view_url, file_size, sha

@aioify
async def query_file_by_path(self, filepath: str, ans: List[dict]):
Expand All @@ -47,8 +53,18 @@ async def query_file_by_path(self, filepath: str, ans: List[dict]):
# return requests.get(url, stream=True).headers['Content-Length']

@aioify
async def update_file(self, filepath: str, content: bytes):
pass
async def update_file(self, filepath: str, content: bytes, base_path: str = None):
base_path = base_path if base_path is not None else self._base_path
gitee_url = self._base_url.format(self.user, self.repo, base_path, filepath)
r = requests.get(gitee_url, params=dict(access_token=self.token))
data = r.json()
sha = data.get("sha")
b64 = base64.b64encode(content).decode()
updated = requests.put(gitee_url, json=dict(access_token=self.token, sha=sha, content=b64, message="updated"))
sha = updated.json().get("content", {}).get("sha")
view_url = self._url.format(self.user, self.repo, await self.get_real_path(filepath, base_path))
return view_url, len(b64), sha


@aioify
async def delete_file(self, filepath: str):
Expand Down
47 changes: 47 additions & 0 deletions app/models/oss_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
pity oss文件映射表
"""
from sqlalchemy import String, Column, UniqueConstraint

from app.models.basic import PityBase

units = (
"B", "KB", "MB", "GB", "TB"
)


class PityOssFile(PityBase):
# 因为没有目录的概念,都是目录+文件名
file_path = Column(String(64), nullable=False, index=True, comment="文件路径")
view_url = Column(String(256), nullable=False, comment="文件预览url")
file_size = Column(String(16), comment="文件大小")
sha = Column(String(128), comment="gitee独有,sha代表一个文件的标识,gitee必须存这个变量")
__tablename__ = "pity_oss_file"
__fields__ = (file_path, view_url, file_size)
__tag__ = "oss"
__alias__ = dict(file_path="文件路径", view_url="地址", file_size="文件大小")
__show__ = 1
__table_args__ = (
UniqueConstraint('file_path', 'deleted_at'),
)

def __init__(self, user, file_path, view_url, file_size, sha=None, id=None):
super().__init__(user, id)
self.file_path = file_path
self.sha = sha
self.view_url = view_url
self.file_size = file_size

@staticmethod
def get_size(file_size: int):
"""
计算文件大小
:param file_size:
:return:
"""
unit_index = 0
while file_size >= 1024:
# 说明可以写成kb
file_size //= 1024
unit_index += 1
return f"{file_size}{units[unit_index]}"
1 change: 1 addition & 0 deletions app/models/schema/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class UserUpdateForm(BaseModel):
id: int
name: str = None
email: str = None
phone: str = None
role: int = None
is_valid: bool = None

Expand Down
4 changes: 3 additions & 1 deletion app/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class User(Base):
password = Column(String(32), unique=False)
email = Column(String(64), unique=True, nullable=False)
role = Column(INT, default=0, comment="0: 普通用户 1: 组长 2: 超级管理员")
phone = Column(String(12), unique=True)
created_at = Column(DATETIME, nullable=False)
updated_at = Column(DATETIME, nullable=False)
deleted_at = Column(DATETIME)
Expand All @@ -23,13 +24,14 @@ class User(Base):
# 管理员可以禁用某个用户,当他离职后
is_valid = Column(Boolean, nullable=False, default=True, comment="是否合法")

def __init__(self, username, name, password, email, avatar=None, is_valid=True):
def __init__(self, username, name, password, email, avatar=None, phone=None, is_valid=True):
self.username = username
self.password = password
self.email = email
self.name = name
self.created_at = datetime.now()
self.updated_at = datetime.now()
self.role = 0
self.phone = phone
self.avatar = avatar
self.is_valid = is_valid
Loading

0 comments on commit 7e578e1

Please sign in to comment.